@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
package/src/parser.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Re-export from new location for backwards compatibility
2
+ export * from './parser/index';
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect } from 'bun:test';
2
+ import { chunkContextForCaching } from './cache-chunking';
3
+
4
+ describe('chunkContextForCaching', () => {
5
+ it('returns empty chunks for empty context', () => {
6
+ const result = chunkContextForCaching('');
7
+ expect(result.chunks).toEqual([]);
8
+ expect(result.cacheBreakpointIndex).toBe(-1);
9
+ });
10
+
11
+ it('returns empty chunks for whitespace-only context', () => {
12
+ const result = chunkContextForCaching(' \n\n ');
13
+ expect(result.chunks).toEqual([]);
14
+ expect(result.cacheBreakpointIndex).toBe(-1);
15
+ });
16
+
17
+ it('creates single chunk for small context', () => {
18
+ const context = '- name: Alice\n- age: 30';
19
+ const result = chunkContextForCaching(context);
20
+ expect(result.chunks.length).toBe(1);
21
+ expect(result.chunks[0].content).toBe(context);
22
+ expect(result.cacheBreakpointIndex).toBe(-1); // No 2nd-to-last with single chunk
23
+ });
24
+
25
+ it('creates multiple chunks when context exceeds threshold', () => {
26
+ // Create context that exceeds 5000 tokens (~20000 chars)
27
+ const largeLine = '- data: ' + 'x'.repeat(10000); // ~2500 tokens
28
+ const context = [largeLine, largeLine, largeLine].join('\n'); // ~7500 tokens total
29
+
30
+ const result = chunkContextForCaching(context);
31
+ expect(result.chunks.length).toBeGreaterThan(1);
32
+ });
33
+
34
+ it('sets cache breakpoint on 2nd-to-last chunk', () => {
35
+ // Create context that will produce exactly 2 chunks
36
+ const largeLine = '- data: ' + 'x'.repeat(10000);
37
+ const context = [largeLine, largeLine].join('\n');
38
+
39
+ const result = chunkContextForCaching(context);
40
+ expect(result.chunks.length).toBe(2);
41
+ expect(result.cacheBreakpointIndex).toBe(0); // 2nd-to-last of 2 = index 0
42
+ });
43
+
44
+ it('sets cache breakpoint correctly for 3 chunks', () => {
45
+ // Create context that will produce 3 chunks
46
+ const largeLine = '- data: ' + 'x'.repeat(10000);
47
+ const context = [largeLine, largeLine, largeLine].join('\n');
48
+
49
+ const result = chunkContextForCaching(context);
50
+ expect(result.chunks.length).toBe(3);
51
+ expect(result.cacheBreakpointIndex).toBe(1); // 2nd-to-last of 3 = index 1
52
+ });
53
+
54
+ it('preserves line content in chunks', () => {
55
+ const lines = ['- a: 1', '- b: 2', '- c: 3'];
56
+ const context = lines.join('\n');
57
+
58
+ const result = chunkContextForCaching(context);
59
+ expect(result.chunks.length).toBe(1);
60
+ expect(result.chunks[0].content).toBe(context);
61
+ });
62
+
63
+ it('estimates token count approximately', () => {
64
+ // 400 chars should be ~100 tokens (4 chars/token)
65
+ const context = 'x'.repeat(400);
66
+ const result = chunkContextForCaching(context);
67
+ expect(result.chunks[0].tokenEstimate).toBe(100);
68
+ });
69
+ });
@@ -0,0 +1,73 @@
1
+ // Anthropic prompt caching utilities
2
+ // Splits context into chunks for progressive caching
3
+
4
+ const TARGET_CHUNK_TOKENS = 5000;
5
+ const CHARS_PER_TOKEN = 4; // Rough estimate for token counting
6
+
7
+ /** A chunk of context content with estimated token count */
8
+ export interface CacheChunk {
9
+ content: string;
10
+ tokenEstimate: number;
11
+ }
12
+
13
+ /** Chunked context ready for Anthropic API with cache breakpoint info */
14
+ export interface ChunkedContext {
15
+ chunks: CacheChunk[];
16
+ cacheBreakpointIndex: number; // Which chunk gets cache_control (-1 if none)
17
+ }
18
+
19
+ /**
20
+ * Estimate token count for a string using character-based heuristic.
21
+ */
22
+ function estimateTokens(text: string): number {
23
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
24
+ }
25
+
26
+ /**
27
+ * Split formatted context text into chunks of ~5000 tokens each.
28
+ * Splits by line boundaries (each context entry is on its own line).
29
+ * Accumulates lines until hitting threshold, then starts new chunk.
30
+ * Cache breakpoint is placed on 2nd-to-last chunk to allow latest chunk to change.
31
+ */
32
+ export function chunkContextForCaching(contextText: string): ChunkedContext {
33
+ const trimmed = contextText.trim();
34
+ if (!trimmed) {
35
+ return { chunks: [], cacheBreakpointIndex: -1 };
36
+ }
37
+
38
+ const lines = trimmed.split('\n');
39
+ const chunks: CacheChunk[] = [];
40
+ let currentLines: string[] = [];
41
+ let currentTokens = 0;
42
+
43
+ for (const line of lines) {
44
+ const lineTokens = estimateTokens(line);
45
+
46
+ // If adding this line exceeds threshold and we have content, start new chunk
47
+ if (currentTokens + lineTokens > TARGET_CHUNK_TOKENS && currentLines.length > 0) {
48
+ chunks.push({
49
+ content: currentLines.join('\n'),
50
+ tokenEstimate: currentTokens,
51
+ });
52
+ currentLines = [];
53
+ currentTokens = 0;
54
+ }
55
+
56
+ currentLines.push(line);
57
+ currentTokens += lineTokens;
58
+ }
59
+
60
+ // Push final chunk if we have content
61
+ if (currentLines.length > 0) {
62
+ chunks.push({
63
+ content: currentLines.join('\n'),
64
+ tokenEstimate: currentTokens,
65
+ });
66
+ }
67
+
68
+ // Cache breakpoint on 2nd-to-last chunk (or -1 if < 2 chunks)
69
+ // This allows the last chunk to change without invalidating the cache
70
+ const cacheBreakpointIndex = chunks.length >= 2 ? chunks.length - 2 : -1;
71
+
72
+ return { chunks, cacheBreakpointIndex };
73
+ }
@@ -0,0 +1,109 @@
1
+ // AI Client - Main orchestration for AI API calls
2
+
3
+ import type { AIProviderType, AIRequest, AIResponse, TargetType, ModelConfig, ProviderExecutor } from './types';
4
+ import type { VibeToolValue } from '../tools/types';
5
+ import { withRetry } from './retry';
6
+ import { executeOpenAI } from './providers/openai';
7
+ import { executeAnthropic } from './providers/anthropic';
8
+ import { executeGoogle } from './providers/google';
9
+
10
+ /**
11
+ * Detect provider from URL.
12
+ * Returns 'openai' as default for unknown/custom endpoints.
13
+ */
14
+ export function detectProvider(url: string | null): AIProviderType {
15
+ if (!url) return 'openai';
16
+
17
+ const u = url.toLowerCase();
18
+ if (u.includes('anthropic')) return 'anthropic';
19
+ if (u.includes('google') || u.includes('generativelanguage')) return 'google';
20
+ return 'openai';
21
+ }
22
+
23
+ /**
24
+ * Get the provider executor function for a given provider type.
25
+ */
26
+ export function getProviderExecutor(provider: AIProviderType): ProviderExecutor {
27
+ switch (provider) {
28
+ case 'anthropic':
29
+ return executeAnthropic;
30
+ case 'google':
31
+ return executeGoogle;
32
+ case 'openai':
33
+ default:
34
+ return executeOpenAI;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Build an AI request from the given parameters.
40
+ */
41
+ export function buildAIRequest(
42
+ model: ModelConfig,
43
+ prompt: string,
44
+ contextText: string,
45
+ operationType: 'do' | 'vibe',
46
+ targetType: TargetType
47
+ ): AIRequest {
48
+ return {
49
+ operationType,
50
+ prompt,
51
+ contextText,
52
+ targetType,
53
+ model,
54
+ };
55
+ }
56
+
57
+ /** Model value as stored in runtime locals */
58
+ export interface VibeModelValue {
59
+ __vibeModel: true;
60
+ name: string | null;
61
+ apiKey: string | null;
62
+ url: string | null;
63
+ provider?: AIProviderType | null;
64
+ maxRetriesOnError?: number | null;
65
+ tools?: VibeToolValue[]; // Tools available to this model for AI calls
66
+ }
67
+
68
+ /**
69
+ * Execute an AI request with the appropriate provider.
70
+ * Handles provider detection, request building, and retry logic.
71
+ */
72
+ export async function executeAI(
73
+ modelValue: VibeModelValue,
74
+ prompt: string,
75
+ contextText: string,
76
+ operationType: 'do' | 'vibe',
77
+ targetType: TargetType
78
+ ): Promise<AIResponse> {
79
+ // Validate model config
80
+ if (!modelValue.name) {
81
+ throw new Error('Model name is required');
82
+ }
83
+ if (!modelValue.apiKey) {
84
+ throw new Error('API key is required');
85
+ }
86
+
87
+ // Determine provider (explicit or auto-detect)
88
+ const provider: AIProviderType =
89
+ (modelValue.provider as AIProviderType) ?? detectProvider(modelValue.url);
90
+
91
+ // Build model config
92
+ const model: ModelConfig = {
93
+ name: modelValue.name,
94
+ apiKey: modelValue.apiKey,
95
+ url: modelValue.url,
96
+ provider,
97
+ maxRetriesOnError: modelValue.maxRetriesOnError ?? undefined,
98
+ };
99
+
100
+ // Build request
101
+ const request = buildAIRequest(model, prompt, contextText, operationType, targetType);
102
+
103
+ // Get provider executor
104
+ const execute = getProviderExecutor(provider);
105
+
106
+ // Execute with retry logic
107
+ const maxRetries = modelValue.maxRetriesOnError ?? 3;
108
+ return withRetry(() => execute(request), { maxRetries });
109
+ }
@@ -0,0 +1,168 @@
1
+ // Unified AI Context System
2
+ // Single source of truth for what gets sent to AI models and logged
3
+ //
4
+ // Architecture:
5
+ // buildAIContext(state) → AIContext (structured)
6
+ // ↓ ↓
7
+ // formatForProvider() formatForLog()
8
+ // ↓ ↓
9
+ // Provider API call Human-readable log
10
+
11
+ import type { RuntimeState, ContextEntry } from '../types';
12
+ import type { ToolSchema } from '../tools/types';
13
+ import type { TargetType, ModelConfig } from './types';
14
+ import { buildGlobalContext, formatContextForAI } from '../context';
15
+ // Import formatters for consistent message building
16
+ import {
17
+ buildSystemMessage as buildSystemMessageImpl,
18
+ buildToolSystemMessage as buildToolSystemMessageImpl,
19
+ buildPromptMessage as buildPromptMessageImpl,
20
+ } from './formatters';
21
+
22
+ /**
23
+ * Message in the AI conversation.
24
+ * Represents a single message sent to or received from the model.
25
+ */
26
+ export interface AIMessage {
27
+ role: 'system' | 'user' | 'assistant';
28
+ content: string;
29
+ /** For assistant messages with tool calls */
30
+ toolCalls?: Array<{
31
+ id: string;
32
+ toolName: string;
33
+ args: Record<string, unknown>;
34
+ }>;
35
+ /** For user messages with tool results */
36
+ toolResults?: Array<{
37
+ toolCallId: string;
38
+ result?: unknown;
39
+ error?: string;
40
+ }>;
41
+ }
42
+
43
+ /**
44
+ * Complete AI context for a single interaction.
45
+ * This is the single source of truth for:
46
+ * - What gets sent to the AI model
47
+ * - What gets logged for debugging
48
+ */
49
+ export interface AIContext {
50
+ /** Operation type */
51
+ operationType: 'do' | 'vibe';
52
+
53
+ /** Model being used */
54
+ model: ModelConfig;
55
+
56
+ /** Target type for response parsing */
57
+ targetType: TargetType;
58
+
59
+ /** Available tools (if any) */
60
+ tools?: ToolSchema[];
61
+
62
+ /** The complete message sequence to send to the model */
63
+ messages: AIMessage[];
64
+
65
+ /** Structured execution context (for detailed logging) */
66
+ executionContext: ContextEntry[];
67
+ }
68
+
69
+ /**
70
+ * Build the complete AI context from runtime state.
71
+ * This is the SINGLE SOURCE OF TRUTH for what gets sent to the model.
72
+ */
73
+ export function buildAIContext(
74
+ state: RuntimeState,
75
+ model: ModelConfig,
76
+ prompt: string,
77
+ targetType: TargetType,
78
+ tools?: ToolSchema[],
79
+ previousToolCalls?: Array<{ id: string; toolName: string; args: Record<string, unknown> }>,
80
+ toolResults?: Array<{ toolCallId: string; result?: unknown; error?: string }>
81
+ ): AIContext {
82
+ const operationType = state.pendingAI?.type ?? 'do';
83
+
84
+ // Build structured execution context from state
85
+ const executionContext = buildGlobalContext(state);
86
+ const formattedContext = formatContextForAI(executionContext);
87
+
88
+ // Build the message sequence
89
+ const messages: AIMessage[] = [];
90
+
91
+ // 1. System message
92
+ messages.push({
93
+ role: 'system',
94
+ content: buildSystemMessage(),
95
+ });
96
+
97
+ // 2. Tool system message (if tools available)
98
+ if (tools && tools.length > 0) {
99
+ messages.push({
100
+ role: 'system',
101
+ content: buildToolSystemMessage(tools),
102
+ });
103
+ }
104
+
105
+ // 3. Context message (if there's execution context)
106
+ if (formattedContext.text) {
107
+ messages.push({
108
+ role: 'user',
109
+ content: `Here is the current program context:\n\n${formattedContext.text}`,
110
+ });
111
+ }
112
+
113
+ // 4. Current prompt
114
+ messages.push({
115
+ role: 'user',
116
+ content: buildPromptMessage(prompt, targetType),
117
+ });
118
+
119
+ // 5. Previous tool calls and results (for multi-turn)
120
+ if (previousToolCalls?.length && toolResults?.length) {
121
+ messages.push({
122
+ role: 'assistant',
123
+ content: '', // Content comes from tool calls
124
+ toolCalls: previousToolCalls,
125
+ });
126
+ messages.push({
127
+ role: 'user',
128
+ content: '', // Content comes from tool results
129
+ toolResults: toolResults,
130
+ });
131
+ }
132
+
133
+ return {
134
+ operationType,
135
+ model,
136
+ targetType,
137
+ tools,
138
+ messages,
139
+ executionContext,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Build the base system message.
145
+ * Delegates to formatters.ts for consistent formatting.
146
+ */
147
+ function buildSystemMessage(): string {
148
+ return buildSystemMessageImpl();
149
+ }
150
+
151
+ /**
152
+ * Build the tool system message describing available tools.
153
+ * Delegates to formatters.ts for consistent formatting.
154
+ */
155
+ function buildToolSystemMessage(tools: ToolSchema[]): string {
156
+ return buildToolSystemMessageImpl(tools) ?? '';
157
+ }
158
+
159
+ /**
160
+ * Build the prompt message with type instructions if needed.
161
+ * Delegates to formatters.ts for consistent formatting.
162
+ */
163
+ function buildPromptMessage(prompt: string, _targetType: TargetType): string {
164
+ return buildPromptMessageImpl(prompt);
165
+ }
166
+
167
+ // Re-export formatters for backward compatibility
168
+ export { buildSystemMessage, buildToolSystemMessage, buildPromptMessage };