@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,467 @@
1
+ import { describe, expect, test, beforeEach } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { Runtime, AIProvider } from '../index';
4
+ import type { VibeToolValue } from '../tools/types';
5
+ import { isVibeToolValue } from '../tools/types';
6
+
7
+ // Mock AI provider for testing
8
+ function createMockProvider(): AIProvider {
9
+ return {
10
+ async execute() {
11
+ return { value: 'ai response' };
12
+ },
13
+ async generateCode() {
14
+ return { value: 'generated code' };
15
+ },
16
+ async askUser(): Promise<string> {
17
+ return 'user input';
18
+ },
19
+ };
20
+ }
21
+
22
+ describe('Runtime - Tool Declaration', () => {
23
+ // ============================================================================
24
+ // Basic tool registration
25
+ // ============================================================================
26
+
27
+ test('tool declaration creates tool variable', async () => {
28
+ const ast = parse(`
29
+ tool greet(name: text): text
30
+ @description "Greet someone"
31
+ {
32
+ ts(name) {
33
+ return "Hello, " + name
34
+ }
35
+ }
36
+
37
+ let x = "test"
38
+ `);
39
+ const runtime = new Runtime(ast, createMockProvider());
40
+ await runtime.run();
41
+
42
+ // Tool is now stored as a variable, not in registry
43
+ const tool = runtime.getValue('greet') as VibeToolValue;
44
+ expect(isVibeToolValue(tool)).toBe(true);
45
+ expect(tool.name).toBe('greet');
46
+ expect(tool.schema.description).toBe('Greet someone');
47
+ });
48
+
49
+ test('tool with multiple parameters has correct schema', async () => {
50
+ const ast = parse(`
51
+ tool calculate(x: number, y: number, op: text): number
52
+ @description "Perform a calculation"
53
+ @param x "First operand"
54
+ @param y "Second operand"
55
+ @param op "Operation to perform"
56
+ {
57
+ ts(x, y, op) {
58
+ if (op === "add") return x + y
59
+ return x * y
60
+ }
61
+ }
62
+
63
+ let result = "done"
64
+ `);
65
+ const runtime = new Runtime(ast, createMockProvider());
66
+ await runtime.run();
67
+
68
+ const tool = runtime.getValue('calculate') as VibeToolValue;
69
+ expect(tool.schema.parameters).toHaveLength(3);
70
+ expect(tool.schema.parameters[0].description).toBe('First operand');
71
+ expect(tool.schema.parameters[1].description).toBe('Second operand');
72
+ expect(tool.schema.parameters[2].description).toBe('Operation to perform');
73
+ });
74
+
75
+ // ============================================================================
76
+ // Tools cannot be called directly from vibe scripts
77
+ // They can only be used by AI models via the tools array in model declarations
78
+ // ============================================================================
79
+
80
+ test('tool cannot be called directly', async () => {
81
+ const ast = parse(`
82
+ tool double(n: number): number
83
+ @description "Double a number"
84
+ {
85
+ ts(n) {
86
+ return n * 2
87
+ }
88
+ }
89
+
90
+ let result = double(21)
91
+ `);
92
+ const runtime = new Runtime(ast, createMockProvider());
93
+ await expect(runtime.run()).rejects.toThrow(
94
+ "Cannot call tool 'double' directly"
95
+ );
96
+ });
97
+
98
+ test('tool with multiple parameters cannot be called directly', async () => {
99
+ const ast = parse(`
100
+ tool add(a: number, b: number): number
101
+ @description "Add two numbers"
102
+ {
103
+ ts(a, b) { return a + b }
104
+ }
105
+
106
+ let sum = add(10, 32)
107
+ `);
108
+ const runtime = new Runtime(ast, createMockProvider());
109
+ await expect(runtime.run()).rejects.toThrow(
110
+ "Cannot call tool 'add' directly"
111
+ );
112
+ });
113
+
114
+ test('multiple tools can be defined (but not called directly)', async () => {
115
+ const ast = parse(`
116
+ tool add(a: number, b: number): number
117
+ @description "Add"
118
+ {
119
+ ts(a, b) { return a + b }
120
+ }
121
+
122
+ tool multiply(a: number, b: number): number
123
+ @description "Multiply"
124
+ {
125
+ ts(a, b) { return a * b }
126
+ }
127
+
128
+ let x = 1
129
+ `);
130
+ const runtime = new Runtime(ast, createMockProvider());
131
+ await runtime.run();
132
+
133
+ // Verify both tools are defined
134
+ const addTool = runtime.getValue('add') as VibeToolValue;
135
+ const multiplyTool = runtime.getValue('multiply') as VibeToolValue;
136
+ expect(isVibeToolValue(addTool)).toBe(true);
137
+ expect(isVibeToolValue(multiplyTool)).toBe(true);
138
+ expect(addTool.name).toBe('add');
139
+ expect(multiplyTool.name).toBe('multiply');
140
+ });
141
+
142
+ // ============================================================================
143
+ // Standard tools must be explicitly registered
144
+ // ============================================================================
145
+
146
+ test('standard tools are NOT auto-available', async () => {
147
+ // Standard tools must be explicitly imported via system modules
148
+ const ast = parse(`
149
+ let timestamp = now()
150
+ `);
151
+ const runtime = new Runtime(ast, createMockProvider());
152
+
153
+ // This should fail because 'now' is not defined (no import)
154
+ await expect(runtime.run()).rejects.toThrow("'now' is not defined");
155
+ });
156
+
157
+ // Core functions (env, print) are auto-imported and available without explicit import
158
+ test('env function works without import (auto-imported)', async () => {
159
+ process.env.TEST_TOOL_VAR = 'test-value';
160
+ const ast = parse(`
161
+ let value = env("TEST_TOOL_VAR")
162
+ `);
163
+ const runtime = new Runtime(ast, createMockProvider());
164
+ await runtime.run();
165
+ expect(runtime.getValue('value')).toBe('test-value');
166
+ delete process.env.TEST_TOOL_VAR;
167
+ });
168
+
169
+ test('print function works without import (auto-imported)', async () => {
170
+ const ast = parse(`
171
+ let _ = print("hello")
172
+ let x = 1
173
+ `);
174
+ const runtime = new Runtime(ast, createMockProvider());
175
+ await runtime.run();
176
+ // print returns void, just verify it doesn't throw
177
+ expect(runtime.getValue('x')).toBe(1);
178
+ });
179
+
180
+ test('uuid function works when imported from system', async () => {
181
+ const ast = parse(`
182
+ import { uuid } from "system"
183
+ let id = uuid()
184
+ `);
185
+ const runtime = new Runtime(ast, createMockProvider());
186
+ await runtime.run();
187
+ const id = runtime.getValue('id') as string;
188
+ expect(typeof id).toBe('string');
189
+ expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
190
+ });
191
+
192
+ // ============================================================================
193
+ // Tools cannot be called directly - only used by AI models
194
+ // ============================================================================
195
+
196
+ test('tools cannot be called directly from vibe scripts', async () => {
197
+ const ast = parse(`
198
+ import { now } from "system/tools"
199
+ let timestamp = now()
200
+ `);
201
+ const runtime = new Runtime(ast, createMockProvider());
202
+ await expect(runtime.run()).rejects.toThrow(
203
+ "Cannot call tool 'now' directly"
204
+ );
205
+ });
206
+
207
+ test('jsonParse tool cannot be called directly', async () => {
208
+ const ast = parse(`
209
+ import { jsonParse } from "system/tools"
210
+ let parsed = jsonParse('{"key": "value"}')
211
+ `);
212
+ const runtime = new Runtime(ast, createMockProvider());
213
+ await expect(runtime.run()).rejects.toThrow(
214
+ "Cannot call tool 'jsonParse' directly"
215
+ );
216
+ });
217
+
218
+ test('jsonStringify tool cannot be called directly', async () => {
219
+ const ast = parse(`
220
+ import { jsonStringify } from "system/tools"
221
+ let obj:json = {name: "test"}
222
+ let str = jsonStringify(obj)
223
+ `);
224
+ const runtime = new Runtime(ast, createMockProvider());
225
+ await expect(runtime.run()).rejects.toThrow(
226
+ "Cannot call tool 'jsonStringify' directly"
227
+ );
228
+ });
229
+
230
+ test('allTools array can be imported from system/tools', async () => {
231
+ const ast = parse(`
232
+ import { allTools } from "system/tools"
233
+ let toolCount = ts(allTools) { return allTools.length }
234
+ `);
235
+ const runtime = new Runtime(ast, createMockProvider());
236
+ await runtime.run();
237
+ const toolCount = runtime.getValue('toolCount');
238
+ expect(toolCount).toBe(19); // File, search, directory, utility, and system tools for AI
239
+ });
240
+ });
241
+
242
+ describe('Runtime - Tool Error Handling', () => {
243
+ test('tool throws error on undefined tool call', async () => {
244
+ const ast = parse(`
245
+ let result = undefinedTool("arg")
246
+ `);
247
+ const runtime = new Runtime(ast, createMockProvider());
248
+
249
+ await expect(runtime.run()).rejects.toThrow("'undefinedTool' is not defined");
250
+ });
251
+
252
+ test('user-defined tool cannot be called directly', async () => {
253
+ const ast = parse(`
254
+ tool myTool(): text
255
+ @description "A custom tool"
256
+ {
257
+ ts() {
258
+ return "result"
259
+ }
260
+ }
261
+
262
+ let result = myTool()
263
+ `);
264
+ const runtime = new Runtime(ast, createMockProvider());
265
+
266
+ await expect(runtime.run()).rejects.toThrow(
267
+ "Cannot call tool 'myTool' directly"
268
+ );
269
+ });
270
+ });
271
+
272
+ describe('Runtime - Model with Tools', () => {
273
+ test('model can have tools array with custom tools', async () => {
274
+ const ast = parse(`
275
+ tool greet(name: text): text
276
+ @description "Greet someone"
277
+ {
278
+ ts(name) { return "Hello, " + name }
279
+ }
280
+
281
+ model m = {
282
+ name: "gpt-4",
283
+ apiKey: "test-key",
284
+ tools: [greet]
285
+ }
286
+
287
+ let x = 1
288
+ `);
289
+ const runtime = new Runtime(ast, createMockProvider());
290
+ await runtime.run();
291
+
292
+ // Verify model has the tool attached
293
+ const model = runtime.getValue('m') as { tools?: unknown[] };
294
+ expect(model.tools).toHaveLength(1);
295
+ expect((model.tools![0] as { name: string }).name).toBe('greet');
296
+ });
297
+
298
+ test('model can have tools array with imported tools', async () => {
299
+ const ast = parse(`
300
+ import { readFile, writeFile } from "system/tools"
301
+
302
+ model m = {
303
+ name: "gpt-4",
304
+ apiKey: "test-key",
305
+ tools: [readFile, writeFile]
306
+ }
307
+
308
+ let x = 1
309
+ `);
310
+ const runtime = new Runtime(ast, createMockProvider());
311
+ await runtime.run();
312
+
313
+ // Verify model has the tools attached
314
+ const model = runtime.getValue('m') as { tools?: unknown[] };
315
+ expect(model.tools).toHaveLength(2);
316
+ expect((model.tools![0] as { name: string }).name).toBe('readFile');
317
+ expect((model.tools![1] as { name: string }).name).toBe('writeFile');
318
+ });
319
+
320
+ test('model can have tools array with allTools', async () => {
321
+ const ast = parse(`
322
+ import { allTools } from "system/tools"
323
+
324
+ model m = {
325
+ name: "gpt-4",
326
+ apiKey: "test-key",
327
+ tools: allTools
328
+ }
329
+
330
+ let x = 1
331
+ `);
332
+ const runtime = new Runtime(ast, createMockProvider());
333
+ await runtime.run();
334
+
335
+ // Verify model has all 19 tools
336
+ const model = runtime.getValue('m') as { tools?: unknown[] };
337
+ expect(model.tools).toHaveLength(19);
338
+ });
339
+
340
+ test('model without tools parameter has undefined tools', async () => {
341
+ const ast = parse(`
342
+ model m = {
343
+ name: "gpt-4",
344
+ apiKey: "test-key"
345
+ }
346
+
347
+ let x = 1
348
+ `);
349
+ const runtime = new Runtime(ast, createMockProvider());
350
+ await runtime.run();
351
+
352
+ const model = runtime.getValue('m') as { tools?: unknown[] };
353
+ expect(model.tools).toBeUndefined();
354
+ });
355
+ });
356
+
357
+ describe('Runtime - Tool Bundles', () => {
358
+ test('allTools contains all 19 tools including bash and runCode', async () => {
359
+ const ast = parse(`
360
+ import { allTools } from "system/tools"
361
+ let count = ts(allTools) { return allTools.length }
362
+ let names = ts(allTools) { return allTools.map(t => t.name).sort() }
363
+ `);
364
+ const runtime = new Runtime(ast, createMockProvider());
365
+ await runtime.run();
366
+
367
+ expect(runtime.getValue('count')).toBe(19);
368
+ const names = runtime.getValue('names') as string[];
369
+ expect(names).toContain('bash');
370
+ expect(names).toContain('runCode');
371
+ expect(names).toContain('readFile');
372
+ expect(names).toContain('writeFile');
373
+ });
374
+
375
+ test('readonlyTools excludes write operations and system tools', async () => {
376
+ const ast = parse(`
377
+ import { readonlyTools } from "system/tools"
378
+ let count = ts(readonlyTools) { return readonlyTools.length }
379
+ let names = ts(readonlyTools) { return readonlyTools.map(t => t.name).sort() }
380
+ `);
381
+ const runtime = new Runtime(ast, createMockProvider());
382
+ await runtime.run();
383
+
384
+ expect(runtime.getValue('count')).toBe(12);
385
+ const names = runtime.getValue('names') as string[];
386
+ // Should include read-only tools
387
+ expect(names).toContain('readFile');
388
+ expect(names).toContain('fileExists');
389
+ expect(names).toContain('listDir');
390
+ expect(names).toContain('glob');
391
+ expect(names).toContain('grep');
392
+ expect(names).toContain('dirExists');
393
+ // Should NOT include write/system tools
394
+ expect(names).not.toContain('writeFile');
395
+ expect(names).not.toContain('appendFile');
396
+ expect(names).not.toContain('edit');
397
+ expect(names).not.toContain('fastEdit');
398
+ expect(names).not.toContain('mkdir');
399
+ expect(names).not.toContain('bash');
400
+ expect(names).not.toContain('runCode');
401
+ });
402
+
403
+ test('safeTools excludes bash and runCode', async () => {
404
+ const ast = parse(`
405
+ import { safeTools } from "system/tools"
406
+ let count = ts(safeTools) { return safeTools.length }
407
+ let names = ts(safeTools) { return safeTools.map(t => t.name).sort() }
408
+ `);
409
+ const runtime = new Runtime(ast, createMockProvider());
410
+ await runtime.run();
411
+
412
+ expect(runtime.getValue('count')).toBe(17);
413
+ const names = runtime.getValue('names') as string[];
414
+ // Should NOT include bash or runCode
415
+ expect(names).not.toContain('bash');
416
+ expect(names).not.toContain('runCode');
417
+ // Should include all file tools
418
+ expect(names).toContain('readFile');
419
+ expect(names).toContain('writeFile');
420
+ expect(names).toContain('edit');
421
+ });
422
+
423
+ test('individual tools can be imported alongside bundles', async () => {
424
+ const ast = parse(`
425
+ import { readonlyTools, bash, runCode } from "system/tools"
426
+ let readCount = ts(readonlyTools) { return readonlyTools.length }
427
+ let hasBash = ts(bash) { return bash.name === "bash" }
428
+ let hasRunCode = ts(runCode) { return runCode.name === "runCode" }
429
+ `);
430
+ const runtime = new Runtime(ast, createMockProvider());
431
+ await runtime.run();
432
+
433
+ expect(runtime.getValue('readCount')).toBe(12);
434
+ expect(runtime.getValue('hasBash')).toBe(true);
435
+ expect(runtime.getValue('hasRunCode')).toBe(true);
436
+ });
437
+
438
+ test('tool bundles can be concatenated with + operator', async () => {
439
+ const ast = parse(`
440
+ import { readonlyTools, bash } from "system/tools"
441
+ let combined = readonlyTools + [bash]
442
+ let count = ts(combined) { return combined.length }
443
+ let names = ts(combined) { return combined.map(t => t.name) }
444
+ `);
445
+ const runtime = new Runtime(ast, createMockProvider());
446
+ await runtime.run();
447
+
448
+ // readonlyTools (12) + [bash] (1) = 13
449
+ expect(runtime.getValue('count')).toBe(13);
450
+ const names = runtime.getValue('names') as string[];
451
+ expect(names).toContain('readFile');
452
+ expect(names).toContain('bash');
453
+ });
454
+
455
+ test('concatenate multiple tool bundles', async () => {
456
+ const ast = parse(`
457
+ import { readonlyTools, bash, runCode } from "system/tools"
458
+ let custom = readonlyTools + [bash] + [runCode]
459
+ let count = ts(custom) { return custom.length }
460
+ `);
461
+ const runtime = new Runtime(ast, createMockProvider());
462
+ await runtime.run();
463
+
464
+ // readonlyTools (12) + bash (1) + runCode (1) = 14
465
+ expect(runtime.getValue('count')).toBe(14);
466
+ });
467
+ });