tjs-lang 0.2.8 → 0.3.0

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/demo/docs.json CHANGED
@@ -173,7 +173,7 @@
173
173
  "type": "example",
174
174
  "group": "fullstack",
175
175
  "order": 14,
176
- "code": "/**\n * # Todo API Service\n *\n * A REST-style API for todo management.\n * Demonstrates a more complete service pattern.\n */\n\n// Simulated persistence layer\nconst todos = new Map()\nlet nextId = 1\n\n// Types\ntype Todo = { id: number, title: string, completed: boolean, createdAt: string }\ntype CreateInput = { title: 'Buy milk' }\ntype UpdateInput = { id: 1, title: 'Buy milk', completed: false }\ntype FilterInput = { completed: true } | { completed: false } | {}\n\n// POST /todos - Create\nexport function createTodo(input: { title: 'New todo' })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n const todo = {\n id: nextId++,\n title: input.title,\n completed: false,\n createdAt: new Date().toISOString()\n }\n todos.set(todo.id, todo)\n return todo\n}\n\n// GET /todos/:id - Read one (returns empty if not found)\nexport function getTodo(input: { id: 1 })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n return todos.get(input.id) || { id: 0, title: '', completed: false, createdAt: '' }\n}\n\n// GET /todos - Read all (with optional filter)\nexport function listTodos(input: { completed: true } | {})\n -> { todos: [{ id: 0, title: '', completed: false, createdAt: '' }] } {\n let items = [...todos.values()]\n\n if ('completed' in input) {\n items = items.filter(t => t.completed === input.completed)\n }\n\n return { todos: items }\n}\n\n// PUT /todos/:id - Update (returns empty if not found)\nexport function updateTodo(input: { id: 1, title: '', completed: false })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n const existing = todos.get(input.id)\n if (!existing) return { id: 0, title: '', completed: false, createdAt: '' }\n\n const updated = {\n ...existing,\n title: input.title ?? existing.title,\n completed: input.completed ?? existing.completed\n }\n todos.set(input.id, updated)\n return updated\n}\n\n// DELETE /todos/:id - Delete\nexport function deleteTodo(input: { id: 1 }) -> { deleted: true } {\n const existed = todos.has(input.id)\n todos.delete(input.id)\n return { deleted: existed }\n}\n\n// PATCH /todos/:id/toggle - Toggle completion (returns empty if not found)\nexport function toggleTodo(input: { id: 1 })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n const todo = todos.get(input.id)\n if (!todo) return { id: 0, title: '', completed: false, createdAt: '' }\n\n todo.completed = !todo.completed\n return todo\n}\n\n// DELETE /todos/completed - Clear completed\nexport function clearCompleted(input: {}) -> { cleared: 0 } {\n let cleared = 0\n for (const [id, todo] of todos) {\n if (todo.completed) {\n todos.delete(id)\n cleared++\n }\n }\n return { cleared }\n}\n\n// Tests\ntest('CRUD operations work') {\n const todo = createTodo({ title: 'Test todo' })\n expect(todo.id).toBeGreaterThan(0)\n expect(todo.completed).toBe(false)\n\n const fetched = getTodo({ id: todo.id })\n expect(fetched?.title).toBe('Test todo')\n\n const toggled = toggleTodo({ id: todo.id })\n expect(toggled?.completed).toBe(true)\n\n const deleted = deleteTodo({ id: todo.id })\n expect(deleted.deleted).toBe(true)\n}\n\n// Demo\nconsole.log('=== Todo API Demo ===\\\\n')\n\n// Create todos\ncreateTodo({ title: 'Learn TJS' })\ncreateTodo({ title: 'Build something cool' })\ncreateTodo({ title: 'Ship it' })\n\nconsole.log('Created 3 todos')\nconsole.log('All:', listTodos({}))\n\n// Complete first one\nconst first = listTodos({}).todos[0]\ntoggleTodo({ id: first.id })\nconsole.log('\\\\nToggled first todo')\nconsole.log('Completed:', listTodos({ completed: true }))\nconsole.log('Pending:', listTodos({ completed: false }))\n\n// Clear completed\nconsole.log('\\\\nClearing completed...')\nconsole.log(clearCompleted({}))\nconsole.log('Remaining:', listTodos({}))",
176
+ "code": "/**\n * # Todo API Service\n *\n * A REST-style API for todo management.\n * Demonstrates a more complete service pattern.\n */\n\n// Simulated persistence layer\nconst todos = new Map()\nlet nextId = 1\n\n// Types are inferred from function signatures below\n// Todo: { id: 0, title: '', completed: false, createdAt: '' }\n// CreateInput: { title: 'Buy milk' }\n// UpdateInput: { id: 1, title: 'Buy milk', completed: false }\n\n// POST /todos - Create\nexport function createTodo(input: { title: 'New todo' })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n const todo = {\n id: nextId++,\n title: input.title,\n completed: false,\n createdAt: new Date().toISOString()\n }\n todos.set(todo.id, todo)\n return todo\n}\n\n// GET /todos/:id - Read one (returns empty if not found)\nexport function getTodo(input: { id: 1 })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n return todos.get(input.id) || { id: 0, title: '', completed: false, createdAt: '' }\n}\n\n// GET /todos - Read all (with optional filter)\nexport function listTodos(input: { completed: true } | {})\n -> { todos: [{ id: 0, title: '', completed: false, createdAt: '' }] } {\n let items = [...todos.values()]\n\n if ('completed' in input) {\n items = items.filter(t => t.completed === input.completed)\n }\n\n return { todos: items }\n}\n\n// PUT /todos/:id - Update (returns empty if not found)\nexport function updateTodo(input: { id: 1, title: '', completed: false })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n const existing = todos.get(input.id)\n if (!existing) return { id: 0, title: '', completed: false, createdAt: '' }\n\n const updated = {\n ...existing,\n title: input.title ?? existing.title,\n completed: input.completed ?? existing.completed\n }\n todos.set(input.id, updated)\n return updated\n}\n\n// DELETE /todos/:id - Delete\nexport function deleteTodo(input: { id: 1 }) -> { deleted: true } {\n const existed = todos.has(input.id)\n todos.delete(input.id)\n return { deleted: existed }\n}\n\n// PATCH /todos/:id/toggle - Toggle completion (returns empty if not found)\nexport function toggleTodo(input: { id: 1 })\n -> { id: 0, title: '', completed: false, createdAt: '' } {\n const todo = todos.get(input.id)\n if (!todo) return { id: 0, title: '', completed: false, createdAt: '' }\n\n todo.completed = !todo.completed\n return todo\n}\n\n// DELETE /todos/completed - Clear completed\nexport function clearCompleted(input: {}) -> { cleared: 0 } {\n let cleared = 0\n for (const [id, todo] of todos) {\n if (todo.completed) {\n todos.delete(id)\n cleared++\n }\n }\n return { cleared }\n}\n\n// Tests\ntest('CRUD operations work') {\n const todo = createTodo({ title: 'Test todo' })\n expect(todo.id).toBeGreaterThan(0)\n expect(todo.completed).toBe(false)\n\n const fetched = getTodo({ id: todo.id })\n expect(fetched?.title).toBe('Test todo')\n\n const toggled = toggleTodo({ id: todo.id })\n expect(toggled?.completed).toBe(true)\n\n const deleted = deleteTodo({ id: todo.id })\n expect(deleted.deleted).toBe(true)\n}\n\n// Demo\nconsole.log('=== Todo API Demo ===\\\\n')\n\n// Create todos\ncreateTodo({ title: 'Learn TJS' })\ncreateTodo({ title: 'Build something cool' })\ncreateTodo({ title: 'Ship it' })\n\nconsole.log('Created 3 todos')\nconsole.log('All:', listTodos({}))\n\n// Complete first one\nconst first = listTodos({}).todos[0]\ntoggleTodo({ id: first.id })\nconsole.log('\\\\nToggled first todo')\nconsole.log('Completed:', listTodos({ completed: true }))\nconsole.log('Pending:', listTodos({ completed: false }))\n\n// Clear completed\nconsole.log('\\\\nClearing completed...')\nconsole.log(clearCompleted({}))\nconsole.log('Remaining:', listTodos({}))",
177
177
  "language": "tjs",
178
178
  "description": "Complete REST-style Todo API with persistence"
179
179
  },
@@ -233,7 +233,7 @@
233
233
  "type": "example",
234
234
  "group": "patterns",
235
235
  "order": 9,
