@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.
- package/package.json +46 -0
- package/src/ast/index.ts +375 -0
- package/src/ast.ts +2 -0
- package/src/debug/advanced-features.ts +482 -0
- package/src/debug/bun-inspector.ts +424 -0
- package/src/debug/handoff-manager.ts +283 -0
- package/src/debug/index.ts +150 -0
- package/src/debug/runner.ts +365 -0
- package/src/debug/server.ts +565 -0
- package/src/debug/stack-merger.ts +267 -0
- package/src/debug/state.ts +581 -0
- package/src/debug/test/advanced-features.test.ts +300 -0
- package/src/debug/test/e2e.test.ts +218 -0
- package/src/debug/test/handoff-manager.test.ts +256 -0
- package/src/debug/test/runner.test.ts +256 -0
- package/src/debug/test/stack-merger.test.ts +163 -0
- package/src/debug/test/state.test.ts +400 -0
- package/src/debug/test/ts-debug-integration.test.ts +374 -0
- package/src/debug/test/ts-import-tracker.test.ts +125 -0
- package/src/debug/test/ts-source-map.test.ts +169 -0
- package/src/debug/ts-import-tracker.ts +151 -0
- package/src/debug/ts-source-map.ts +171 -0
- package/src/errors/index.ts +124 -0
- package/src/index.ts +358 -0
- package/src/lexer/index.ts +348 -0
- package/src/lexer.ts +2 -0
- package/src/parser/index.ts +792 -0
- package/src/parser/parse.ts +45 -0
- package/src/parser/test/async.test.ts +248 -0
- package/src/parser/test/destructuring.test.ts +167 -0
- package/src/parser/test/do-expression.test.ts +486 -0
- package/src/parser/test/errors/do-expression.test.ts +95 -0
- package/src/parser/test/errors/error-locations.test.ts +230 -0
- package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
- package/src/parser/test/errors/missing-tokens.test.ts +126 -0
- package/src/parser/test/errors/model-declaration.test.ts +185 -0
- package/src/parser/test/errors/nested-blocks.test.ts +226 -0
- package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
- package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
- package/src/parser/test/import-export.test.ts +143 -0
- package/src/parser/test/literals.test.ts +404 -0
- package/src/parser/test/model-declaration.test.ts +161 -0
- package/src/parser/test/nested-blocks.test.ts +402 -0
- package/src/parser/test/parser.test.ts +743 -0
- package/src/parser/test/private.test.ts +136 -0
- package/src/parser/test/template-literal.test.ts +127 -0
- package/src/parser/test/tool-declaration.test.ts +302 -0
- package/src/parser/test/ts-block.test.ts +252 -0
- package/src/parser/test/type-annotations.test.ts +254 -0
- package/src/parser/visitor/helpers.ts +330 -0
- package/src/parser/visitor.ts +794 -0
- package/src/parser.ts +2 -0
- package/src/runtime/ai/cache-chunking.test.ts +69 -0
- package/src/runtime/ai/cache-chunking.ts +73 -0
- package/src/runtime/ai/client.ts +109 -0
- package/src/runtime/ai/context.ts +168 -0
- package/src/runtime/ai/formatters.ts +316 -0
- package/src/runtime/ai/index.ts +38 -0
- package/src/runtime/ai/language-ref.ts +38 -0
- package/src/runtime/ai/providers/anthropic.ts +253 -0
- package/src/runtime/ai/providers/google.ts +201 -0
- package/src/runtime/ai/providers/openai.ts +156 -0
- package/src/runtime/ai/retry.ts +100 -0
- package/src/runtime/ai/return-tools.ts +301 -0
- package/src/runtime/ai/test/client.test.ts +83 -0
- package/src/runtime/ai/test/formatters.test.ts +485 -0
- package/src/runtime/ai/test/retry.test.ts +137 -0
- package/src/runtime/ai/test/return-tools.test.ts +450 -0
- package/src/runtime/ai/test/tool-loop.test.ts +319 -0
- package/src/runtime/ai/test/tool-schema.test.ts +241 -0
- package/src/runtime/ai/tool-loop.ts +203 -0
- package/src/runtime/ai/tool-schema.ts +151 -0
- package/src/runtime/ai/types.ts +113 -0
- package/src/runtime/ai-logger.ts +255 -0
- package/src/runtime/ai-provider.ts +347 -0
- package/src/runtime/async/dependencies.ts +276 -0
- package/src/runtime/async/executor.ts +293 -0
- package/src/runtime/async/index.ts +43 -0
- package/src/runtime/async/scheduling.ts +163 -0
- package/src/runtime/async/test/dependencies.test.ts +284 -0
- package/src/runtime/async/test/executor.test.ts +388 -0
- package/src/runtime/context.ts +357 -0
- package/src/runtime/exec/ai.ts +139 -0
- package/src/runtime/exec/expressions.ts +475 -0
- package/src/runtime/exec/frames.ts +26 -0
- package/src/runtime/exec/functions.ts +305 -0
- package/src/runtime/exec/interpolation.ts +312 -0
- package/src/runtime/exec/statements.ts +604 -0
- package/src/runtime/exec/tools.ts +129 -0
- package/src/runtime/exec/typescript.ts +215 -0
- package/src/runtime/exec/variables.ts +279 -0
- package/src/runtime/index.ts +975 -0
- package/src/runtime/modules.ts +452 -0
- package/src/runtime/serialize.ts +103 -0
- package/src/runtime/state.ts +489 -0
- package/src/runtime/stdlib/core.ts +45 -0
- package/src/runtime/stdlib/directory.test.ts +156 -0
- package/src/runtime/stdlib/edit.test.ts +154 -0
- package/src/runtime/stdlib/fastEdit.test.ts +201 -0
- package/src/runtime/stdlib/glob.test.ts +106 -0
- package/src/runtime/stdlib/grep.test.ts +144 -0
- package/src/runtime/stdlib/index.ts +16 -0
- package/src/runtime/stdlib/readFile.test.ts +123 -0
- package/src/runtime/stdlib/tools/index.ts +707 -0
- package/src/runtime/stdlib/writeFile.test.ts +157 -0
- package/src/runtime/step.ts +969 -0
- package/src/runtime/test/ai-context.test.ts +1086 -0
- package/src/runtime/test/ai-result-object.test.ts +419 -0
- package/src/runtime/test/ai-tool-flow.test.ts +859 -0
- package/src/runtime/test/async-execution-order.test.ts +618 -0
- package/src/runtime/test/async-execution.test.ts +344 -0
- package/src/runtime/test/async-nested.test.ts +660 -0
- package/src/runtime/test/async-parallel-timing.test.ts +546 -0
- package/src/runtime/test/basic1.test.ts +154 -0
- package/src/runtime/test/binary-operators.test.ts +431 -0
- package/src/runtime/test/break-statement.test.ts +257 -0
- package/src/runtime/test/context-modes.test.ts +650 -0
- package/src/runtime/test/context.test.ts +466 -0
- package/src/runtime/test/core-functions.test.ts +228 -0
- package/src/runtime/test/e2e.test.ts +88 -0
- package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
- package/src/runtime/test/error-locations/main-error.vibe +4 -0
- package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
- package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
- package/src/runtime/test/for-in.test.ts +312 -0
- package/src/runtime/test/helpers.ts +69 -0
- package/src/runtime/test/imports.test.ts +334 -0
- package/src/runtime/test/json-expressions.test.ts +232 -0
- package/src/runtime/test/literals.test.ts +372 -0
- package/src/runtime/test/logical-indexing.test.ts +478 -0
- package/src/runtime/test/member-methods.test.ts +324 -0
- package/src/runtime/test/model-config.test.ts +338 -0
- package/src/runtime/test/null-handling.test.ts +342 -0
- package/src/runtime/test/private-visibility.test.ts +332 -0
- package/src/runtime/test/runtime-state.test.ts +514 -0
- package/src/runtime/test/scoping.test.ts +370 -0
- package/src/runtime/test/string-interpolation.test.ts +354 -0
- package/src/runtime/test/template-literal.test.ts +181 -0
- package/src/runtime/test/tool-execution.test.ts +467 -0
- package/src/runtime/test/tool-schema-generation.test.ts +477 -0
- package/src/runtime/test/tostring.test.ts +210 -0
- package/src/runtime/test/ts-block.test.ts +594 -0
- package/src/runtime/test/ts-error-location.test.ts +231 -0
- package/src/runtime/test/types.test.ts +732 -0
- package/src/runtime/test/verbose-logger.test.ts +710 -0
- package/src/runtime/test/vibe-expression.test.ts +54 -0
- package/src/runtime/test/vibe-value-errors.test.ts +541 -0
- package/src/runtime/test/while.test.ts +232 -0
- package/src/runtime/tools/builtin.ts +30 -0
- package/src/runtime/tools/directory-tools.ts +70 -0
- package/src/runtime/tools/file-tools.ts +228 -0
- package/src/runtime/tools/index.ts +5 -0
- package/src/runtime/tools/registry.ts +48 -0
- package/src/runtime/tools/search-tools.ts +134 -0
- package/src/runtime/tools/security.ts +36 -0
- package/src/runtime/tools/system-tools.ts +312 -0
- package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
- package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
- package/src/runtime/tools/test/registry.test.ts +713 -0
- package/src/runtime/tools/test/security.test.ts +86 -0
- package/src/runtime/tools/test/system-tools.test.ts +679 -0
- package/src/runtime/tools/test/ts-schema.test.ts +357 -0
- package/src/runtime/tools/ts-schema.ts +341 -0
- package/src/runtime/tools/types.ts +89 -0
- package/src/runtime/tools/utility-tools.ts +198 -0
- package/src/runtime/ts-eval.ts +126 -0
- package/src/runtime/types.ts +797 -0
- package/src/runtime/validation.ts +160 -0
- package/src/runtime/verbose-logger.ts +459 -0
- package/src/runtime.ts +2 -0
- package/src/semantic/analyzer-context.ts +62 -0
- package/src/semantic/analyzer-validators.ts +575 -0
- package/src/semantic/analyzer-visitors.ts +534 -0
- package/src/semantic/analyzer.ts +83 -0
- package/src/semantic/index.ts +11 -0
- package/src/semantic/symbol-table.ts +58 -0
- package/src/semantic/test/async-validation.test.ts +301 -0
- package/src/semantic/test/compress-validation.test.ts +179 -0
- package/src/semantic/test/const-reassignment.test.ts +111 -0
- package/src/semantic/test/control-flow.test.ts +346 -0
- package/src/semantic/test/destructuring.test.ts +185 -0
- package/src/semantic/test/duplicate-declarations.test.ts +168 -0
- package/src/semantic/test/export-validation.test.ts +111 -0
- package/src/semantic/test/fixtures/math.ts +31 -0
- package/src/semantic/test/imports.test.ts +148 -0
- package/src/semantic/test/json-type.test.ts +68 -0
- package/src/semantic/test/literals.test.ts +127 -0
- package/src/semantic/test/model-validation.test.ts +179 -0
- package/src/semantic/test/prompt-validation.test.ts +343 -0
- package/src/semantic/test/scoping.test.ts +312 -0
- package/src/semantic/test/tool-validation.test.ts +306 -0
- package/src/semantic/test/ts-type-checking.test.ts +563 -0
- package/src/semantic/test/type-constraints.test.ts +111 -0
- package/src/semantic/test/type-inference.test.ts +87 -0
- package/src/semantic/test/type-validation.test.ts +552 -0
- package/src/semantic/test/undefined-variables.test.ts +163 -0
- package/src/semantic/ts-block-checker.ts +204 -0
- package/src/semantic/ts-signatures.ts +194 -0
- package/src/semantic/ts-types.ts +170 -0
- package/src/semantic/types.ts +58 -0
- package/tests/fixtures/conditional-logic.vibe +14 -0
- package/tests/fixtures/function-call.vibe +16 -0
- package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
- package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
- package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
- package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
- package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
- package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
- package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
- package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
- package/tests/fixtures/imports/nested-import/main.vibe +3 -0
- package/tests/fixtures/imports/nested-import/utils.ts +3 -0
- package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
- package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
- package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
- package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
- package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
- package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
- package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
- package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
- package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
- package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
- package/tests/fixtures/imports/ts-import/main.vibe +4 -0
- package/tests/fixtures/imports/ts-import/math.ts +9 -0
- package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/data.ts +10 -0
- package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
- package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
- package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
- package/tests/fixtures/multiple-ai-calls.vibe +10 -0
- package/tests/fixtures/simple-greeting.vibe +6 -0
- package/tests/fixtures/template-literals.vibe +11 -0
- package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
- package/tests/integration/basic-ai/basic-ai.vibe +12 -0
- package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
- package/tests/integration/bug-fix/buggy-code.ts +22 -0
- package/tests/integration/bug-fix/fix-bug.vibe +21 -0
- package/tests/integration/compress/compress.integration.test.ts +206 -0
- package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
- package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
- package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
- package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
- package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
- package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
- package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
- 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
|
+
});
|