@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,477 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parse } from '../../parser/parse';
|
|
3
|
+
import { Runtime, AIProvider } from '../index';
|
|
4
|
+
import type { VibeToolValue, ToolSchema } from '../tools/types';
|
|
5
|
+
|
|
6
|
+
// Mock AI provider for testing (never called in these tests)
|
|
7
|
+
function createMockProvider(): AIProvider {
|
|
8
|
+
return {
|
|
9
|
+
async execute() {
|
|
10
|
+
return { value: 'ai response' };
|
|
11
|
+
},
|
|
12
|
+
async generateCode() {
|
|
13
|
+
return { value: 'generated code' };
|
|
14
|
+
},
|
|
15
|
+
async askUser(): Promise<string> {
|
|
16
|
+
return 'user input';
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Helper to get tool schema from tool variable
|
|
22
|
+
function getToolSchema(runtime: Runtime, toolName: string): ToolSchema | undefined {
|
|
23
|
+
const tool = runtime.getValue(toolName) as VibeToolValue | undefined;
|
|
24
|
+
return tool?.schema;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('Tool Schema Generation', () => {
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Basic types - what schemas look like for simple Vibe tools
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
describe('Basic Types', () => {
|
|
33
|
+
test('tool with text parameter generates string schema', async () => {
|
|
34
|
+
const ast = parse(`
|
|
35
|
+
tool greet(name: text): text
|
|
36
|
+
@description "Greet someone"
|
|
37
|
+
{
|
|
38
|
+
ts(name) { return "Hello, " + name }
|
|
39
|
+
}
|
|
40
|
+
let x = "init"
|
|
41
|
+
`);
|
|
42
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
43
|
+
await runtime.run();
|
|
44
|
+
|
|
45
|
+
const greetSchema = getToolSchema(runtime, 'greet');
|
|
46
|
+
|
|
47
|
+
expect(greetSchema).toEqual({
|
|
48
|
+
name: 'greet',
|
|
49
|
+
description: 'Greet someone',
|
|
50
|
+
parameters: [
|
|
51
|
+
{
|
|
52
|
+
name: 'name',
|
|
53
|
+
type: { type: 'string' },
|
|
54
|
+
required: true,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
returns: { type: 'string' },
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('tool with number parameter generates number schema', async () => {
|
|
62
|
+
const ast = parse(`
|
|
63
|
+
tool double(n: number): number
|
|
64
|
+
@description "Double a number"
|
|
65
|
+
{
|
|
66
|
+
ts(n) { return n * 2 }
|
|
67
|
+
}
|
|
68
|
+
let x = "init"
|
|
69
|
+
`);
|
|
70
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
71
|
+
await runtime.run();
|
|
72
|
+
|
|
73
|
+
const doubleSchema = getToolSchema(runtime, 'double');
|
|
74
|
+
|
|
75
|
+
expect(doubleSchema).toEqual({
|
|
76
|
+
name: 'double',
|
|
77
|
+
description: 'Double a number',
|
|
78
|
+
parameters: [
|
|
79
|
+
{
|
|
80
|
+
name: 'n',
|
|
81
|
+
type: { type: 'number' },
|
|
82
|
+
required: true,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
returns: { type: 'number' },
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('tool with boolean parameter generates boolean schema', async () => {
|
|
90
|
+
const ast = parse(`
|
|
91
|
+
tool negate(flag: boolean): boolean
|
|
92
|
+
@description "Negate a boolean"
|
|
93
|
+
{
|
|
94
|
+
ts(flag) { return !flag }
|
|
95
|
+
}
|
|
96
|
+
let x = "init"
|
|
97
|
+
`);
|
|
98
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
99
|
+
await runtime.run();
|
|
100
|
+
|
|
101
|
+
const negateSchema = getToolSchema(runtime, 'negate');
|
|
102
|
+
|
|
103
|
+
expect(negateSchema).toEqual({
|
|
104
|
+
name: 'negate',
|
|
105
|
+
description: 'Negate a boolean',
|
|
106
|
+
parameters: [
|
|
107
|
+
{
|
|
108
|
+
name: 'flag',
|
|
109
|
+
type: { type: 'boolean' },
|
|
110
|
+
required: true,
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
returns: { type: 'boolean' },
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('tool with json parameter generates object schema', async () => {
|
|
118
|
+
const ast = parse(`
|
|
119
|
+
tool processData(data: json): json
|
|
120
|
+
@description "Process JSON data"
|
|
121
|
+
{
|
|
122
|
+
ts(data) { return { processed: true, ...data } }
|
|
123
|
+
}
|
|
124
|
+
let x = "init"
|
|
125
|
+
`);
|
|
126
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
127
|
+
await runtime.run();
|
|
128
|
+
|
|
129
|
+
const processSchema = getToolSchema(runtime, 'processData');
|
|
130
|
+
|
|
131
|
+
expect(processSchema).toEqual({
|
|
132
|
+
name: 'processData',
|
|
133
|
+
description: 'Process JSON data',
|
|
134
|
+
parameters: [
|
|
135
|
+
{
|
|
136
|
+
name: 'data',
|
|
137
|
+
type: { type: 'object', additionalProperties: true },
|
|
138
|
+
description: undefined,
|
|
139
|
+
required: true,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
returns: { type: 'object', additionalProperties: true },
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// Array types
|
|
149
|
+
// ============================================================================
|
|
150
|
+
|
|
151
|
+
describe('Array Types', () => {
|
|
152
|
+
test('tool with text array parameter', async () => {
|
|
153
|
+
const ast = parse(`
|
|
154
|
+
tool joinStrings(items: text[]): text
|
|
155
|
+
@description "Join strings"
|
|
156
|
+
{
|
|
157
|
+
ts(items) { return items.join(", ") }
|
|
158
|
+
}
|
|
159
|
+
let x = "init"
|
|
160
|
+
`);
|
|
161
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
162
|
+
await runtime.run();
|
|
163
|
+
|
|
164
|
+
const joinSchema = getToolSchema(runtime, 'joinStrings');
|
|
165
|
+
|
|
166
|
+
expect(joinSchema).toEqual({
|
|
167
|
+
name: 'joinStrings',
|
|
168
|
+
description: 'Join strings',
|
|
169
|
+
parameters: [
|
|
170
|
+
{
|
|
171
|
+
name: 'items',
|
|
172
|
+
type: { type: 'array', items: { type: 'string' } },
|
|
173
|
+
required: true,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
returns: { type: 'string' },
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('tool with number array parameter', async () => {
|
|
181
|
+
const ast = parse(`
|
|
182
|
+
tool sum(numbers: number[]): number
|
|
183
|
+
@description "Sum numbers"
|
|
184
|
+
{
|
|
185
|
+
ts(numbers) { return numbers.reduce((a, b) => a + b, 0) }
|
|
186
|
+
}
|
|
187
|
+
let x = "init"
|
|
188
|
+
`);
|
|
189
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
190
|
+
await runtime.run();
|
|
191
|
+
|
|
192
|
+
const sumSchema = getToolSchema(runtime, 'sum');
|
|
193
|
+
|
|
194
|
+
expect(sumSchema).toEqual({
|
|
195
|
+
name: 'sum',
|
|
196
|
+
description: 'Sum numbers',
|
|
197
|
+
parameters: [
|
|
198
|
+
{
|
|
199
|
+
name: 'numbers',
|
|
200
|
+
type: { type: 'array', items: { type: 'number' } },
|
|
201
|
+
required: true,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
returns: { type: 'number' },
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('tool with json array return type', async () => {
|
|
209
|
+
const ast = parse(`
|
|
210
|
+
tool getItems(): json[]
|
|
211
|
+
@description "Get items"
|
|
212
|
+
{
|
|
213
|
+
ts() { return [{id: 1}, {id: 2}] }
|
|
214
|
+
}
|
|
215
|
+
let x = "init"
|
|
216
|
+
`);
|
|
217
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
218
|
+
await runtime.run();
|
|
219
|
+
|
|
220
|
+
const getItemsSchema = getToolSchema(runtime, 'getItems');
|
|
221
|
+
|
|
222
|
+
expect(getItemsSchema).toEqual({
|
|
223
|
+
name: 'getItems',
|
|
224
|
+
description: 'Get items',
|
|
225
|
+
parameters: [],
|
|
226
|
+
returns: { type: 'array', items: { type: 'object', additionalProperties: true } },
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// ============================================================================
|
|
232
|
+
// Multiple parameters
|
|
233
|
+
// ============================================================================
|
|
234
|
+
|
|
235
|
+
describe('Multiple Parameters', () => {
|
|
236
|
+
test('tool with multiple parameters of different types', async () => {
|
|
237
|
+
const ast = parse(`
|
|
238
|
+
tool calculate(x: number, y: number, op: text): number
|
|
239
|
+
@description "Perform a calculation"
|
|
240
|
+
{
|
|
241
|
+
ts(x, y, op) {
|
|
242
|
+
if (op === "add") return x + y
|
|
243
|
+
if (op === "mul") return x * y
|
|
244
|
+
return 0
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
let x = "init"
|
|
248
|
+
`);
|
|
249
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
250
|
+
await runtime.run();
|
|
251
|
+
|
|
252
|
+
const calcSchema = getToolSchema(runtime, 'calculate');
|
|
253
|
+
|
|
254
|
+
expect(calcSchema).toEqual({
|
|
255
|
+
name: 'calculate',
|
|
256
|
+
description: 'Perform a calculation',
|
|
257
|
+
parameters: [
|
|
258
|
+
{ name: 'x', type: { type: 'number' }, required: true },
|
|
259
|
+
{ name: 'y', type: { type: 'number' }, required: true },
|
|
260
|
+
{ name: 'op', type: { type: 'string' }, required: true },
|
|
261
|
+
],
|
|
262
|
+
returns: { type: 'number' },
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test('tool with mixed primitive and array parameters', async () => {
|
|
267
|
+
const ast = parse(`
|
|
268
|
+
tool filter(items: text[], prefix: text): text[]
|
|
269
|
+
@description "Filter items by prefix"
|
|
270
|
+
{
|
|
271
|
+
ts(items, prefix) { return items.filter(i => i.startsWith(prefix)) }
|
|
272
|
+
}
|
|
273
|
+
let x = "init"
|
|
274
|
+
`);
|
|
275
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
276
|
+
await runtime.run();
|
|
277
|
+
|
|
278
|
+
const filterSchema = getToolSchema(runtime, 'filter');
|
|
279
|
+
|
|
280
|
+
expect(filterSchema).toEqual({
|
|
281
|
+
name: 'filter',
|
|
282
|
+
description: 'Filter items by prefix',
|
|
283
|
+
parameters: [
|
|
284
|
+
{ name: 'items', type: { type: 'array', items: { type: 'string' } }, required: true },
|
|
285
|
+
{ name: 'prefix', type: { type: 'string' }, required: true },
|
|
286
|
+
],
|
|
287
|
+
returns: { type: 'array', items: { type: 'string' } },
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// ============================================================================
|
|
293
|
+
// Decorators - @description and @param
|
|
294
|
+
// ============================================================================
|
|
295
|
+
|
|
296
|
+
describe('Decorator Usage', () => {
|
|
297
|
+
test('tool with @description only', async () => {
|
|
298
|
+
const ast = parse(`
|
|
299
|
+
tool getCurrentTime(): text
|
|
300
|
+
@description "Get the current ISO timestamp"
|
|
301
|
+
{
|
|
302
|
+
ts() { return new Date().toISOString() }
|
|
303
|
+
}
|
|
304
|
+
let x = "init"
|
|
305
|
+
`);
|
|
306
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
307
|
+
await runtime.run();
|
|
308
|
+
|
|
309
|
+
const timeSchema = getToolSchema(runtime, 'getCurrentTime');
|
|
310
|
+
|
|
311
|
+
expect(timeSchema?.description).toBe('Get the current ISO timestamp');
|
|
312
|
+
expect(timeSchema?.parameters).toEqual([]);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('tool with @param descriptions', async () => {
|
|
316
|
+
const ast = parse(`
|
|
317
|
+
tool sendEmail(to: text, subject: text, body: text): boolean
|
|
318
|
+
@description "Send an email"
|
|
319
|
+
@param to "The recipient email address"
|
|
320
|
+
@param subject "The email subject line"
|
|
321
|
+
@param body "The email body content"
|
|
322
|
+
{
|
|
323
|
+
ts(to, subject, body) { return true }
|
|
324
|
+
}
|
|
325
|
+
let x = "init"
|
|
326
|
+
`);
|
|
327
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
328
|
+
await runtime.run();
|
|
329
|
+
|
|
330
|
+
const emailSchema = getToolSchema(runtime, 'sendEmail');
|
|
331
|
+
|
|
332
|
+
expect(emailSchema).toEqual({
|
|
333
|
+
name: 'sendEmail',
|
|
334
|
+
description: 'Send an email',
|
|
335
|
+
parameters: [
|
|
336
|
+
{ name: 'to', type: { type: 'string' }, description: 'The recipient email address', required: true },
|
|
337
|
+
{ name: 'subject', type: { type: 'string' }, description: 'The email subject line', required: true },
|
|
338
|
+
{ name: 'body', type: { type: 'string' }, description: 'The email body content', required: true },
|
|
339
|
+
],
|
|
340
|
+
returns: { type: 'boolean' },
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('tool with partial @param descriptions (not all params described)', async () => {
|
|
345
|
+
const ast = parse(`
|
|
346
|
+
tool search(query: text, limit: number, exact: boolean): json
|
|
347
|
+
@description "Search for items"
|
|
348
|
+
@param query "The search query"
|
|
349
|
+
{
|
|
350
|
+
ts(query, limit, exact) { return [] }
|
|
351
|
+
}
|
|
352
|
+
let x = "init"
|
|
353
|
+
`);
|
|
354
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
355
|
+
await runtime.run();
|
|
356
|
+
|
|
357
|
+
const searchSchema = getToolSchema(runtime, 'search');
|
|
358
|
+
|
|
359
|
+
expect(searchSchema).toEqual({
|
|
360
|
+
name: 'search',
|
|
361
|
+
description: 'Search for items',
|
|
362
|
+
parameters: [
|
|
363
|
+
{ name: 'query', type: { type: 'string' }, description: 'The search query', required: true },
|
|
364
|
+
{ name: 'limit', type: { type: 'number' }, description: undefined, required: true },
|
|
365
|
+
{ name: 'exact', type: { type: 'boolean' }, description: undefined, required: true },
|
|
366
|
+
],
|
|
367
|
+
returns: { type: 'object', additionalProperties: true },
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// Multiple tools - full registry schema
|
|
374
|
+
// ============================================================================
|
|
375
|
+
|
|
376
|
+
describe('Multiple Tools', () => {
|
|
377
|
+
test('multiple tools can be defined and have correct schemas', async () => {
|
|
378
|
+
const ast = parse(`
|
|
379
|
+
tool add(a: number, b: number): number
|
|
380
|
+
@description "Add two numbers"
|
|
381
|
+
{
|
|
382
|
+
ts(a, b) { return a + b }
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
tool multiply(a: number, b: number): number
|
|
386
|
+
@description "Multiply two numbers"
|
|
387
|
+
{
|
|
388
|
+
ts(a, b) { return a * b }
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
tool greet(name: text): text
|
|
392
|
+
@description "Greet someone"
|
|
393
|
+
@param name "The person's name"
|
|
394
|
+
{
|
|
395
|
+
ts(name) { return "Hello, " + name }
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
let x = "init"
|
|
399
|
+
`);
|
|
400
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
401
|
+
await runtime.run();
|
|
402
|
+
|
|
403
|
+
// Verify each tool's schema by getting it from the tool variable
|
|
404
|
+
expect(getToolSchema(runtime, 'add')).toEqual({
|
|
405
|
+
name: 'add',
|
|
406
|
+
description: 'Add two numbers',
|
|
407
|
+
parameters: [
|
|
408
|
+
{ name: 'a', type: { type: 'number' }, required: true },
|
|
409
|
+
{ name: 'b', type: { type: 'number' }, required: true },
|
|
410
|
+
],
|
|
411
|
+
returns: { type: 'number' },
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
expect(getToolSchema(runtime, 'multiply')).toEqual({
|
|
415
|
+
name: 'multiply',
|
|
416
|
+
description: 'Multiply two numbers',
|
|
417
|
+
parameters: [
|
|
418
|
+
{ name: 'a', type: { type: 'number' }, required: true },
|
|
419
|
+
{ name: 'b', type: { type: 'number' }, required: true },
|
|
420
|
+
],
|
|
421
|
+
returns: { type: 'number' },
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
expect(getToolSchema(runtime, 'greet')).toEqual({
|
|
425
|
+
name: 'greet',
|
|
426
|
+
description: 'Greet someone',
|
|
427
|
+
parameters: [
|
|
428
|
+
{ name: 'name', type: { type: 'string' }, description: "The person's name", required: true },
|
|
429
|
+
],
|
|
430
|
+
returns: { type: 'string' },
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test('standard tools are NOT auto-available', async () => {
|
|
435
|
+
// Standard tools must be imported from system modules
|
|
436
|
+
const ast = parse(`let x = "init"`);
|
|
437
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
438
|
+
await runtime.run();
|
|
439
|
+
|
|
440
|
+
// Standard tools should not be defined as variables
|
|
441
|
+
const standardToolNames = ['sleep', 'now', 'jsonParse', 'jsonStringify', 'env', 'readFile'];
|
|
442
|
+
for (const name of standardToolNames) {
|
|
443
|
+
expect(runtime.getValue(name)).toBeUndefined();
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// ============================================================================
|
|
449
|
+
// No return type
|
|
450
|
+
// ============================================================================
|
|
451
|
+
|
|
452
|
+
describe('No Return Type', () => {
|
|
453
|
+
test('tool without return type annotation', async () => {
|
|
454
|
+
const ast = parse(`
|
|
455
|
+
tool logMessage(message: text)
|
|
456
|
+
@description "Log a message"
|
|
457
|
+
{
|
|
458
|
+
ts(message) { console.log(message) }
|
|
459
|
+
}
|
|
460
|
+
let x = "init"
|
|
461
|
+
`);
|
|
462
|
+
const runtime = new Runtime(ast, createMockProvider());
|
|
463
|
+
await runtime.run();
|
|
464
|
+
|
|
465
|
+
const logSchema = getToolSchema(runtime, 'logMessage');
|
|
466
|
+
|
|
467
|
+
expect(logSchema).toEqual({
|
|
468
|
+
name: 'logMessage',
|
|
469
|
+
description: 'Log a message',
|
|
470
|
+
parameters: [
|
|
471
|
+
{ name: 'message', type: { type: 'string' }, required: true },
|
|
472
|
+
],
|
|
473
|
+
// No 'returns' field when return type is not specified
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* toString() Method Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the toString() method for explicit type coercion.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect } from 'bun:test';
|
|
7
|
+
import { parse } from '../../parser/parse';
|
|
8
|
+
import { createInitialState, runUntilPause, currentFrame } from '..';
|
|
9
|
+
import { resolveValue } from '../types';
|
|
10
|
+
|
|
11
|
+
describe('toString() method', () => {
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Number toString
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
test('number.toString() returns string', () => {
|
|
17
|
+
const program = parse(`
|
|
18
|
+
let num: number = 42
|
|
19
|
+
let str = num.toString()
|
|
20
|
+
`);
|
|
21
|
+
let state = createInitialState(program);
|
|
22
|
+
state = runUntilPause(state);
|
|
23
|
+
|
|
24
|
+
expect(state.status).toBe('completed');
|
|
25
|
+
const frame = currentFrame(state);
|
|
26
|
+
expect(resolveValue(frame.locals['str'])).toBe('42');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('negative number.toString()', () => {
|
|
30
|
+
const program = parse(`
|
|
31
|
+
let num: number = -123
|
|
32
|
+
let str = num.toString()
|
|
33
|
+
`);
|
|
34
|
+
let state = createInitialState(program);
|
|
35
|
+
state = runUntilPause(state);
|
|
36
|
+
|
|
37
|
+
expect(state.status).toBe('completed');
|
|
38
|
+
const frame = currentFrame(state);
|
|
39
|
+
expect(resolveValue(frame.locals['str'])).toBe('-123');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('decimal number.toString()', () => {
|
|
43
|
+
const program = parse(`
|
|
44
|
+
let num: number = 3.14159
|
|
45
|
+
let str = num.toString()
|
|
46
|
+
`);
|
|
47
|
+
let state = createInitialState(program);
|
|
48
|
+
state = runUntilPause(state);
|
|
49
|
+
|
|
50
|
+
expect(state.status).toBe('completed');
|
|
51
|
+
const frame = currentFrame(state);
|
|
52
|
+
expect(resolveValue(frame.locals['str'])).toBe('3.14159');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Boolean toString
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
test('true.toString() returns "true"', () => {
|
|
60
|
+
const program = parse(`
|
|
61
|
+
let flag: boolean = true
|
|
62
|
+
let str = flag.toString()
|
|
63
|
+
`);
|
|
64
|
+
let state = createInitialState(program);
|
|
65
|
+
state = runUntilPause(state);
|
|
66
|
+
|
|
67
|
+
expect(state.status).toBe('completed');
|
|
68
|
+
const frame = currentFrame(state);
|
|
69
|
+
expect(resolveValue(frame.locals['str'])).toBe('true');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('false.toString() returns "false"', () => {
|
|
73
|
+
const program = parse(`
|
|
74
|
+
let flag: boolean = false
|
|
75
|
+
let str = flag.toString()
|
|
76
|
+
`);
|
|
77
|
+
let state = createInitialState(program);
|
|
78
|
+
state = runUntilPause(state);
|
|
79
|
+
|
|
80
|
+
expect(state.status).toBe('completed');
|
|
81
|
+
const frame = currentFrame(state);
|
|
82
|
+
expect(resolveValue(frame.locals['str'])).toBe('false');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Object/JSON toString
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
test('object.toString() returns JSON string', () => {
|
|
90
|
+
const program = parse(`
|
|
91
|
+
let data: json = { name: "Alice", age: 30 }
|
|
92
|
+
let str = data.toString()
|
|
93
|
+
`);
|
|
94
|
+
let state = createInitialState(program);
|
|
95
|
+
state = runUntilPause(state);
|
|
96
|
+
|
|
97
|
+
expect(state.status).toBe('completed');
|
|
98
|
+
const frame = currentFrame(state);
|
|
99
|
+
const result = resolveValue(frame.locals['str']);
|
|
100
|
+
expect(result).toBe('{"name":"Alice","age":30}');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('nested object.toString()', () => {
|
|
104
|
+
const program = parse(`
|
|
105
|
+
let data: json = { user: { name: "Bob" } }
|
|
106
|
+
let str = data.toString()
|
|
107
|
+
`);
|
|
108
|
+
let state = createInitialState(program);
|
|
109
|
+
state = runUntilPause(state);
|
|
110
|
+
|
|
111
|
+
expect(state.status).toBe('completed');
|
|
112
|
+
const frame = currentFrame(state);
|
|
113
|
+
const result = resolveValue(frame.locals['str']);
|
|
114
|
+
expect(result).toBe('{"user":{"name":"Bob"}}');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Array toString
|
|
119
|
+
// ============================================================================
|
|
120
|
+
|
|
121
|
+
test('array.toString() returns JSON string', () => {
|
|
122
|
+
const program = parse(`
|
|
123
|
+
let arr = [1, 2, 3]
|
|
124
|
+
let str = arr.toString()
|
|
125
|
+
`);
|
|
126
|
+
let state = createInitialState(program);
|
|
127
|
+
state = runUntilPause(state);
|
|
128
|
+
|
|
129
|
+
expect(state.status).toBe('completed');
|
|
130
|
+
const frame = currentFrame(state);
|
|
131
|
+
expect(resolveValue(frame.locals['str'])).toBe('[1,2,3]');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('array of strings.toString()', () => {
|
|
135
|
+
const program = parse(`
|
|
136
|
+
let arr = ["a", "b", "c"]
|
|
137
|
+
let str = arr.toString()
|
|
138
|
+
`);
|
|
139
|
+
let state = createInitialState(program);
|
|
140
|
+
state = runUntilPause(state);
|
|
141
|
+
|
|
142
|
+
expect(state.status).toBe('completed');
|
|
143
|
+
const frame = currentFrame(state);
|
|
144
|
+
expect(resolveValue(frame.locals['str'])).toBe('["a","b","c"]');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('typed number[] array.toString()', () => {
|
|
148
|
+
const program = parse(`
|
|
149
|
+
let arr: number[] = [1, 2, 3]
|
|
150
|
+
let str = arr.toString()
|
|
151
|
+
`);
|
|
152
|
+
let state = createInitialState(program);
|
|
153
|
+
state = runUntilPause(state);
|
|
154
|
+
|
|
155
|
+
expect(state.status).toBe('completed');
|
|
156
|
+
const frame = currentFrame(state);
|
|
157
|
+
expect(resolveValue(frame.locals['str'])).toBe('[1,2,3]');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Null toString
|
|
162
|
+
// ============================================================================
|
|
163
|
+
|
|
164
|
+
test('null.toString() returns empty string', () => {
|
|
165
|
+
const program = parse(`
|
|
166
|
+
let x: text = null
|
|
167
|
+
let str = x.toString()
|
|
168
|
+
`);
|
|
169
|
+
let state = createInitialState(program);
|
|
170
|
+
state = runUntilPause(state);
|
|
171
|
+
|
|
172
|
+
expect(state.status).toBe('completed');
|
|
173
|
+
const frame = currentFrame(state);
|
|
174
|
+
expect(resolveValue(frame.locals['str'])).toBe('');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// ============================================================================
|
|
178
|
+
// String toString (identity)
|
|
179
|
+
// ============================================================================
|
|
180
|
+
|
|
181
|
+
test('string.toString() returns same string', () => {
|
|
182
|
+
const program = parse(`
|
|
183
|
+
let s: text = "hello"
|
|
184
|
+
let str = s.toString()
|
|
185
|
+
`);
|
|
186
|
+
let state = createInitialState(program);
|
|
187
|
+
state = runUntilPause(state);
|
|
188
|
+
|
|
189
|
+
expect(state.status).toBe('completed');
|
|
190
|
+
const frame = currentFrame(state);
|
|
191
|
+
expect(resolveValue(frame.locals['str'])).toBe('hello');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Chaining and expressions
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
test('toString in expression', () => {
|
|
199
|
+
const program = parse(`
|
|
200
|
+
let num: number = 42
|
|
201
|
+
let msg = "Value: " + num.toString()
|
|
202
|
+
`);
|
|
203
|
+
let state = createInitialState(program);
|
|
204
|
+
state = runUntilPause(state);
|
|
205
|
+
|
|
206
|
+
expect(state.status).toBe('completed');
|
|
207
|
+
const frame = currentFrame(state);
|
|
208
|
+
expect(resolveValue(frame.locals['msg'])).toBe('Value: 42');
|
|
209
|
+
});
|
|
210
|
+
});
|