236
- "code": "/**\n * # Date Formatting with Imports\n *\n * This example demonstrates importing an external ESM module\n * (date-fns) and using it with TJS type safety.\n */\n\nimport { format, formatDistance, addDays, parseISO } from 'date-fns'\n\n// Format a date with various patterns\nfunction formatDate(date: '2024-01-15', pattern: 'yyyy-MM-dd') -> '' {\n const parsed = parseISO(date)\n return format(parsed, pattern)\n}\n\n// Get human-readable relative time\nfunction timeAgo(date: '2024-01-15') -> '' {\n const parsed = parseISO(date)\n return formatDistance(parsed, new Date(), { addSuffix: true })\n}\n\n// Add days to a date\nfunction addWorkdays(date: '2024-01-15', days: 5) -> '' {\n const parsed = parseISO(date)\n const result = addDays(parsed, days)\n return format(result, 'yyyy-MM-dd')\n}\n\n// Complex date operation with validation\nfunction createEvent(input: {\n title: 'Meeting',\n startDate: '2024-01-15',\n durationDays: 1\n}) -> { title: '', start: '', end: '', formatted: '' } {\n const start = parseISO(input.startDate)\n const end = addDays(start, input.durationDays)\n\n return {\n title: input.title,\n start: format(start, 'yyyy-MM-dd'),\n end: format(end, 'yyyy-MM-dd'),\n formatted: `\\${input.title}: \\${format(start, 'MMM d')} - \\${format(end, 'MMM d, yyyy')}\\",
236
+ "code": "/**\n * # Date Formatting with Imports\n *\n * This example demonstrates importing an external ESM module\n * (date-fns) and using it with TJS type safety.\n */\n\nimport { format, formatDistance, addDays, parseISO } from 'date-fns'\n\n// Format a date with various patterns\nfunction formatDate(date: '2024-01-15', pattern: 'yyyy-MM-dd') -> '' {\n const parsed = parseISO(date)\n return format(parsed, pattern)\n}\n\n// Get human-readable relative time\nfunction timeAgo(date: '2024-01-15') -> '' {\n const parsed = parseISO(date)\n return formatDistance(parsed, new Date(), { addSuffix: true })\n}\n\n// Add days to a date\nfunction addWorkdays(date: '2024-01-15', days: 5) -> '' {\n const parsed = parseISO(date)\n const result = addDays(parsed, days)\n return format(result, 'yyyy-MM-dd')\n}\n\n// Complex date operation with validation\nfunction createEvent(input: {\n title: 'Meeting',\n startDate: '2024-01-15',\n durationDays: 1\n}) -> { title: '', start: '', end: '', formatted: '' } {\n const start = parseISO(input.startDate)\n const end = addDays(start, input.durationDays)\n\n return {\n title: input.title,\n start: format(start, 'yyyy-MM-dd'),\n end: format(end, 'yyyy-MM-dd'),\n formatted: `${input.title}: ${format(start, 'MMM d')} - ${format(end, 'MMM d, yyyy')}`\n }\n}",
237
237
  "language": "tjs",
238
238
  "description": "Uses date-fns for date formatting via ESM import"
239
239
  },
@@ -245,7 +245,7 @@
245
245
  "type": "example",
246
246
  "group": "patterns",
247
247
  "order": 10,
248
- "code": "/*#\n# Local Module Imports\n\nYou can import from modules saved in the playground!\n\n## How it works:\n1. Save a module (use the Save button, give it a name like \"math\")\n2. Import it by name from another file\n\n## Try it:\n1. First, create and save a module named \"mymath\":",
248
+ "code": "/*#\n# Local Module Imports\n\nYou can import from modules saved in the playground!\n\n## How it works:\n1. Save a module (use the Save button, give it a name like \"math\")\n2. Import it by name from another file\n\n## Try it:\n1. First, create and save a module named \"mymath\":\n\n export function add(a: 0, b: 0) -> 0 {\n return a + b\n }\n\n export function multiply(a: 0, b: 0) -> 0 {\n return a * b\n }\n\n2. Then run this code (it imports from your saved module)\n*/\n\n// This imports from a module you saved in the playground\n// Change 'mymath' to match whatever name you used when saving\nimport { add, multiply } from 'mymath'\n\nfunction calculate(x: 0, y: 0) -> 0 {\n // (x + y) * 2\n return multiply(add(x, y), 2)\n}\n\ntest 'calculate combines add and multiply' {\n expect(calculate(3, 4)).toBe(14) // (3 + 4) * 2 = 14\n}\n\nconsole.log('calculate(3, 4) =', calculate(3, 4))\nconsole.log('calculate(10, 5) =', calculate(10, 5))",
249
249
  "language": "tjs",
250
250
  "description": "Import from modules you save in the playground"
251
251
  },
@@ -294,7 +294,7 @@
294
294
  "group": "advanced",
295
295
  "order": 14,
296
296
  "requiresApi": true,
297
- "code": "function mathAssistant({ question = 'What is 23 * 47 + 156?' }) {\n // First, ask LLM to extract the calculation\n let extractPrompt = \\`Extract the math expression from this question. Return ONLY the expression, nothing else.\nQuestion: \\${question}\\`\n let expression = llmPredict({ prompt: extractPrompt })\n\n // Evaluate the expression (simple eval simulation)\n let calcPrompt = \\`Calculate: \\${expression}\nReturn ONLY the numeric result.\\`\n let calcResult = llmPredict({ prompt: calcPrompt })\n\n // Format the answer\n let answerPrompt = \\`The user asked: \"\\${question}\"\nThe calculated result is: \\${calcResult}\nWrite a brief, friendly response with the answer.\\`\n let answer = llmPredict({ prompt: answerPrompt })\n\n return { question, expression: expression.trim(), result: calcResult.trim(), answer }\n}",
297
+ "code": "function mathAssistant({ question = 'What is 23 * 47 + 156?' }) {\n // First, ask LLM to extract the calculation\n let extractPrompt =\n 'Extract the math expression from this question. Return ONLY the expression, nothing else.\\nQuestion: ' +\n question\n let expression = llmPredict({ prompt: extractPrompt })\n\n // Evaluate the expression (simple eval simulation)\n let calcPrompt =\n 'Calculate: ' + expression + '\\nReturn ONLY the numeric result.'\n let calcResult = llmPredict({ prompt: calcPrompt })\n\n // Format the answer\n let answerPrompt =\n 'The user asked: \"' +\n question +\n '\"\\nThe calculated result is: ' +\n calcResult +\n '\\nWrite a brief, friendly response with the answer.'\n let answer = llmPredict({ prompt: answerPrompt })\n\n return {\n question,\n expression: expression.trim(),\n result: calcResult.trim(),\n answer,\n }\n}",
298
298
  "language": "javascript",
299
299
  "description": "LLM uses a calculator tool (requires llm capability)"
300
300
  },
@@ -307,7 +307,7 @@
307
307
  "group": "advanced",
308
308
  "order": 15,
309
309
  "requiresApi": true,
310
- "code": "function collaborativeWriting({ topic = 'the future of renewable energy' }) {\n // Agent 1: Research Agent - generates key points\n let researchPrompt = \\`You are a research agent. Generate 3 key facts or points about: \\${topic}\nFormat as a numbered list. Be concise.\\`\n let research = llmPredict({ prompt: researchPrompt })\n\n // Agent 2: Writer Agent - creates content from research\n let writerPrompt = \\`You are a writer agent. Using these research points:\n\n\\${research}\n\nWrite a short, engaging paragraph (2-3 sentences) about \\${topic}.\nMake it informative and accessible.\\`\n let article = llmPredict({ prompt: writerPrompt })\n\n // Agent 3: Editor Agent - reviews and improves\n let editorPrompt = \\`You are an editor agent. Review this draft:\n\n\"\\${article}\"\n\nSuggest one specific improvement. Then provide the improved version.\nFormat: \"Suggestion: [your suggestion]\\\\n\\\\nImproved: [improved text]\"\\`\n let edited = llmPredict({ prompt: editorPrompt })\n\n return {\n topic,\n researchPoints: research,\n firstDraft: article,\n editedVersion: edited\n }\n}",
310
+ "code": "function collaborativeWriting({ topic = 'the future of renewable energy' }) {\n // Agent 1: Research Agent - generates key points\n let researchPrompt =\n 'You are a research agent. Generate 3 key facts or points about: ' +\n topic +\n '\\nFormat as a numbered list. Be concise.'\n let research = llmPredict({ prompt: researchPrompt })\n\n // Agent 2: Writer Agent - creates content from research\n let writerPrompt =\n 'You are a writer agent. Using these research points:\\n\\n' +\n research +\n '\\n\\nWrite a short, engaging paragraph (2-3 sentences) about ' +\n topic +\n '.\\nMake it informative and accessible.'\n let article = llmPredict({ prompt: writerPrompt })\n\n // Agent 3: Editor Agent - reviews and improves\n let editorPrompt =\n 'You are an editor agent. Review this draft:\\n\\n\"' +\n article +\n '\"\\n\\nSuggest one specific improvement. Then provide the improved version.\\nFormat: \"Suggestion: [your suggestion]\\n\\nImproved: [improved text]\"'\n let edited = llmPredict({ prompt: editorPrompt })\n\n return {\n topic,\n researchPoints: research,\n firstDraft: article,\n editedVersion: edited,\n }\n}",
311
311
  "language": "javascript",
312
312
  "description": "Two agents collaborate on a task (requires llm capability)"
313
313
  },
@@ -346,7 +346,7 @@
346
346
  "group": "advanced",
347
347
  "order": 19,
348
348
  "requiresApi": true,
