@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,975 @@
1
+ // Re-export types (RuntimeStatus exported as enum below for backward compatibility)
2
+ export type {
3
+ RuntimeState,
4
+ StackFrame,
5
+ Variable,
6
+ ContextVariable,
7
+ ContextEntry,
8
+ AIOperation,
9
+ AIInteraction,
10
+ ExecutionEntry,
11
+ PendingAI,
12
+ PendingCompress,
13
+ PendingTS,
14
+ PendingToolCall,
15
+ TsModule,
16
+ VibeModule,
17
+ ExportedItem,
18
+ Instruction,
19
+ } from './types';
20
+
21
+ // Re-export module functions
22
+ export {
23
+ loadImports,
24
+ getImportedValue,
25
+ isImportedTsFunction,
26
+ isImportedVibeFunction,
27
+ getImportedVibeFunction,
28
+ getImportedTsFunction,
29
+ } from './modules';
30
+
31
+ // Re-export state functions
32
+ export {
33
+ createInitialState,
34
+ createFrame,
35
+ resumeWithAIResponse,
36
+ resumeWithUserInput,
37
+ resumeWithTsResult,
38
+ resumeWithImportedTsResult,
39
+ resumeWithToolResult,
40
+ resumeWithCompressResult,
41
+ pauseExecution,
42
+ resumeExecution,
43
+ currentFrame,
44
+ getVariable,
45
+ } from './state';
46
+
47
+ // Re-export TypeScript evaluation functions
48
+ export { evalTsBlock, validateReturnType, clearFunctionCache, getFunctionCacheSize, TsBlockError } from './ts-eval';
49
+
50
+ // Re-export step functions
51
+ export {
52
+ step,
53
+ stepN,
54
+ runUntilPause,
55
+ getNextInstruction,
56
+ stepUntilCondition,
57
+ stepUntilStatement,
58
+ stepUntilOp,
59
+ } from './step';
60
+
61
+ // Re-export context functions
62
+ export {
63
+ buildLocalContext,
64
+ buildGlobalContext,
65
+ formatContextForAI,
66
+ formatEntriesForSummarization,
67
+ type FormattedContext,
68
+ } from './context';
69
+
70
+ // Re-export serialization
71
+ export {
72
+ serializeState,
73
+ deserializeState,
74
+ cloneState,
75
+ deepCloneState,
76
+ getStateSummary,
77
+ } from './serialize';
78
+
79
+ // Re-export AI provider implementations
80
+ export { createRealAIProvider, createMockAIProvider } from './ai-provider';
81
+
82
+ // Re-export AI module
83
+ export * from './ai';
84
+
85
+ // Re-export AI interaction logging utilities
86
+ export { formatAIInteractions, dumpAIInteractions, saveAIInteractions } from './ai-logger';
87
+
88
+ // Re-export verbose logging
89
+ export { VerboseLogger, type VerboseLoggerOptions } from './verbose-logger';
90
+ export type { LogEvent, AILogMessage, TokenUsage } from './types';
91
+
92
+ // Legacy imports for backward compatibility
93
+ import * as AST from '../ast';
94
+ import { dirname } from 'path';
95
+ import type { SourceLocation } from '../errors';
96
+ import type { RuntimeState, AIInteraction } from './types';
97
+ import { resolveValue } from './types';
98
+ import { createInitialState, resumeWithAIResponse, resumeWithUserInput, resumeWithTsResult, resumeWithImportedTsResult, resumeWithToolResult, resumeWithCompressResult, resumeWithAsyncResults } from './state';
99
+ import type { VibeValue, PendingAsyncStart } from './types';
100
+ import { createVibeValue, isVibeValue } from './types';
101
+ import { awaitOperations, completeAsyncOperation, failAsyncOperation, createAsyncVibeError, startAsyncOperation } from './async';
102
+ import { step, runUntilPause } from './step';
103
+ import { evalTsBlock, TsBlockError } from './ts-eval';
104
+ import { loadImports, getImportedTsFunction, getImportedVibeFunction } from './modules';
105
+ import { createFunctionFrame } from './exec/functions';
106
+ import { buildLocalContext, buildGlobalContext, formatContextForAI, formatEntriesForSummarization } from './context';
107
+ import { saveAIInteractions } from './ai-logger';
108
+ import { deepCloneState } from './serialize';
109
+
110
+ // Token usage from AI providers
111
+ import type { TokenUsage } from './ai/types';
112
+ import type { ToolRoundResult } from './ai/tool-loop';
113
+ import type { AILogMessage, ContextEntry, PromptToolCall, LogEvent } from './types';
114
+ import { VerboseLogger } from './verbose-logger';
115
+
116
+ // AI execution result with optional usage and tool rounds
117
+ export interface AIExecutionResult {
118
+ value: unknown;
119
+ usage?: TokenUsage;
120
+ toolRounds?: ToolRoundResult[]; // Tool calling rounds that occurred during execution
121
+ // Context for logging (single source of truth)
122
+ messages?: AILogMessage[]; // Complete message sequence sent to model
123
+ executionContext?: ContextEntry[]; // Structured execution context
124
+ interactionToolCalls?: PromptToolCall[]; // Tool calls made during interaction
125
+ }
126
+
127
+ // AI provider interface (for external callers)
128
+ export interface AIProvider {
129
+ execute(prompt: string): Promise<AIExecutionResult>;
130
+ generateCode(prompt: string): Promise<AIExecutionResult>;
131
+ askUser(prompt: string): Promise<string>;
132
+ }
133
+
134
+ // Runtime options
135
+ export interface RuntimeOptions {
136
+ basePath?: string; // Base path for resolving imports (defaults to cwd)
137
+ logAiInteractions?: boolean; // Capture detailed AI interaction logs for debugging (DEPRECATED: use verbose)
138
+ rootDir?: string; // Root directory for file operation sandboxing (defaults to cwd)
139
+ verbose?: boolean; // Enable verbose logging (JSONL events + context files)
140
+ logDir?: string; // Directory for verbose logs (default: .vibe-logs)
141
+ printToConsole?: boolean; // Print verbose logs to console (default: true)
142
+ writeToFile?: boolean; // Write verbose logs to file (default: true)
143
+ maxParallel?: number; // Max concurrent async operations (default: 4)
144
+ }
145
+
146
+ // Legacy Runtime class - convenience wrapper around functional API
147
+ export class Runtime {
148
+ private state: RuntimeState;
149
+ private aiProvider: AIProvider;
150
+ private basePath: string;
151
+ private importsLoaded: boolean = false;
152
+ private logAiInteractions: boolean;
153
+ private verboseLogger: VerboseLogger | null = null;
154
+
155
+ constructor(program: AST.Program, aiProvider: AIProvider, options?: RuntimeOptions) {
156
+ this.logAiInteractions = options?.logAiInteractions ?? false;
157
+ this.state = createInitialState(program, {
158
+ logAiInteractions: this.logAiInteractions,
159
+ rootDir: options?.rootDir,
160
+ maxParallel: options?.maxParallel,
161
+ });
162
+ this.aiProvider = aiProvider;
163
+ this.basePath = options?.basePath ?? process.cwd() + '/main.vibe';
164
+
165
+ // Initialize verbose logger if enabled
166
+ if (options?.verbose) {
167
+ this.verboseLogger = new VerboseLogger({
168
+ logDir: options.logDir,
169
+ printToConsole: options.printToConsole,
170
+ writeToFile: options.writeToFile,
171
+ });
172
+ }
173
+ }
174
+
175
+ getState(): RuntimeState {
176
+ return { ...this.state };
177
+ }
178
+
179
+ getValue(name: string): unknown {
180
+ const frame = this.state.callStack[this.state.callStack.length - 1];
181
+ if (!frame) return undefined;
182
+
183
+ const variable = frame.locals[name];
184
+ // Resolve VibeValue to its value for easier testing
185
+ return resolveValue(variable?.value);
186
+ }
187
+
188
+ // Get raw value including VibeValue wrapper if present
189
+ getRawValue(name: string): unknown {
190
+ const frame = this.state.callStack[this.state.callStack.length - 1];
191
+ if (!frame) return undefined;
192
+
193
+ const variable = frame.locals[name];
194
+ return variable?.value;
195
+ }
196
+
197
+ // Get all AI interactions (for debugging)
198
+ getAIInteractions(): AIInteraction[] {
199
+ return [...this.state.aiInteractions];
200
+ }
201
+
202
+ // Run the program to completion, handling AI calls and TS evaluation
203
+ async run(): Promise<unknown> {
204
+ // Log run start
205
+ this.verboseLogger?.start(this.basePath);
206
+
207
+ // Load imports if not already loaded
208
+ if (!this.importsLoaded) {
209
+ this.state = await loadImports(this.state, this.basePath);
210
+ this.importsLoaded = true;
211
+ }
212
+
213
+ // Run until pause or complete
214
+ this.state = runUntilPause(this.state);
215
+
216
+ // Start any scheduled async operations (non-blocking)
217
+ await this.startScheduledAsyncOps();
218
+
219
+ // Handle AI calls, TS evaluation, tool calls, compress, and async in a loop
220
+ while (
221
+ this.state.status === 'awaiting_ai' ||
222
+ this.state.status === 'awaiting_user' ||
223
+ this.state.status === 'awaiting_ts' ||
224
+ this.state.status === 'awaiting_tool' ||
225
+ this.state.status === 'awaiting_compress' ||
226
+ this.state.status === 'awaiting_async'
227
+ ) {
228
+ // Handle awaiting_async - wait for pending async operations
229
+ if (this.state.status === 'awaiting_async') {
230
+ // IMPORTANT: Start scheduled async ops BEFORE awaiting them
231
+ // Operations are scheduled (in pendingAsyncStarts) but their promises
232
+ // are only created when started
233
+ await this.startScheduledAsyncOps();
234
+
235
+ const results = await awaitOperations(
236
+ this.state.awaitingAsyncIds,
237
+ this.state.asyncOperations
238
+ );
239
+ this.state = resumeWithAsyncResults(this.state, results);
240
+ this.state = runUntilPause(this.state);
241
+ await this.startScheduledAsyncOps();
242
+ continue;
243
+ }
244
+ if (this.state.status === 'awaiting_ts') {
245
+ if (this.state.pendingTS) {
246
+ // Handle inline ts block evaluation
247
+ const { params, body, paramValues, location } = this.state.pendingTS;
248
+
249
+ // Log TS block start
250
+ const tsId = this.verboseLogger?.tsBlockStart(
251
+ params,
252
+ paramValues,
253
+ body,
254
+ { file: location.file ?? this.basePath, line: location.line }
255
+ );
256
+ const tsStartTime = Date.now();
257
+
258
+ try {
259
+ const result = await evalTsBlock(params, body, paramValues, location);
260
+ this.state = resumeWithTsResult(this.state, result);
261
+
262
+ // Log TS block complete
263
+ if (tsId) {
264
+ this.verboseLogger?.tsBlockComplete(tsId, Date.now() - tsStartTime);
265
+ }
266
+ } catch (error) {
267
+ // Log TS block error
268
+ if (tsId) {
269
+ const errMsg = error instanceof Error ? error.message : String(error);
270
+ this.verboseLogger?.tsBlockComplete(tsId, Date.now() - tsStartTime, errMsg);
271
+ }
272
+ throw error;
273
+ }
274
+ } else if (this.state.pendingImportedTsCall) {
275
+ // Handle imported TS function call
276
+ const { funcName, args, location } = this.state.pendingImportedTsCall;
277
+ const fn = getImportedTsFunction(this.state, funcName);
278
+ if (!fn) {
279
+ throw new Error(`Import error: Function '${funcName}' not found`);
280
+ }
281
+
282
+ // Log TS function start
283
+ const tsfId = this.verboseLogger?.tsFunctionStart(
284
+ funcName,
285
+ args,
286
+ { file: location.file ?? this.basePath, line: location.line }
287
+ );
288
+ const tsfStartTime = Date.now();
289
+
290
+ try {
291
+ const result = await fn(...args);
292
+ this.state = resumeWithImportedTsResult(this.state, result);
293
+
294
+ // Log TS function complete
295
+ if (tsfId) {
296
+ this.verboseLogger?.tsFunctionComplete(tsfId, Date.now() - tsfStartTime);
297
+ }
298
+ } catch (error) {
299
+ // Log TS function error
300
+ if (tsfId) {
301
+ const errMsg = error instanceof Error ? error.message : String(error);
302
+ this.verboseLogger?.tsFunctionComplete(tsfId, Date.now() - tsfStartTime, errMsg);
303
+ }
304
+ // Wrap imported TS function error with Vibe location info
305
+ const originalError = error instanceof Error ? error : new Error(String(error));
306
+ throw new TsBlockError(
307
+ `Error in imported function '${funcName}': ${originalError.message}`,
308
+ [], // no params for imported functions
309
+ `/* imported function: ${funcName} */`,
310
+ originalError,
311
+ location
312
+ );
313
+ }
314
+ } else {
315
+ throw new Error('State awaiting TS but no pending TS request');
316
+ }
317
+ } else if (this.state.status === 'awaiting_ai') {
318
+ // Handle AI calls
319
+ if (!this.state.pendingAI) {
320
+ throw new Error('State awaiting AI but no pending AI request');
321
+ }
322
+
323
+ const startTime = Date.now();
324
+ const pendingAI = this.state.pendingAI;
325
+
326
+ // Get target type from next instruction
327
+ let targetType: string | null = null;
328
+ const nextInstruction = this.state.instructionStack[0];
329
+ if (nextInstruction?.op === 'declare_var' && nextInstruction.type) {
330
+ targetType = nextInstruction.type;
331
+ }
332
+
333
+ // Get model details from state for logging
334
+ let modelDetails: AIInteraction['modelDetails'];
335
+ const modelVar = this.state.callStack[0]?.locals?.[pendingAI.model];
336
+ if (modelVar?.value && typeof modelVar.value === 'object') {
337
+ const mv = modelVar.value as Record<string, unknown>;
338
+ modelDetails = {
339
+ name: String(mv.name ?? ''),
340
+ provider: String(mv.provider ?? ''),
341
+ url: mv.url ? String(mv.url) : undefined,
342
+ thinkingLevel: mv.thinkingLevel ? String(mv.thinkingLevel) : undefined,
343
+ };
344
+ }
345
+
346
+ // Log AI start (verbose logger)
347
+ const aiId = this.verboseLogger?.aiStart(
348
+ pendingAI.type,
349
+ pendingAI.model,
350
+ pendingAI.prompt,
351
+ {
352
+ model: pendingAI.model,
353
+ modelDetails,
354
+ type: pendingAI.type,
355
+ targetType,
356
+ messages: [], // Will be updated after execution
357
+ }
358
+ );
359
+
360
+ // vibe is the only AI expression type now
361
+ let result: AIExecutionResult;
362
+ let aiError: string | undefined;
363
+ try {
364
+ result = await this.aiProvider.execute(pendingAI.prompt);
365
+ } catch (error) {
366
+ aiError = error instanceof Error ? error.message : String(error);
367
+ // Log AI error
368
+ if (aiId) {
369
+ this.verboseLogger?.aiComplete(aiId, Date.now() - startTime, undefined, 0, aiError);
370
+ }
371
+ throw error;
372
+ }
373
+
374
+ // Log AI complete (verbose logger)
375
+ if (aiId) {
376
+ const toolCallCount = result.toolRounds?.reduce((sum, r) => sum + r.toolCalls.length, 0) ?? 0;
377
+ this.verboseLogger?.aiComplete(aiId, Date.now() - startTime, result.usage, toolCallCount);
378
+ }
379
+
380
+ // Create interaction record if logging (legacy)
381
+ // Uses context from result (single source of truth from ai-provider)
382
+ let interaction: AIInteraction | undefined;
383
+ if (this.logAiInteractions) {
384
+ interaction = {
385
+ type: pendingAI.type,
386
+ prompt: pendingAI.prompt,
387
+ response: result.value,
388
+ timestamp: startTime,
389
+ model: pendingAI.model,
390
+ modelDetails,
391
+ targetType,
392
+ usage: result.usage,
393
+ durationMs: Date.now() - startTime,
394
+ // Context from ai-provider (single source of truth)
395
+ messages: result.messages ?? [],
396
+ executionContext: result.executionContext ?? [],
397
+ interactionToolCalls: result.interactionToolCalls,
398
+ };
399
+ }
400
+
401
+ this.state = resumeWithAIResponse(this.state, result.value, interaction, result.toolRounds);
402
+ } else if (this.state.status === 'awaiting_tool') {
403
+ // Handle tool calls
404
+ if (!this.state.pendingToolCall) {
405
+ throw new Error('State awaiting tool but no pending tool call');
406
+ }
407
+
408
+ const { toolName, args, executor } = this.state.pendingToolCall;
409
+
410
+ // Log tool start (we don't have a parent AI ID here, use empty string)
411
+ this.verboseLogger?.toolStart('', toolName ?? 'unknown', args as Record<string, unknown>);
412
+ const toolStartTime = Date.now();
413
+
414
+ try {
415
+ // Execute the tool with context - let errors propagate
416
+ const context = { rootDir: this.state.rootDir };
417
+ const result = await executor(args, context);
418
+ this.state = resumeWithToolResult(this.state, result);
419
+
420
+ // Log tool complete
421
+ this.verboseLogger?.toolComplete('', toolName ?? 'unknown', Date.now() - toolStartTime, true);
422
+ } catch (error) {
423
+ const errMsg = error instanceof Error ? error.message : String(error);
424
+ this.verboseLogger?.toolComplete('', toolName ?? 'unknown', Date.now() - toolStartTime, false, errMsg);
425
+ throw error;
426
+ }
427
+ } else if (this.state.status === 'awaiting_compress') {
428
+ // Handle compress AI summarization
429
+ if (!this.state.pendingCompress) {
430
+ throw new Error('State awaiting compress but no pending compress request');
431
+ }
432
+
433
+ const { prompt, scopeType } = this.state.pendingCompress;
434
+
435
+ // Build local context at end of loop (before we discard entries)
436
+ const localContext = buildLocalContext(this.state);
437
+ const contextFormatted = formatContextForAI(localContext, { includeInstructions: false });
438
+
439
+ // Build summarization prompt
440
+ const defaultPrompt = `Provide a concise summary of the most recent ${scopeType} loop execution. Focus on key results, state changes, and outcomes.`;
441
+ const summaryPrompt = `${prompt ?? defaultPrompt}\n\n${contextFormatted.text}`;
442
+
443
+ // Execute AI call for summarization (uses pendingCompress.model)
444
+ const result = await this.aiProvider.execute(summaryPrompt);
445
+ const summary = typeof result.value === 'string' ? result.value : String(result.value);
446
+
447
+ this.state = resumeWithCompressResult(this.state, summary);
448
+ } else {
449
+ // Handle user input
450
+ if (!this.state.pendingAI) {
451
+ throw new Error('State awaiting user but no pending AI request');
452
+ }
453
+ const response = await this.aiProvider.askUser(this.state.pendingAI.prompt);
454
+ this.state = resumeWithUserInput(this.state, response);
455
+ }
456
+
457
+ // Continue running
458
+ this.state = runUntilPause(this.state);
459
+
460
+ // Start any newly scheduled async operations
461
+ await this.startScheduledAsyncOps();
462
+ }
463
+
464
+ if (this.state.status === 'error') {
465
+ // Log run error
466
+ this.verboseLogger?.complete('error', this.state.error ?? 'Unknown error');
467
+
468
+ // Save logs even on error if logging enabled
469
+ this.saveLogsIfEnabled();
470
+ // Throw the original error object to preserve location info
471
+ throw this.state.errorObject ?? new Error(this.state.error ?? 'Unknown runtime error');
472
+ }
473
+
474
+ // At program completion, await any remaining pending async operations
475
+ // This implements "await at block boundary" for the main program
476
+ if (this.state.pendingAsyncIds.size > 0) {
477
+ const pendingIds = Array.from(this.state.pendingAsyncIds);
478
+ const results = await awaitOperations(pendingIds, this.state.asyncOperations);
479
+ this.state = resumeWithAsyncResults(
480
+ { ...this.state, status: 'awaiting_async', awaitingAsyncIds: pendingIds },
481
+ results
482
+ );
483
+ // resumeWithAsyncResults sets status to 'running', so run until completion again
484
+ this.state = runUntilPause(this.state);
485
+ }
486
+
487
+ // Log run complete
488
+ this.verboseLogger?.complete('completed');
489
+
490
+ // Save logs on successful completion
491
+ this.saveLogsIfEnabled();
492
+
493
+ // Resolve VibeValue to its value for the final return
494
+ return resolveValue(this.state.lastResult);
495
+ }
496
+
497
+ // Save AI interaction logs if logging is enabled
498
+ private saveLogsIfEnabled(): void {
499
+ if (this.logAiInteractions && this.state.aiInteractions.length > 0) {
500
+ const projectRoot = dirname(this.basePath);
501
+ saveAIInteractions(this.state, projectRoot);
502
+ }
503
+ }
504
+
505
+ // Start scheduled async operations as background Promises
506
+ private async startScheduledAsyncOps(): Promise<void> {
507
+ const pending = this.state.pendingAsyncStarts;
508
+ if (pending.length === 0) return;
509
+
510
+ // Clear pending starts
511
+ this.state = { ...this.state, pendingAsyncStarts: [] };
512
+
513
+ // Start each operation as a Promise (non-blocking)
514
+ for (const start of pending) {
515
+ const operation = this.state.asyncOperations.get(start.operationId);
516
+ if (!operation) continue;
517
+
518
+ if (start.aiDetails) {
519
+ // Start AI operation
520
+ const { prompt } = start.aiDetails;
521
+
522
+ // Create the Promise for this AI call
523
+ const promise = this.executeAIAsync(start.operationId, prompt);
524
+
525
+ // Store the promise in the operation (so awaiting_async can wait on it)
526
+ startAsyncOperation(operation, promise);
527
+ } else if (start.tsDetails) {
528
+ // Start TS block operation
529
+ const { params, body, paramValues, location } = start.tsDetails;
530
+
531
+ // Create the Promise for this TS eval
532
+ const promise = this.executeTsAsync(start.operationId, params, body, paramValues, location);
533
+
534
+ startAsyncOperation(operation, promise);
535
+ } else if (start.tsFuncDetails) {
536
+ // Start imported TS function operation
537
+ const { funcName, args, location } = start.tsFuncDetails;
538
+
539
+ // Create the Promise for this TS function call
540
+ const promise = this.executeTsFuncAsync(start.operationId, funcName, args, location);
541
+
542
+ startAsyncOperation(operation, promise);
543
+ } else if (start.vibeFuncDetails) {
544
+ // Start Vibe function operation
545
+ const { funcName, args, modulePath } = start.vibeFuncDetails;
546
+
547
+ // Create the Promise for this Vibe function call
548
+ const promise = this.executeVibeFuncAsync(start.operationId, funcName, args, modulePath);
549
+
550
+ startAsyncOperation(operation, promise);
551
+ }
552
+ }
553
+ }
554
+
555
+ // Execute an AI call asynchronously (returns Promise that resolves to VibeValue)
556
+ private async executeAIAsync(
557
+ operationId: string,
558
+ prompt: string
559
+ ): Promise<VibeValue> {
560
+ try {
561
+ const result = await this.aiProvider.execute(prompt);
562
+
563
+ // Create VibeValue with the result
564
+ const vibeValue = createVibeValue(result.value, {
565
+ source: 'ai',
566
+ toolCalls: result.toolRounds?.flatMap((round) =>
567
+ round.toolCalls.map((call, i) => {
568
+ const error = round.results[i]?.error;
569
+ return {
570
+ toolName: call.toolName,
571
+ args: call.args,
572
+ result: error ? null : String(round.results[i]?.result ?? ''),
573
+ err: !!error,
574
+ errDetails: error ? { message: error } : null,
575
+ duration: round.results[i]?.duration ?? 0,
576
+ };
577
+ })
578
+ ) ?? [],
579
+ });
580
+
581
+ // Mark operation as complete
582
+ completeAsyncOperation(this.state, operationId, vibeValue);
583
+
584
+ return vibeValue;
585
+ } catch (error) {
586
+ const vibeError = createAsyncVibeError(error);
587
+ failAsyncOperation(this.state, operationId, vibeError);
588
+ return createVibeValue(null, { source: 'ai', err: true, errDetails: vibeError });
589
+ }
590
+ }
591
+
592
+ // Execute a TS block asynchronously (returns Promise that resolves to VibeValue)
593
+ private async executeTsAsync(
594
+ operationId: string,
595
+ params: string[],
596
+ body: string,
597
+ paramValues: unknown[],
598
+ location: SourceLocation
599
+ ): Promise<VibeValue> {
600
+ try {
601
+ const result = await evalTsBlock(params, body, paramValues, location);
602
+
603
+ // Create VibeValue with the result
604
+ const vibeValue = createVibeValue(result, { source: 'ts' });
605
+
606
+ // Mark operation as complete
607
+ completeAsyncOperation(this.state, operationId, vibeValue);
608
+
609
+ return vibeValue;
610
+ } catch (error) {
611
+ const vibeError = createAsyncVibeError(error, location);
612
+ failAsyncOperation(this.state, operationId, vibeError);
613
+ return createVibeValue(null, { source: 'ts', err: true, errDetails: vibeError });
614
+ }
615
+ }
616
+
617
+ // Execute an imported TS function asynchronously (returns Promise that resolves to VibeValue)
618
+ private async executeTsFuncAsync(
619
+ operationId: string,
620
+ funcName: string,
621
+ args: unknown[],
622
+ location: SourceLocation
623
+ ): Promise<VibeValue> {
624
+ try {
625
+ const fn = getImportedTsFunction(this.state, funcName);
626
+ if (!fn) {
627
+ throw new Error(`Import error: Function '${funcName}' not found`);
628
+ }
629
+
630
+ const result = await fn(...args);
631
+
632
+ // Create VibeValue with the result
633
+ const vibeValue = createVibeValue(result, { source: 'ts' });
634
+
635
+ // Mark operation as complete
636
+ completeAsyncOperation(this.state, operationId, vibeValue);
637
+
638
+ return vibeValue;
639
+ } catch (error) {
640
+ const vibeError = createAsyncVibeError(error, location);
641
+ failAsyncOperation(this.state, operationId, vibeError);
642
+ return createVibeValue(null, { source: 'ts', err: true, errDetails: vibeError });
643
+ }
644
+ }
645
+
646
+ /**
647
+ * Core helper to run a Vibe function in isolated state.
648
+ * Used by both top-level async calls and nested calls within isolated execution.
649
+ * The function runs to completion in isolation - only the return value persists.
650
+ */
651
+ private async runVibeFuncIsolated(
652
+ sourceState: RuntimeState,
653
+ funcName: string,
654
+ args: unknown[],
655
+ modulePath?: string
656
+ ): Promise<unknown> {
657
+ // Get the function declaration
658
+ let func: AST.FunctionDeclaration | undefined;
659
+ if (modulePath) {
660
+ func = getImportedVibeFunction(sourceState, funcName);
661
+ } else {
662
+ func = sourceState.functions[funcName];
663
+ }
664
+
665
+ if (!func) {
666
+ throw new Error(`ReferenceError: '${funcName}' is not defined`);
667
+ }
668
+
669
+ // Deep clone the state for isolated execution
670
+ let isolatedState = deepCloneState(sourceState);
671
+
672
+ // Reset async-related state for the isolated execution
673
+ isolatedState.asyncOperations = new Map();
674
+ isolatedState.pendingAsyncIds = new Set();
675
+ isolatedState.asyncVarToOpId = new Map();
676
+ isolatedState.pendingAsyncStarts = [];
677
+ isolatedState.awaitingAsyncIds = [];
678
+
679
+ // Create the function frame
680
+ const newFrame = createFunctionFrame(funcName, func.params, args, modulePath);
681
+
682
+ // Set up the function call
683
+ const bodyInstructions = func.body.body.map((s) => ({
684
+ op: 'exec_statement' as const,
685
+ stmt: s,
686
+ location: s.location,
687
+ }));
688
+
689
+ isolatedState = {
690
+ ...isolatedState,
691
+ status: 'running',
692
+ callStack: [...isolatedState.callStack, newFrame],
693
+ instructionStack: [
694
+ ...bodyInstructions,
695
+ { op: 'pop_frame' as const, location: func.body.location },
696
+ ],
697
+ lastResult: null,
698
+ isInAsyncIsolation: true,
699
+ };
700
+
701
+ // Run the isolated state to completion
702
+ return this.runIsolatedState(isolatedState);
703
+ }
704
+
705
+ /**
706
+ * Execute a Vibe function asynchronously from main state.
707
+ */
708
+ private async executeVibeFuncAsync(
709
+ operationId: string,
710
+ funcName: string,
711
+ args: unknown[],
712
+ modulePath?: string
713
+ ): Promise<VibeValue> {
714
+ try {
715
+ const result = await this.runVibeFuncIsolated(this.state, funcName, args, modulePath);
716
+
717
+ // If result is already a VibeValue with error (from function returning error value), preserve it
718
+ if (isVibeValue(result) && result.err) {
719
+ completeAsyncOperation(this.state, operationId, result);
720
+ return result;
721
+ }
722
+
723
+ // Otherwise wrap in a new VibeValue
724
+ const vibeValue = createVibeValue(result, { source: 'vibe-function' });
725
+ completeAsyncOperation(this.state, operationId, vibeValue);
726
+ return vibeValue;
727
+ } catch (error) {
728
+ const vibeError = createAsyncVibeError(error);
729
+ failAsyncOperation(this.state, operationId, vibeError);
730
+ return createVibeValue(null, { source: 'vibe-function', err: true, errDetails: vibeError });
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Run an isolated state to completion, handling AI calls, TS blocks, etc.
736
+ * Returns the final lastResult value.
737
+ */
738
+ private async runIsolatedState(state: RuntimeState): Promise<unknown> {
739
+ // Run until pause or complete
740
+ state = runUntilPause(state);
741
+
742
+ // Start any scheduled async operations in the isolated state
743
+ state = await this.startIsolatedAsyncOps(state);
744
+
745
+ // Handle AI calls, TS evaluation, async awaits, etc. in a loop
746
+ while (
747
+ state.status === 'awaiting_ai' ||
748
+ state.status === 'awaiting_ts' ||
749
+ state.status === 'awaiting_tool' ||
750
+ state.status === 'awaiting_async'
751
+ ) {
752
+ if (state.status === 'awaiting_async') {
753
+ // Wait for pending async operations in isolated state
754
+ const results = await awaitOperations(
755
+ state.awaitingAsyncIds,
756
+ state.asyncOperations
757
+ );
758
+ state = resumeWithAsyncResults(state, results);
759
+ state = runUntilPause(state);
760
+ state = await this.startIsolatedAsyncOps(state);
761
+ continue;
762
+ }
763
+
764
+ if (state.status === 'awaiting_ts') {
765
+ if (state.pendingTS) {
766
+ const { params, body, paramValues, location } = state.pendingTS;
767
+ const result = await evalTsBlock(params, body, paramValues, location);
768
+ state = resumeWithTsResult(state, result);
769
+ } else if (state.pendingImportedTsCall) {
770
+ const { funcName, args } = state.pendingImportedTsCall;
771
+ const fn = getImportedTsFunction(state, funcName);
772
+ if (!fn) {
773
+ throw new Error(`Import error: Function '${funcName}' not found`);
774
+ }
775
+ const result = await fn(...args);
776
+ state = resumeWithImportedTsResult(state, result);
777
+ }
778
+ } else if (state.status === 'awaiting_ai') {
779
+ if (!state.pendingAI) {
780
+ throw new Error('State awaiting AI but no pending AI request');
781
+ }
782
+ const result = await this.aiProvider.execute(state.pendingAI.prompt);
783
+ state = resumeWithAIResponse(state, result.value);
784
+ } else if (state.status === 'awaiting_tool') {
785
+ if (!state.pendingToolCall) {
786
+ throw new Error('State awaiting tool but no pending tool call');
787
+ }
788
+ const { args, executor } = state.pendingToolCall;
789
+ const context = { rootDir: state.rootDir };
790
+ const result = await executor(args, context);
791
+ state = resumeWithToolResult(state, result);
792
+ }
793
+
794
+ // Continue running
795
+ state = runUntilPause(state);
796
+
797
+ // Start any newly scheduled async operations
798
+ state = await this.startIsolatedAsyncOps(state);
799
+ }
800
+
801
+ if (state.status === 'error') {
802
+ throw state.errorObject ?? new Error(state.error ?? 'Unknown runtime error');
803
+ }
804
+
805
+ // Await any remaining pending async operations at function exit
806
+ if (state.pendingAsyncIds.size > 0) {
807
+ const pendingIds = Array.from(state.pendingAsyncIds);
808
+ const results = await awaitOperations(pendingIds, state.asyncOperations);
809
+ state = resumeWithAsyncResults(
810
+ { ...state, status: 'awaiting_async', awaitingAsyncIds: pendingIds },
811
+ results
812
+ );
813
+ state = runUntilPause(state);
814
+ }
815
+
816
+ // If lastResult is a VibeValue with error, return it as-is to preserve error info
817
+ if (isVibeValue(state.lastResult) && state.lastResult.err) {
818
+ return state.lastResult;
819
+ }
820
+ return resolveValue(state.lastResult);
821
+ }
822
+
823
+ /**
824
+ * Start scheduled async operations in an isolated state.
825
+ * Similar to startScheduledAsyncOps but operates on passed state.
826
+ */
827
+ private async startIsolatedAsyncOps(state: RuntimeState): Promise<RuntimeState> {
828
+ const pending = state.pendingAsyncStarts;
829
+ if (pending.length === 0) return state;
830
+
831
+ // Clear pending starts
832
+ state = { ...state, pendingAsyncStarts: [] };
833
+
834
+ // Start each operation as a Promise
835
+ for (const start of pending) {
836
+ const operation = state.asyncOperations.get(start.operationId);
837
+ if (!operation) continue;
838
+
839
+ if (start.aiDetails) {
840
+ // Start AI operation
841
+ const { prompt } = start.aiDetails;
842
+
843
+ // Create the Promise for this AI call (simpler version for isolated state)
844
+ const promise = (async () => {
845
+ try {
846
+ const result = await this.aiProvider.execute(prompt);
847
+ const vibeValue = createVibeValue(result.value, { source: 'ai' });
848
+ completeAsyncOperation(state, start.operationId, vibeValue);
849
+ return vibeValue;
850
+ } catch (error) {
851
+ const vibeError = createAsyncVibeError(error);
852
+ failAsyncOperation(state, start.operationId, vibeError);
853
+ return createVibeValue(null, { source: 'ai', err: true, errDetails: vibeError });
854
+ }
855
+ })();
856
+
857
+ startAsyncOperation(operation, promise);
858
+ } else if (start.tsDetails) {
859
+ // Start TS block operation
860
+ const { params, body, paramValues, location } = start.tsDetails;
861
+
862
+ const promise = (async () => {
863
+ try {
864
+ const result = await evalTsBlock(params, body, paramValues, location);
865
+ const vibeValue = createVibeValue(result, { source: 'ts' });
866
+ completeAsyncOperation(state, start.operationId, vibeValue);
867
+ return vibeValue;
868
+ } catch (error) {
869
+ const vibeError = createAsyncVibeError(error, location);
870
+ failAsyncOperation(state, start.operationId, vibeError);
871
+ return createVibeValue(null, { source: 'ts', err: true, errDetails: vibeError });
872
+ }
873
+ })();
874
+
875
+ startAsyncOperation(operation, promise);
876
+ } else if (start.tsFuncDetails) {
877
+ // Start imported TS function operation
878
+ const { funcName, args, location } = start.tsFuncDetails;
879
+
880
+ const promise = (async () => {
881
+ try {
882
+ const fn = getImportedTsFunction(state, funcName);
883
+ if (!fn) {
884
+ throw new Error(`Import error: Function '${funcName}' not found`);
885
+ }
886
+ const result = await fn(...args);
887
+ const vibeValue = createVibeValue(result, { source: 'ts' });
888
+ completeAsyncOperation(state, start.operationId, vibeValue);
889
+ return vibeValue;
890
+ } catch (error) {
891
+ const vibeError = createAsyncVibeError(error, location);
892
+ failAsyncOperation(state, start.operationId, vibeError);
893
+ return createVibeValue(null, { source: 'ts', err: true, errDetails: vibeError });
894
+ }
895
+ })();
896
+
897
+ startAsyncOperation(operation, promise);
898
+ } else if (start.vibeFuncDetails) {
899
+ // Nested Vibe function call - reuse shared helper
900
+ const { funcName, args, modulePath } = start.vibeFuncDetails;
901
+
902
+ const promise = (async () => {
903
+ try {
904
+ const result = await this.runVibeFuncIsolated(state, funcName, args, modulePath);
905
+ const vibeValue = createVibeValue(result, { source: 'vibe-function' });
906
+ completeAsyncOperation(state, start.operationId, vibeValue);
907
+ return vibeValue;
908
+ } catch (error) {
909
+ const vibeError = createAsyncVibeError(error);
910
+ failAsyncOperation(state, start.operationId, vibeError);
911
+ return createVibeValue(null, { source: 'vibe-function', err: true, errDetails: vibeError });
912
+ }
913
+ })();
914
+
915
+ startAsyncOperation(operation, promise);
916
+ }
917
+ }
918
+
919
+ return state;
920
+ }
921
+
922
+ // Step through one instruction at a time
923
+ step(): RuntimeState {
924
+ this.state = step(this.state);
925
+ return this.state;
926
+ }
927
+
928
+ // Run until pause point (AI call, user input, or completion)
929
+ runUntilPause(): RuntimeState {
930
+ this.state = runUntilPause(this.state);
931
+ return this.state;
932
+ }
933
+
934
+ // Resume after providing AI response
935
+ resumeWithAIResponse(response: string): RuntimeState {
936
+ this.state = resumeWithAIResponse(this.state, response);
937
+ return this.state;
938
+ }
939
+
940
+ // Resume after providing user input
941
+ resumeWithUserInput(input: string): RuntimeState {
942
+ this.state = resumeWithUserInput(this.state, input);
943
+ return this.state;
944
+ }
945
+
946
+ // Get all logged events (for programmatic access / testing)
947
+ getLogEvents(): LogEvent[] {
948
+ return this.verboseLogger?.getEvents() ?? [];
949
+ }
950
+
951
+ // Get the main log file path (for debugging)
952
+ getMainLogPath(): string | null {
953
+ return this.verboseLogger?.getMainLogPath() ?? null;
954
+ }
955
+
956
+ // Get the context directory path (for debugging)
957
+ getContextDir(): string | null {
958
+ return this.verboseLogger?.getContextDir() ?? null;
959
+ }
960
+ }
961
+
962
+ // Legacy enum for backward compatibility
963
+ export enum RuntimeStatus {
964
+ RUNNING = 'running',
965
+ AWAITING_AI_RESPONSE = 'awaiting_ai',
966
+ AWAITING_COMPRESS = 'awaiting_compress',
967
+ AWAITING_USER_INPUT = 'awaiting_user',
968
+ AWAITING_TS = 'awaiting_ts',
969
+ AWAITING_TOOL = 'awaiting_tool',
970
+ COMPLETED = 'completed',
971
+ ERROR = 'error',
972
+ }
973
+
974
+ // Re-export tool system
975
+ export * from './tools';