349
- "code": "function solveWithCode({ problem = 'Calculate the 10th Fibonacci number' }) {\n // System prompt with AsyncJS rules and example\n let systemContext = \\`You write AsyncJS code. AsyncJS is a JavaScript subset.\n\nRULES:\n- NO: async, await, new, class, this, var, for loops\n- Use let for variables, while for loops\n- Return an object: return { result }\n\nEXAMPLE (factorial):\nfunction solve() {\n let result = 1\n let i = 5\n while (i > 1) {\n result = result * i\n i = i - 1\n }\n return { result }\n}\n\nReturn ONLY the function code, nothing else.\\`\n\n let prompt = \\`\\${systemContext}\n\nWrite a function called \"solve\" that: \\${problem}\\`\n\n let response = llmPredict({ prompt })\n\n // Clean up code - remove markdown fences, fix escapes, extract function\n let code = response\n code = code.replace(/\\`\\`\\`(?:javascript|js|asyncjs)?\\\\n?/g, '')\n code = code.replace(/\\\\n?\\`\\`\\`/g, '')\n code = code.replace(/\\\\\\\\n/g, '\\\\n')\n code = code.replace(/\\\\\\\\t/g, '\\\\t')\n code = code.replace(/\\\\\\\\\"/g, '\"')\n code = code.trim()\n\n // Try to extract just the function if there's extra text\n let funcMatch = code.match(/function\\\\s+solve\\\\s*\\\\([^)]*\\\\)\\\\s*\\\\{[\\\\s\\\\S]*\\\\}/)\n if (funcMatch) {\n code = funcMatch[0]\n }\n\n // Validate it looks like a function before running\n if (!code.startsWith('function')) {\n return {\n problem,\n error: 'LLM did not generate valid code',\n rawResponse: response\n }\n }\n\n // Execute the generated code\n let output = runCode({ code, args: {} })\n\n return {\n problem,\n generatedCode: code,\n result: output.result\n }\n}",
349
+ "code": "function solveWithCode({ problem = 'Calculate the 10th Fibonacci number' }) {\n // System prompt with AsyncJS rules and example\n let systemContext =\n 'You write AsyncJS code. AsyncJS is a JavaScript subset.\\n\\nRULES:\\n- NO: async, await, new, class, this, var, for loops\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE (factorial):\\nfunction solve() {\\n let result = 1\\n let i = 5\\n while (i > 1) {\\n result = result * i\\n i = i - 1\\n }\\n return { result }\\n}\\n\\nReturn ONLY the function code, nothing else.'\n\n let prompt =\n systemContext + '\\n\\nWrite a function called \"solve\" that: ' + problem\n\n let response = llmPredict({ prompt })\n\n // Clean up code - remove markdown fences, fix escapes, extract function\n let code = response\n code = code.replace(/",
350
350
  "language": "javascript",
351
351
  "description": "LLM writes and runs code to solve a problem (requires llm capability)"
352
352
  },
@@ -359,7 +359,7 @@
359
359
  "group": "advanced",
360
360
  "order": 20,
361
361
  "requiresApi": true,
362
- "code": "function generateCode({ task = 'Calculate the factorial of n' }) {\n // System prompt with AsyncJS rules and complete example\n let systemContext = \\`You write AsyncJS code. AsyncJS is a subset of JavaScript.\n\nRULES:\n- Types by example: fn(n: 5) means required number param with example value 5\n- NO: async, await, new, class, this, var, for, generator functions (function*)\n- Use let for variables, while for loops\n- Return an object: return { result }\n\nEXAMPLE - calculating sum of 1 to n:\nfunction sumTo(n: 10) {\n let sum = 0\n let i = 1\n while (i <= n) {\n sum = sum + i\n i = i + 1\n }\n return { result: sum }\n}\\`\n\n let schema = Schema.response('generated_code', {\n code: '',\n description: ''\n })\n\n let prompt = \\`\\${systemContext}\n\nWrite an AsyncJS function for: \\${task}\n\nReturn ONLY valid AsyncJS code in the code field. Must start with \"function\" and use while loops (not for loops).\\`\n\n let response = llmPredict({ prompt, options: { responseFormat: schema } })\n let result = JSON.parse(response)\n\n // Clean up any markdown fences and fix escaped newlines\n let code = result.code\n code = code.replace(/\\`\\`\\`(?:javascript|js)?\\\\n?/g, '')\n code = code.replace(/\\\\n?\\`\\`\\`/g, '')\n code = code.replace(/\\\\\\\\n/g, '\\\\n')\n code = code.replace(/\\\\\\\\t/g, '\\\\t')\n code = code.trim()\n\n return {\n task,\n code,\n description: result.description\n }\n}",
362
+ "code": "function generateCode({ task = 'Calculate the factorial of n' }) {\n // System prompt with AsyncJS rules and complete example\n let systemContext =\n 'You write AsyncJS code. AsyncJS is a subset of JavaScript.\\n\\nRULES:\\n- Types by example: fn(n: 5) means required number param with example value 5\\n- NO: async, await, new, class, this, var, for, generator functions (function*)\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE - calculating sum of 1 to n:\\nfunction sumTo(n: 10) {\\n let sum = 0\\n let i = 1\\n while (i <= n) {\\n sum = sum + i\\n i = i + 1\\n }\\n return { result: sum }\\n}'\n\n let schema = Schema.response('generated_code', {\n code: '',\n description: '',\n })\n\n let prompt =\n systemContext +\n '\\n\\nWrite an AsyncJS function for: ' +\n task +\n '\\n\\nReturn ONLY valid AsyncJS code in the code field. Must start with \"function\" and use while loops (not for loops).'\n\n let response = llmPredict({ prompt, options: { responseFormat: schema } })\n let result = JSON.parse(response)\n\n // Clean up any markdown fences and fix escaped newlines\n let code = result.code\n code = code.replace(/",
363
363
  "language": "javascript",
364
364
  "description": "LLM writes AsyncJS code from a description (requires llm capability)"
365
365
  },
@@ -371,7 +371,7 @@
371
371
  "type": "example",
372
372
  "group": "api",
373
373
  "order": 7,
374
- "code": "function getWeather({ lat = 37.7749, lon = -122.4194 }) {\n let url = \\`https://api.open-meteo.com/v1/forecast?latitude=\\${lat}&longitude=\\${lon}&current_weather=true\\`\n let response = httpFetch({ url, cache: 1800 })\n let weather = response.current_weather\n return { weather }\n}",
374
+ "code": "function getWeather({ lat = 37.7749, lon = -122.4194 }) {\n let url =\n 'https://api.open-meteo.com/v1/forecast?latitude=' +\n lat +\n '&longitude=' +\n lon +\n '&current_weather=true'\n let response = httpFetch({ url, cache: 1800 })\n let weather = response.current_weather\n return { weather }\n}",
375
375
  "language": "javascript",
376
376
  "description": "Fetch weather data (no API key needed)"
377
377
  },
@@ -383,7 +383,7 @@
383
383
  "type": "example",
384
384
  "group": "api",
385
385
  "order": 8,
386
- "code": "function searchMusic({ query = 'Beatles', limit = 5 }) {\n let url = \\`https://itunes.apple.com/search?term=\\${query}&limit=\\${limit}&media=music\\`\n let response = httpFetch({ url, cache: 3600 })\n let tracks = response.results.map(x => ({\n artist: x.artistName,\n track: x.trackName,\n album: x.collectionName\n }))\n return { tracks }\n}",
386
+ "code": "function searchMusic({ query = 'Beatles', limit = 5 }) {\n let url =\n 'https://itunes.apple.com/search?term=' +\n query +\n '&limit=' +\n limit +\n '&media=music'\n let response = httpFetch({ url, cache: 3600 })\n let tracks = response.results.map((x) => ({\n artist: x.artistName,\n track: x.trackName,\n album: x.collectionName,\n }))\n return { tracks }\n}",
387
387
  "language": "javascript",
388
388
  "description": "Search Apple iTunes catalog"
389
389
  },
@@ -395,7 +395,7 @@
395
395
  "type": "example",
396
396
  "group": "api",
397
397
  "order": 9,
398
- "code": "function searchRepos({ query = 'tosijs', perPage = 5 }) {\n let url = \\`https://api.github.com/search/repositories?q=\\${query}&per_page=\\${perPage}&sort=stars\\`\n let response = httpFetch({ url, cache: 300 })\n let repos = response.items.map(x => ({\n name: x.full_name,\n stars: x.stargazers_count,\n description: x.description\n }))\n return { repos }\n}",
398
+ "code": "function searchRepos({ query = 'tosijs', perPage = 5 }) {\n let url =\n 'https://api.github.com/search/repositories?q=' +\n query +\n '&per_page=' +\n perPage +\n '&sort=stars'\n let response = httpFetch({ url, cache: 300 })\n let repos = response.items.map((x) => ({\n name: x.full_name,\n stars: x.stargazers_count,\n description: x.description,\n }))\n return { repos }\n}",
399
399
  "language": "javascript",
400
400
  "description": "Search GitHub repositories"
401
401
  },
@@ -512,7 +512,7 @@
512
512
  "group": "featured",
513
513
  "order": 13,
514
514
  "requiresApi": true,
515
- "code": "function findCovers({ song = 'Yesterday', artist = 'Beatles' }) {\n // Search iTunes for the song\n let query = song + ' ' + artist\n let url = \\`https://itunes.apple.com/search?term=\\${query}&limit=25&media=music\\`\n let response = httpFetch({ url, cache: 3600 })\n\n // Format results for LLM analysis\n let results = response.results || []\n let tracks = results.map(x => \\`\"\\${x.trackName}\" by \\${x.artistName} (\\${x.collectionName})\\`)\n let trackList = tracks.join('\\\\n')\n\n // Schema.response from example - much cleaner!\n let schema = Schema.response('cover_versions', {\n covers: [{ track: '', artist: '', album: '' }]\n })\n\n let prompt = \\`Search results for \"\\${song}\" by \\${artist}:\n\n\\${trackList}\n\nList cover versions (tracks NOT by \\${artist}).\\`\n\n let llmResponse = llmPredict({ prompt, options: { responseFormat: schema } })\n let parsed = JSON.parse(llmResponse)\n return { originalArtist: artist, song, covers: parsed.covers }\n}",
515
+ "code": "function findCovers({ song = 'Yesterday', artist = 'Beatles' }) {\n // Search iTunes for the song\n let query = song + ' ' + artist\n let url =\n 'https://itunes.apple.com/search?term=' + query + '&limit=25&media=music'\n let response = httpFetch({ url, cache: 3600 })\n\n // Format results for LLM analysis\n let results = response.results || []\n let tracks = results.map(\n (x) =>\n '\"' + x.trackName + '\" by ' + x.artistName + ' (' + x.collectionName + ')'\n )\n let trackList = tracks.join('\\n')\n\n // Schema.response from example - much cleaner!\n let schema = Schema.response('cover_versions', {\n covers: [{ track: '', artist: '', album: '' }],\n })\n\n let prompt =\n 'Search results for \"' +\n song +\n '\" by ' +\n artist +\n ':\\n\\n' +\n trackList +\n '\\n\\nList cover versions (tracks NOT by ' +\n artist +\n ').'\n\n let llmResponse = llmPredict({ prompt, options: { responseFormat: schema } })\n let parsed = JSON.parse(llmResponse)\n return { originalArtist: artist, song, covers: parsed.covers }\n}",
516
516
  "language": "javascript",
517
517
  "description": "LLM analyzes API data (requires llm capability)"
518
518
  },
@@ -550,7 +550,7 @@
550
550
  "group": "llm",
551
551
  "order": 11,
552
552
  "requiresApi": true,
553
- "code": "function summarize({ source = 'coffee-origins' }) {\n // Fetch text from our sample documents\n // Options: 'coffee-origins', 'ai-history', 'renewable-energy'\n let url = \\`/texts/\\${source}.txt\\`\n let text = httpFetch({ url })\n\n let prompt = \\`Summarize the following text in 2-3 sentences:\\n\\n\\${text}\\`\n let summary = llmPredict({ prompt })\n return { source, summary }\n}",
553
+ "code": "function summarize({ source = 'coffee-origins' }) {\n // Fetch text from our sample documents\n // Options: 'coffee-origins', 'ai-history', 'renewable-energy'\n let url = '/texts/' + source + '.txt'\n let text = httpFetch({ url })\n\n let prompt = 'Summarize the following text in 2-3 sentences:\\n\\n' + text\n let summary = llmPredict({ prompt })\n return { source, summary }\n}",
554
554
  "language": "javascript",
555
555
  "description": "Fetch and summarize text (requires llm capability)"
556
556
  },
@@ -563,7 +563,7 @@
563
563
  "group": "llm",
564
564
  "order": 12,
565
565
  "requiresApi": true,
566
- "code": "function extractInfo({ text = 'John Smith is a 35-year-old software engineer from San Francisco who loves hiking and photography.' }) {\n // Schema.response builds responseFormat from an example\n let schema = Schema.response('person_info', {\n name: '',\n age: 0,\n occupation: '',\n location: '',\n hobbies: ['']\n })\n\n let prompt = \\`Extract person info from this text: \\${text}\\`\n let response = llmPredict({ prompt, options: { responseFormat: schema } })\n let person = JSON.parse(response)\n return { person }\n}",
566
+ "code": "function extractInfo({\n text = 'John Smith is a 35-year-old software engineer from San Francisco who loves hiking and photography.',\n}) {\n // Schema.response builds responseFormat from an example\n let schema = Schema.response('person_info', {\n name: '',\n age: 0,\n occupation: '',\n location: '',\n hobbies: [''],\n })\n\n let prompt = 'Extract person info from this text: ' + text\n let response = llmPredict({ prompt, options: { responseFormat: schema } })\n let person = JSON.parse(response)\n return { person }\n}",
567
567
  "language": "javascript",
568
568
  "description": "Get structured JSON from LLM (requires llm capability)"
569
569
  },
@@ -607,7 +607,7 @@
607
607
  "title": "CLAUDE.md",
608
608
  "filename": "CLAUDE.md",
609
609
  "path": "CLAUDE.md",
610
- "text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a type-safe virtual machine (~33KB) for safe execution of untrusted code in any JavaScript environment. It compiles logic chains and AI agents to JSON-serializable ASTs that run sandboxed with fuel (gas) limits.\n\nKey concept: Code travels to data (rather than shipping data to code). Agents are defined as data, not deployed code.\n\n**Two languages in one platform:**\n\n- **TJS** — TypeScript-like syntax with runtime type validation for writing your platform\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution\n\n## Common Commands\n\n```bash\n# Development\nnpm run format # ESLint fix + Prettier\nnpm run test:fast # Core tests (skips LLM & benchmarks)\nnpm run make # Full build (clean, format, grammars, tsc, esbuild)\nnpm run dev # Development server with file watcher\n\n# Testing\nbun test # Full test suite\nbun test src/path/to/file.test.ts # Single test file\nbun test --test-name-pattern \"pattern\" # Run tests matching pattern\nSKIP_LLM_TESTS=1 bun test # Skip LLM integration tests\nbun test --coverage # With coverage report\n\n# Efficient test debugging - capture once, query multiple times\nbun test 2>&1 | tee /tmp/test-results.txt | tail -20 # Run and save\ngrep -E \"^\\(fail\\)\" /tmp/test-results.txt # List failures\ngrep -A10 \"test name\" /tmp/test-results.txt # See specific error\n\n# CLI tools\nbun src/cli/tjs.ts check <file> # Parse and type check TJS file\nbun src/cli/tjs.ts run <file> # Transpile and execute\nbun src/cli/tjs.ts types <file> # Output type metadata as JSON\nbun src/cli/tjs.ts emit <file> # Output transpiled JavaScript\nbun src/cli/tjs.ts convert <file> # Convert TypeScript to TJS (--emit-tjs) or JS\n\n# Other\nnpm run test:llm # LM Studio integration tests\nnpm run bench # Vector search benchmarks\nnpm run show-size # Show gzipped bundle size\n```\n\n## Architecture\n\n### Two-Layer Design\n\n1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.\n2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~2700 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~2900 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~226 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation)\n- `src/types/` - Type system definitions (Type.ts, Generic.ts)\n- `src/transpiler/` - AJS transpiler (source → AST)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/store/` - Store implementations for persistence\n- `src/rbac/` - Role-based access control\n- `src/use-cases/` - Integration tests and real-world examples (27 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit/convert commands\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n```\n\n### Package Entry Points\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Main entry\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval utilities\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools only\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TypeScript transpilation\n```\n\n### Transpiler Chain (TS → TJS → JS)\n\nTJS supports transpiling TypeScript to JavaScript with runtime type validation. The pipeline has two distinct, independently testable steps:\n\n**Step 1: TypeScript → TJS** (`fromTS`)\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: '') -> '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n\n```typescript\nimport { tjs } from 'tosijs/lang'\n\nconst tjsSource = `\nfunction greet(name: '') -> '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsCode = tjs(tjsSource)\n// Generates JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\nimport { tjs } from 'tosijs/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**CLI Commands:**\n\n```bash\n# Convert TypeScript to TJS\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Emit TJS to JavaScript\nbun src/cli/tjs.ts emit input.tjs > output.js\n\n# Run TJS file directly (transpiles and executes)\nbun src/cli/tjs.ts run input.tjs\n```\n\n**Design Notes:**\n\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError`, not thrown (prevents exception exploits)\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests in `src/lang/lang.test.ts` (~46KB comprehensive)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `npm run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Parser Syntax Extensions\n\nTJS extends JavaScript with type annotations that survive to runtime.\n\n#### Classes (Callable Without `new`)\n\nTJS classes are wrapped to be callable without the `new` keyword:\n\n```typescript\nclass Point {\n constructor(public x: number, public y: number) {}\n}\n\n// Both work identically:\nconst p1 = Point(10, 20) // TJS way - clean\nconst p2 = new Point(10, 20) // Still works, but linter warns\n\n// The linter flags explicit `new` usage:\n// Warning: Unnecessary 'new' keyword. In TJS, classes are callable without 'new'\n```\n\nThe `wrapClass()` function in the runtime uses a Proxy to intercept calls and auto-construct.\n\n#### Function Parameters\n\n```typescript\n// Required param with example value (colon shorthand)\nfunction greet(name: 'Alice') { } // name is required, type inferred as string\n\n// Optional param with default\nfunction greet(name = 'Alice') { } // name is optional, defaults to 'Alice'\n\n// Object parameter with shape\nfunction createUser(user: { name: '', age: 0 }) { }\n\n// Nullable type\nfunction find(id: 0 || null) { } // number or null\n\n// Optional TS-style\nfunction greet(name?: '') { } // same as name = ''\n```\n\n#### Return Types\n\n```typescript\n// Return type annotation (arrow syntax)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Object return type\nfunction getUser(id: 0) -> { name: '', age: 0 } { ... }\n```\n\n#### Safety Markers\n\n```typescript\n// Unsafe function (skips runtime validation)\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n\n// Safe function (explicit validation)\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n\n// Unsafe block\nunsafe {\n // All calls in here skip validation\n fastPath(data)\n}\n```\n\n#### Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n#### Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Bare Assignments\n\n```typescript\n// Uppercase identifiers auto-get const\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n#### Module Safety Directive\n\n```typescript\n// At top of file - sets default validation level\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n#### Equality Operators\n\nTJS redefines equality to be structural by default, fixing JavaScript's confusing `==` vs `===` semantics.\n\n**Normal TJS Mode (default):**\n\n| Operator | Meaning | Example |\n| ----------- | -------------------------------- | --------------------------- |\n| `==` | Structural equality | `{a:1} == {a:1}` is `true` |\n| `!=` | Structural inequality | `{a:1} != {a:2}` is `true` |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Structural equality (explicit) | Same as `==` |\n| `a IsNot b` | Structural inequality (explicit) | Same as `!=` |\n\n```typescript\n// Structural equality - compares values deeply\nconst a = { x: 1, y: [2, 3] }\nconst b = { x: 1, y: [2, 3] }\na == b // true (same structure)\na === b // false (different objects)\n\n// Works with arrays too\n[1, 2, 3] == [1, 2, 3] // true\n\n// Infix operators for readability\nuser Is expectedUser\nresult IsNot errorValue\n```\n\n**Implementation Notes:**\n\n- **AJS (VM)**: The VM's expression evaluator handles `==`/`!=` with structural semantics at runtime\n- **TJS (browser/Node)**: Source transformation converts `==` to `Is()` and `!=` to `IsNot()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- The `Is()` and `IsNot()` functions are available in `src/lang/runtime.ts` and exposed globally\n\n## WASM Blocks\n\nTJS supports inline WebAssembly for performance-critical code. WASM blocks are compiled at transpile time and embedded as base64 in the output.\n\n### Syntax\n\n```typescript\nconst add = wasm (a: i32, b: i32) -> i32 {\n local.get $a\n local.get $b\n i32.add\n}\n```\n\n### Features\n\n- **Transpile-time compilation**: WASM bytecode is generated during transpilation, not at runtime\n- **WAT comments**: Human-readable WebAssembly Text format is included as comments above the base64\n- **Type-safe**: Parameters and return types are validated\n- **Self-contained**: Compiled WASM is embedded in output JS, no separate .wasm files needed\n\n### Output Example\n\nThe transpiler generates code like:\n\n```javascript\n/*\n * WASM Block: add\n * WAT (WebAssembly Text):\n * (func $add (param $a i32) (param $b i32) (result i32)\n * local.get 0\n * local.get 1\n * i32.add\n * )\n */\nconst add = await (async () => {\n const bytes = Uint8Array.from(atob('AGFzbQEAAAA...'), c => c.charCodeAt(0))\n const { instance } = await WebAssembly.instantiate(bytes)\n return instance.exports.fn\n})()\n```\n\n### Current Limitations\n\n- No SIMD support yet (planned - see TODO.md)\n- Memory operations require manual management\n- No imports/exports beyond the function itself\n\n## Dependencies\n\nRuntime (shipped): `acorn` (JS parser, ~30KB), `tosijs-schema` (validation, ~5KB). Both have zero transitive dependencies.\n\n## Forbidden Properties (Security)\n\nThe following property names are blocked in expression evaluation to prevent prototype pollution:\n\n- `__proto__`, `constructor`, `prototype`\n\nThese are hardcoded in `runtime.ts` and checked during member access in `evaluateExpr()`.\n\n## Batteries System\n\nThe batteries (`src/batteries/`) provide zero-config local AI development:\n\n- **Lazy initialization**: First import audits LM Studio models (cached 24 hours)\n- **HTTPS detection**: Blocks local LLM calls from HTTPS contexts (security)\n- **Capabilities interface**: `fetch`, `store` (KV + vector), `llmBattery` (predict/embed)\n\nRegister battery atoms: `new AgentVM(batteryAtoms)` then pass `{ capabilities: batteries }` to `run()`.\n"
610
+ "text": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\n**tjs-lang** (npm: `tjs-lang`) is a type-safe virtual machine (~33KB) for safe execution of untrusted code in any JavaScript environment. It compiles logic chains and AI agents to JSON-serializable ASTs that run sandboxed with fuel (gas) limits.\n\nKey concept: Code travels to data (rather than shipping data to code). Agents are defined as data, not deployed code.\n\n**Two languages in one platform:**\n\n- **TJS** — TypeScript-like syntax with runtime type validation for writing your platform\n- **AJS** — Agent language that compiles to JSON AST for safe, sandboxed execution\n\n## Common Commands\n\n```bash\n# Development\nnpm run format # ESLint fix + Prettier\nnpm run test:fast # Core tests (skips LLM & benchmarks)\nnpm run make # Full build (clean, format, grammars, tsc, esbuild)\nnpm run dev # Development server with file watcher\n\n# Testing\nbun test # Full test suite\nbun test src/path/to/file.test.ts # Single test file\nbun test --test-name-pattern \"pattern\" # Run tests matching pattern\nSKIP_LLM_TESTS=1 bun test # Skip LLM integration tests\nbun test --coverage # With coverage report\n\n# Efficient test debugging - capture once, query multiple times\nbun test 2>&1 | tee /tmp/test-results.txt | tail -20 # Run and save\ngrep -E \"^\\(fail\\)\" /tmp/test-results.txt # List failures\ngrep -A10 \"test name\" /tmp/test-results.txt # See specific error\n\n# CLI tools\nbun src/cli/tjs.ts check <file> # Parse and type check TJS file\nbun src/cli/tjs.ts run <file> # Transpile and execute\nbun src/cli/tjs.ts types <file> # Output type metadata as JSON\nbun src/cli/tjs.ts emit <file> # Output transpiled JavaScript\nbun src/cli/tjs.ts convert <file> # Convert TypeScript to TJS (--emit-tjs) or JS\n\n# Other\nnpm run test:llm # LM Studio integration tests\nnpm run bench # Vector search benchmarks\nnpm run show-size # Show gzipped bundle size\n```\n\n## Architecture\n\n### Two-Layer Design\n\n1. **Builder Layer** (`src/builder.ts`): Fluent API that constructs AST nodes. Contains no execution logic.\n2. **Runtime Layer** (`src/vm/runtime.ts`): Executes AST nodes. Contains all atom implementations (~2700 lines, security-critical).\n\n### Key Source Files\n\n- `src/index.ts` - Main entry, re-exports everything\n- `src/vm/runtime.ts` - All atom implementations, expression evaluation, fuel charging (~2900 lines, security-critical)\n- `src/vm/vm.ts` - AgentVM class (~226 lines)\n- `src/vm/atoms/batteries.ts` - Battery atoms (vector search, LLM, store operations)\n- `src/builder.ts` - TypedBuilder fluent API (~19KB)\n- `src/lang/parser.ts` - TJS parser with colon shorthand, unsafe markers, return type extraction\n- `src/lang/emitters/ast.ts` - Emits Agent99 AST from parsed source\n- `src/lang/emitters/js.ts` - Emits JavaScript with `__tjs` metadata\n- `src/lang/emitters/from-ts.ts` - TypeScript to TJS/JS transpiler with class metadata extraction\n- `src/lang/inference.ts` - Type inference from example values\n- `src/lang/linter.ts` - Static analysis (unused vars, unreachable code, no-explicit-new)\n- `src/lang/runtime.ts` - TJS runtime (monadic errors, type checking, wrapClass)\n- `src/lang/wasm.ts` - WASM compiler (opcodes, disassembler, bytecode generation)\n- `src/types/` - Type system definitions (Type.ts, Generic.ts)\n- `src/transpiler/` - AJS transpiler (source → AST)\n- `src/batteries/` - LM Studio integration (lazy init, model audit, vector search)\n- `src/store/` - Store implementations for persistence\n- `src/rbac/` - Role-based access control\n- `src/use-cases/` - Integration tests and real-world examples (27 test files)\n- `src/cli/tjs.ts` - CLI tool for check/run/types/emit/convert commands\n\n### Core APIs\n\n```typescript\n// Language\najs`...` // Parse AJS to AST\ntjs`...` // Parse TypeScript variant with type metadata\ntranspile(source, options) // Full transpilation with signature extraction\ncreateAgent(source, vm) // Creates callable agent\n\n// VM\nconst vm = new AgentVM(customAtoms)\nawait vm.run(ast, args, { fuel, capabilities, timeoutMs, trace })\n\n// Builder\nAgent.take(schema).varSet(...).httpFetch(...).return(schema)\nvm.Agent // Builder with custom atoms included\n```\n\n### Package Entry Points\n\n```typescript\nimport { Agent, AgentVM, ajs, tjs } from 'tjs-lang' // Main entry\nimport { Eval, SafeFunction } from 'tjs-lang/eval' // Safe eval utilities\nimport { tjs, transpile } from 'tjs-lang/lang' // Language tools only\nimport { fromTS } from 'tjs-lang/lang/from-ts' // TypeScript transpilation\n```\n\n### Transpiler Chain (TS → TJS → JS)\n\nTJS supports transpiling TypeScript to JavaScript with runtime type validation. The pipeline has two distinct, independently testable steps:\n\n**Step 1: TypeScript → TJS** (`fromTS`)\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\n\nconst tsSource = `\nfunction greet(name: string): string {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst result = fromTS(tsSource, { emitTJS: true })\n// result.code contains TJS:\n// function greet(name: '') -> '' {\n// return \\`Hello, \\${name}!\\`\n// }\n```\n\n**Step 2: TJS → JavaScript** (`tjs`)\n\n```typescript\nimport { tjs } from 'tosijs/lang'\n\nconst tjsSource = `\nfunction greet(name: '') -> '' {\n return \\`Hello, \\${name}!\\`\n}\n`\n\nconst jsCode = tjs(tjsSource)\n// Generates JavaScript with __tjs metadata for runtime validation\n```\n\n**Full Chain Example:**\n\n```typescript\nimport { fromTS } from 'tosijs/lang/from-ts'\nimport { tjs } from 'tosijs/lang'\n\n// TypeScript source with type annotations\nconst tsSource = `\nfunction add(a: number, b: number): number {\n return a + b\n}\n`\n\n// Step 1: TS → TJS\nconst tjsResult = fromTS(tsSource, { emitTJS: true })\n\n// Step 2: TJS → JS (with runtime validation)\nconst jsCode = tjs(tjsResult.code)\n\n// Execute the result\nconst fn = new Function('__tjs', jsCode + '; return add')(__tjs_runtime)\nfn(1, 2) // Returns 3\nfn('a', 'b') // Returns { error: 'type mismatch', ... }\n```\n\n**CLI Commands:**\n\n```bash\n# Convert TypeScript to TJS\nbun src/cli/tjs.ts convert input.ts --emit-tjs > output.tjs\n\n# Emit TJS to JavaScript\nbun src/cli/tjs.ts emit input.tjs > output.js\n\n# Run TJS file directly (transpiles and executes)\nbun src/cli/tjs.ts run input.tjs\n```\n\n**Design Notes:**\n\n- The two steps are intentionally separate for tree-shaking (TS support is optional)\n- `fromTS` lives in a separate entry point (`tosijs/lang/from-ts`)\n- Import only what you need to keep bundle size minimal\n- Each step is independently testable (see `src/lang/codegen.test.ts`)\n\n### Security Model\n\n- **Capability-based**: VM has zero IO by default; inject `fetch`, `store`, `llm` via capabilities\n- **Fuel metering**: Every atom has a cost; execution stops when fuel exhausted\n- **Timeout enforcement**: Default `fuel × 10ms`; explicit `timeoutMs` overrides\n- **Monadic errors**: Errors wrapped in `AgentError`, not thrown (prevents exception exploits)\n- **Expression sandboxing**: ExprNode AST evaluation, blocked prototype access\n\n### Expression Evaluation\n\nExpressions use AST nodes (`$expr`), not strings:\n\n```typescript\n{ $expr: 'binary', op: '+', left: {...}, right: {...} }\n{ $expr: 'ident', name: 'varName' }\n{ $expr: 'member', object: {...}, property: 'foo' }\n```\n\nEach node costs 0.01 fuel. Forbidden: function calls, `new`, `this`, `__proto__`, `constructor`.\n\n## Testing Strategy\n\n- Unit tests alongside source files (`*.test.ts`)\n- Integration tests in `src/use-cases/` (RAG, orchestration, malicious actors)\n- Security tests in `src/use-cases/malicious-actor.test.ts`\n- Language tests in `src/lang/lang.test.ts` (~46KB comprehensive)\n\nCoverage targets: 98% lines on `src/vm/runtime.ts` (security-critical), 80%+ overall.\n\n## Key Patterns\n\n### Adding a New Atom\n\n1. Define with `defineAtom(opCode, inputSchema, outputSchema, implementation, { cost, timeoutMs, docs })`\n2. Add to `src/vm/atoms/` and export from `src/vm/atoms/index.ts`\n3. Add tests\n4. Run `npm run test:fast`\n\n**Atom implementation notes:**\n\n- `cost` can be static number or dynamic: `(input, ctx) => number`\n- `timeoutMs` defaults to 1000ms; use `0` for no timeout (e.g., `seq`)\n- Atoms are always async; fuel deduction is automatic in the `exec` wrapper\n\n### Debugging Agents\n\nEnable tracing: `vm.run(ast, args, { trace: true })` returns `TraceEvent[]` with execution path, fuel consumption, and state changes.\n\n### Custom Atoms Must\n\n- Be non-blocking (no synchronous CPU-heavy work)\n- Respect `ctx.signal` for cancellation\n- Access IO only via `ctx.capabilities`\n\n### Value Resolution\n\nThe `resolveValue()` function handles multiple input patterns:\n\n- `{ $kind: 'arg', path: 'varName' }` → lookup in `ctx.args`\n- `{ $expr: ... }` → evaluate ExprNode via `evaluateExpr()`\n- String with dots `'obj.foo.bar'` → traverse state with forbidden property checks\n- Bare strings → lookup in state, else return literal\n\n### Monadic Error Flow\n\nWhen `ctx.error` is set, subsequent atoms in a `seq` skip execution. Errors are wrapped in `AgentError`, not thrown. This prevents exception-based exploits.\n\n### TJS Parser Syntax Extensions\n\nTJS extends JavaScript with type annotations that survive to runtime.\n\n#### Classes (Callable Without `new`)\n\nTJS classes are wrapped to be callable without the `new` keyword:\n\n```typescript\nclass Point {\n constructor(public x: number, public y: number) {}\n}\n\n// Both work identically:\nconst p1 = Point(10, 20) // TJS way - clean\nconst p2 = new Point(10, 20) // Still works, but linter warns\n\n// The linter flags explicit `new` usage:\n// Warning: Unnecessary 'new' keyword. In TJS, classes are callable without 'new'\n```\n\nThe `wrapClass()` function in the runtime uses a Proxy to intercept calls and auto-construct.\n\n#### Function Parameters\n\n```typescript\n// Required param with example value (colon shorthand)\nfunction greet(name: 'Alice') { } // name is required, type inferred as string\n\n// Optional param with default\nfunction greet(name = 'Alice') { } // name is optional, defaults to 'Alice'\n\n// Object parameter with shape\nfunction createUser(user: { name: '', age: 0 }) { }\n\n// Nullable type\nfunction find(id: 0 || null) { } // number or null\n\n// Optional TS-style\nfunction greet(name?: '') { } // same as name = ''\n```\n\n#### Return Types\n\n```typescript\n// Return type annotation (arrow syntax)\nfunction add(a: 0, b: 0) -> 0 { return a + b }\n\n// Object return type\nfunction getUser(id: 0) -> { name: '', age: 0 } { ... }\n```\n\n#### Safety Markers\n\n```typescript\n// Unsafe function (skips runtime validation)\nfunction fastAdd(! a: 0, b: 0) { return a + b }\n\n// Safe function (explicit validation)\nfunction safeAdd(? a: 0, b: 0) { return a + b }\n\n// Unsafe block\nunsafe {\n // All calls in here skip validation\n fastPath(data)\n}\n```\n\n#### Type Declarations\n\n```typescript\n// Simple type from example\nType Name 'Alice'\n\n// Type with description and example\nType User {\n description: 'a user object'\n example: { name: '', age: 0 }\n}\n\n// Type with predicate (auto-generates type guard from example)\nType EvenNumber {\n description: 'an even number'\n example: 2\n predicate(x) { return x % 2 === 0 }\n}\n```\n\n#### Generic Declarations\n\n```typescript\n// Simple generic\nGeneric Box<T> {\n description: 'a boxed value'\n predicate(x, T) {\n return typeof x === 'object' && x !== null && 'value' in x && T(x.value)\n }\n}\n\n// Generic with default type parameter\nGeneric Container<T, U = ''> {\n description: 'container with label'\n predicate(obj, T, U) {\n return T(obj.item) && U(obj.label)\n }\n}\n```\n\n#### Bare Assignments\n\n```typescript\n// Uppercase identifiers auto-get const\nFoo = Type('test', 'example') // becomes: const Foo = Type(...)\nMyConfig = { debug: true } // becomes: const MyConfig = { ... }\n```\n\n#### Module Safety Directive\n\n```typescript\n// At top of file - sets default validation level\nsafety none // No validation (metadata only)\nsafety inputs // Validate function inputs (default)\nsafety all // Validate everything (debug mode)\n```\n\n#### Equality Operators\n\nTJS redefines equality to be structural by default, fixing JavaScript's confusing `==` vs `===` semantics.\n\n**Normal TJS Mode (default):**\n\n| Operator | Meaning | Example |\n| ----------- | -------------------------------- | --------------------------- |\n| `==` | Structural equality | `{a:1} == {a:1}` is `true` |\n| `!=` | Structural inequality | `{a:1} != {a:2}` is `true` |\n| `===` | Identity (same reference) | `obj === obj` is `true` |\n| `!==` | Not same reference | `{a:1} !== {a:1}` is `true` |\n| `a Is b` | Structural equality (explicit) | Same as `==` |\n| `a IsNot b` | Structural inequality (explicit) | Same as `!=` |\n\n```typescript\n// Structural equality - compares values deeply\nconst a = { x: 1, y: [2, 3] }\nconst b = { x: 1, y: [2, 3] }\na == b // true (same structure)\na === b // false (different objects)\n\n// Works with arrays too\n[1, 2, 3] == [1, 2, 3] // true\n\n// Infix operators for readability\nuser Is expectedUser\nresult IsNot errorValue\n```\n\n**Implementation Notes:**\n\n- **AJS (VM)**: The VM's expression evaluator handles `==`/`!=` with structural semantics at runtime\n- **TJS (browser/Node)**: Source transformation converts `==` to `Is()` and `!=` to `IsNot()` calls\n- **`===` and `!==`**: Always preserved as identity checks, never transformed\n- The `Is()` and `IsNot()` functions are available in `src/lang/runtime.ts` and exposed globally\n\n## WASM Blocks\n\nTJS supports inline WebAssembly for performance-critical code. WASM blocks are compiled at transpile time and embedded as base64 in the output.\n\n### Syntax\n\n```typescript\nconst add = wasm (a: i32, b: i32) -> i32 {\n local.get $a\n local.get $b\n i32.add\n}\n```\n\n### Features\n\n- **Transpile-time compilation**: WASM bytecode is generated during transpilation, not at runtime\n- **WAT comments**: Human-readable WebAssembly Text format is included as comments above the base64\n- **Type-safe**: Parameters and return types are validated\n- **Self-contained**: Compiled WASM is embedded in output JS, no separate .wasm files needed\n\n### Output Example\n\nThe transpiler generates code like:\n\n```javascript\n/*\n * WASM Block: add\n * WAT (WebAssembly Text):\n * (func $add (param $a i32) (param $b i32) (result i32)\n * local.get 0\n * local.get 1\n * i32.add\n * )\n */\nconst add = await (async () => {\n const bytes = Uint8Array.from(atob('AGFzbQEAAAA...'), (c) => c.charCodeAt(0))\n const { instance } = await WebAssembly.instantiate(bytes)\n return instance.exports.fn\n})()\n```\n\n### Current Limitations\n\n- No SIMD support yet (planned - see TODO.md)\n- Memory operations require manual management\n- No imports/exports beyond the function itself\n\n## Dependencies\n\nRuntime (shipped): `acorn` (JS parser, ~30KB), `tosijs-schema` (validation, ~5KB). Both have zero transitive dependencies.\n\n## Forbidden Properties (Security)\n\nThe following property names are blocked in expression evaluation to prevent prototype pollution:\n\n- `__proto__`, `constructor`, `prototype`\n\nThese are hardcoded in `runtime.ts` and checked during member access in `evaluateExpr()`.\n\n## Batteries System\n\nThe batteries (`src/batteries/`) provide zero-config local AI development:\n\n- **Lazy initialization**: First import audits LM Studio models (cached 24 hours)\n- **HTTPS detection**: Blocks local LLM calls from HTTPS contexts (security)\n- **Capabilities interface**: `fetch`, `store` (KV + vector), `llmBattery` (predict/embed)\n\nRegister battery atoms: `new AgentVM(batteryAtoms)` then pass `{ capabilities: batteries }` to `run()`.\n"
611
611
  },
612
612
  {
613
613
  "title": "Context: Working with tosijs-schema",
@@ -645,6 +645,12 @@
645
645
  "filename": "from-ts.test.ts",
646
646
  "path": "src/lang/from-ts.test.ts"
647
647
  },
648
+ {
649
+ "title": "icebox",
650
+ "filename": "tasks-f6d70f.md",
651
+ "path": ".haltija/tasks-f6d70f.md",
652
+ "text": "# icebox\n\n# queued\n\n# in_progress\n\n# blocked\n\n# review\n\n# done\n\n# trash\n"
653
+ },
648
654
  {
649
655
  "text": "# TJS Platform Cloud Functions\n\nCloud Functions for the TJS Platform.\nModular architecture with separate concerns:\n- crypto.tjs - Encryption/decryption\n- llm.tjs - LLM capability (multi-provider)\n- schema.tjs - JSON schema validation\n- rbac.tjs - Security rules & access shortcuts\n- indexes.tjs - Automatic index management\n- store.tjs - Store capability with RBAC\n- routing.tjs - URL pattern matching & function cache\n\n## Future Work\n- Benchmark `safety none` vs `safety inputs` - likely negligible overhead for small payloads\n\n---\n\n## Get User API Keys\n\nLoads and decrypts the user's API keys from Firestore.\n\n---\n\n## Health Check\n\nSimple endpoint to verify functions are deployed and running.\n\n---\n\n## Agent Run Endpoint\n\nUniversal AJS endpoint - accepts code, args, and fuel limit.\nExecutes the code in a sandboxed VM with user's API keys as capabilities.\n\n---\n\n## REST Agent Endpoint\n\nSame as agentRun but as a simple POST endpoint.\nAuth via Bearer token (Firebase ID token).\n\n---\n\n## Page Endpoint\n\nServes stored functions based on URL routing.\nMatches incoming path against stored function URL patterns.\nExecutes matched function's AJS code and returns with appropriate content-type.",
650
656
  "title": "index (inline docs)",
@@ -103,7 +103,7 @@ export const examples: Example[] = [
103
103
  description: 'Fetch weather data (no API key needed)',
104
104
  group: 'api',
105
105
  code: `function getWeather({ lat = 37.7749, lon = -122.4194 }) {
106
- let url = \`https://api.open-meteo.com/v1/forecast?latitude=\${lat}&longitude=\${lon}&current_weather=true\`
106
+ let url = 'https://api.open-meteo.com/v1/forecast?latitude=' + lat + '&longitude=' + lon + '&current_weather=true'
107
107
  let response = httpFetch({ url, cache: 1800 })
108
108
  let weather = response.current_weather
109
109
  return { weather }
@@ -114,7 +114,7 @@ export const examples: Example[] = [
114
114
  description: 'Search Apple iTunes catalog',
115
115
  group: 'api',
116
116
  code: `function searchMusic({ query = 'Beatles', limit = 5 }) {
117
- let url = \`https://itunes.apple.com/search?term=\${query}&limit=\${limit}&media=music\`
117
+ let url = 'https://itunes.apple.com/search?term=' + query + '&limit=' + limit + '&media=music'
118
118
  let response = httpFetch({ url, cache: 3600 })
119
119
  let tracks = response.results.map(x => ({
120
120
  artist: x.artistName,
@@ -129,7 +129,7 @@ export const examples: Example[] = [
129
129
  description: 'Search GitHub repositories',
130
130
  group: 'api',
131
131
  code: `function searchRepos({ query = 'tosijs', perPage = 5 }) {
132
- let url = \`https://api.github.com/search/repositories?q=\${query}&per_page=\${perPage}&sort=stars\`
132
+ let url = 'https://api.github.com/search/repositories?q=' + query + '&per_page=' + perPage + '&sort=stars'
133
133
  let response = httpFetch({ url, cache: 300 })
134
134
  let repos = response.items.map(x => ({
135
135
  name: x.full_name,
@@ -158,10 +158,10 @@ export const examples: Example[] = [
158
158
  code: `function summarize({ source = 'coffee-origins' }) {
159
159
  // Fetch text from our sample documents
160
160
  // Options: 'coffee-origins', 'ai-history', 'renewable-energy'
161
- let url = \`/texts/\${source}.txt\`
161
+ let url = '/texts/' + source + '.txt'
162
162
  let text = httpFetch({ url })
163
163
 
164
- let prompt = \`Summarize the following text in 2-3 sentences:\n\n\${text}\`
164
+ let prompt = 'Summarize the following text in 2-3 sentences:\\n\\n' + text
165
165
  let summary = llmPredict({ prompt })
166
166
  return { source, summary }
167
167
  }`,
@@ -181,7 +181,7 @@ export const examples: Example[] = [
181
181
  hobbies: ['']
182
182
  })
183
183
 
184
- let prompt = \`Extract person info from this text: \${text}\`
184
+ let prompt = 'Extract person info from this text: ' + text
185
185
  let response = llmPredict({ prompt, options: { responseFormat: schema } })
186
186
  let person = JSON.parse(response)
187
187
  return { person }
@@ -195,12 +195,12 @@ export const examples: Example[] = [
195
195
  code: `function findCovers({ song = 'Yesterday', artist = 'Beatles' }) {
196
196
  // Search iTunes for the song
197
197
  let query = song + ' ' + artist
198
- let url = \`https://itunes.apple.com/search?term=\${query}&limit=25&media=music\`
198
+ let url = 'https://itunes.apple.com/search?term=' + query + '&limit=25&media=music'
199
199
  let response = httpFetch({ url, cache: 3600 })
200
200
 
201
201
  // Format results for LLM analysis
202
202
  let results = response.results || []
203
- let tracks = results.map(x => \`"\${x.trackName}" by \${x.artistName} (\${x.collectionName})\`)
203
+ let tracks = results.map(x => '"' + x.trackName + '" by ' + x.artistName + ' (' + x.collectionName + ')')
204
204
  let trackList = tracks.join('\\n')
205
205
 
206
206
  // Schema.response from example - much cleaner!
@@ -208,11 +208,7 @@ export const examples: Example[] = [
208
208
  covers: [{ track: '', artist: '', album: '' }]
209
209
  })
210
210
 
211
- let prompt = \`Search results for "\${song}" by \${artist}:
212
-
213
- \${trackList}
214
-
215
- List cover versions (tracks NOT by \${artist}).\`
211
+ let prompt = 'Search results for "' + song + '" by ' + artist + ':\\n\\n' + trackList + '\\n\\nList cover versions (tracks NOT by ' + artist + ').'
216
212
 
217
213
  let llmResponse = llmPredict({ prompt, options: { responseFormat: schema } })
218
214
  let parsed = JSON.parse(llmResponse)
@@ -226,19 +222,15 @@ List cover versions (tracks NOT by \${artist}).\`
226
222
  requiresApi: true,
227
223
  code: `function mathAssistant({ question = 'What is 23 * 47 + 156?' }) {
228
224
  // First, ask LLM to extract the calculation
229
- let extractPrompt = \`Extract the math expression from this question. Return ONLY the expression, nothing else.
230
- Question: \${question}\`
225
+ let extractPrompt = 'Extract the math expression from this question. Return ONLY the expression, nothing else.\\nQuestion: ' + question
231
226
  let expression = llmPredict({ prompt: extractPrompt })
232
227
 
233
228
  // Evaluate the expression (simple eval simulation)
234
- let calcPrompt = \`Calculate: \${expression}
235
- Return ONLY the numeric result.\`
229
+ let calcPrompt = 'Calculate: ' + expression + '\\nReturn ONLY the numeric result.'
236
230
  let calcResult = llmPredict({ prompt: calcPrompt })
237
231
 
238
232
  // Format the answer
239
- let answerPrompt = \`The user asked: "\${question}"
240
- The calculated result is: \${calcResult}
241
- Write a brief, friendly response with the answer.\`
233
+ let answerPrompt = 'The user asked: "' + question + '"\\nThe calculated result is: ' + calcResult + '\\nWrite a brief, friendly response with the answer.'
242
234
  let answer = llmPredict({ prompt: answerPrompt })
243
235
 
244
236
  return { question, expression: expression.trim(), result: calcResult.trim(), answer }
@@ -251,26 +243,15 @@ Write a brief, friendly response with the answer.\`
251
243
  requiresApi: true,
252
244
  code: `function collaborativeWriting({ topic = 'the future of renewable energy' }) {
253
245
  // Agent 1: Research Agent - generates key points
254
- let researchPrompt = \`You are a research agent. Generate 3 key facts or points about: \${topic}
255
- Format as a numbered list. Be concise.\`
246
+ let researchPrompt = 'You are a research agent. Generate 3 key facts or points about: ' + topic + '\\nFormat as a numbered list. Be concise.'
256
247
  let research = llmPredict({ prompt: researchPrompt })
257
248
 
258
249
  // Agent 2: Writer Agent - creates content from research
259
- let writerPrompt = \`You are a writer agent. Using these research points:
260
-
261
- \${research}
262
-
263
- Write a short, engaging paragraph (2-3 sentences) about \${topic}.
264
- Make it informative and accessible.\`
250
+ let writerPrompt = 'You are a writer agent. Using these research points:\\n\\n' + research + '\\n\\nWrite a short, engaging paragraph (2-3 sentences) about ' + topic + '.\\nMake it informative and accessible.'
265
251
  let article = llmPredict({ prompt: writerPrompt })
266
252
 
267
253
  // Agent 3: Editor Agent - reviews and improves
268
- let editorPrompt = \`You are an editor agent. Review this draft:
269
-
270
- "\${article}"
271
-
272
- Suggest one specific improvement. Then provide the improved version.
273
- Format: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"\`
254
+ let editorPrompt = 'You are an editor agent. Review this draft:\\n\\n"' + article + '"\\n\\nSuggest one specific improvement. Then provide the improved version.\\nFormat: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"'
274
255
  let edited = llmPredict({ prompt: editorPrompt })
275
256
 
276
257
  return {
@@ -357,36 +338,16 @@ Format: "Suggestion: [your suggestion]\\n\\nImproved: [improved text]"\`
357
338
  requiresApi: true,
358
339
  code: `function solveWithCode({ problem = 'Calculate the 10th Fibonacci number' }) {
359
340
  // System prompt with AsyncJS rules and example
360
- let systemContext = \`You write AsyncJS code. AsyncJS is a JavaScript subset.
361
-
362
- RULES:
363
- - NO: async, await, new, class, this, var, for loops
364
- - Use let for variables, while for loops
365
- - Return an object: return { result }
366
-
367
- EXAMPLE (factorial):
368
- function solve() {
369
- let result = 1
370
- let i = 5
371
- while (i > 1) {
372
- result = result * i
373
- i = i - 1
374
- }
375
- return { result }
376
- }
377
-
378
- Return ONLY the function code, nothing else.\`
341
+ let systemContext = 'You write AsyncJS code. AsyncJS is a JavaScript subset.\\n\\nRULES:\\n- NO: async, await, new, class, this, var, for loops\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE (factorial):\\nfunction solve() {\\n let result = 1\\n let i = 5\\n while (i > 1) {\\n result = result * i\\n i = i - 1\\n }\\n return { result }\\n}\\n\\nReturn ONLY the function code, nothing else.'
379
342
 
380
- let prompt = \`\${systemContext}
381
-
382
- Write a function called "solve" that: \${problem}\`
343
+ let prompt = systemContext + '\\n\\nWrite a function called "solve" that: ' + problem
383
344
 
384
345
  let response = llmPredict({ prompt })
385
346
 
386
347
  // Clean up code - remove markdown fences, fix escapes, extract function
387
348
  let code = response
388
- code = code.replace(/\`\`\`(?:javascript|js|asyncjs)?\\n?/g, '')
389
- code = code.replace(/\\n?\`\`\`/g, '')
349
+ code = code.replace(/\\\`\\\`\\\`(?:javascript|js|asyncjs)?\\n?/g, '')
350
+ code = code.replace(/\\n?\\\`\\\`\\\`/g, '')
390
351
  code = code.replace(/\\\\n/g, '\\n')
391
352
  code = code.replace(/\\\\t/g, '\\t')
392
353
  code = code.replace(/\\\\"/g, '"')
@@ -425,43 +386,22 @@ Write a function called "solve" that: \${problem}\`
425
386
  requiresApi: true,
426
387
  code: `function generateCode({ task = 'Calculate the factorial of n' }) {
427
388
  // System prompt with AsyncJS rules and complete example
428
- let systemContext = \`You write AsyncJS code. AsyncJS is a subset of JavaScript.
429
-
430
- RULES:
431
- - Types by example: fn(n: 5) means required number param with example value 5
432
- - NO: async, await, new, class, this, var, for, generator functions (function*)
433
- - Use let for variables, while for loops
434
- - Return an object: return { result }
435
-
436
- EXAMPLE - calculating sum of 1 to n:
437
- function sumTo(n: 10) {
438
- let sum = 0
439
- let i = 1
440
- while (i <= n) {
441
- sum = sum + i
442
- i = i + 1
443
- }
444
- return { result: sum }
445
- }\`
389
+ let systemContext = 'You write AsyncJS code. AsyncJS is a subset of JavaScript.\\n\\nRULES:\\n- Types by example: fn(n: 5) means required number param with example value 5\\n- NO: async, await, new, class, this, var, for, generator functions (function*)\\n- Use let for variables, while for loops\\n- Return an object: return { result }\\n\\nEXAMPLE - calculating sum of 1 to n:\\nfunction sumTo(n: 10) {\\n let sum = 0\\n let i = 1\\n while (i <= n) {\\n sum = sum + i\\n i = i + 1\\n }\\n return { result: sum }\\n}'
446
390
 
447
391
  let schema = Schema.response('generated_code', {
448
392
  code: '',
449
393
  description: ''
450
394
  })
451
395
 
452
- let prompt = \`\${systemContext}
453
-
454
- Write an AsyncJS function for: \${task}
455
-
456
- Return ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).\`
396
+ let prompt = systemContext + '\\n\\nWrite an AsyncJS function for: ' + task + '\\n\\nReturn ONLY valid AsyncJS code in the code field. Must start with "function" and use while loops (not for loops).'
457
397
 
458
398
  let response = llmPredict({ prompt, options: { responseFormat: schema } })
459
399
  let result = JSON.parse(response)
460
400
 
461
401
  // Clean up any markdown fences and fix escaped newlines
462
402
  let code = result.code
463
- code = code.replace(/\`\`\`(?:javascript|js)?\\n?/g, '')
464
- code = code.replace(/\\n?\`\`\`/g, '')
403
+ code = code.replace(/\\\`\\\`\\\`(?:javascript|js)?\\n?/g, '')
404
+ code = code.replace(/\\n?\\\`\\\`\\\`/g, '')
465
405
  code = code.replace(/\\\\n/g, '\\n')
466
406
  code = code.replace(/\\\\t/g, '\\t')
467
407
  code = code.trim()