tjs-lang 0.8.0 → 0.8.2

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 (86) hide show
  1. package/CLAUDE.md +15 -5
  2. package/CONTEXT.md +4 -0
  3. package/demo/docs.json +12 -690
  4. package/demo/examples.test.ts +6 -2
  5. package/demo/src/playground-shared.ts +48 -16
  6. package/demo/src/playground-test-results.test.ts +112 -0
  7. package/demo/src/tjs-playground.ts +11 -5
  8. package/dist/eslint.config.d.ts +2 -0
  9. package/dist/index.js +146 -141
  10. package/dist/index.js.map +4 -4
  11. package/dist/src/lang/core.d.ts +1 -1
  12. package/dist/src/lang/dialect.d.ts +35 -0
  13. package/dist/src/lang/emitters/ast.d.ts +1 -1
  14. package/dist/src/lang/emitters/js-tests.d.ts +7 -0
  15. package/dist/src/lang/emitters/js-wasm.d.ts +5 -1
  16. package/dist/src/lang/emitters/js.d.ts +21 -0
  17. package/dist/src/lang/index.d.ts +3 -1
  18. package/dist/src/lang/module-loader.d.ts +125 -0
  19. package/dist/src/lang/parser-transforms.d.ts +79 -0
  20. package/dist/src/lang/parser-types.d.ts +50 -0
  21. package/dist/src/lang/parser.d.ts +18 -0
  22. package/dist/src/lang/transpiler.d.ts +1 -0
  23. package/dist/src/lang/types.d.ts +18 -0
  24. package/dist/src/lang/wasm.d.ts +67 -1
  25. package/dist/src/vm/runtime.d.ts +18 -0
  26. package/dist/src/vm/vm.d.ts +15 -1
  27. package/dist/tjs-batteries.js +2 -2
  28. package/dist/tjs-batteries.js.map +2 -2
  29. package/dist/tjs-eval.js +43 -41
  30. package/dist/tjs-eval.js.map +3 -3
  31. package/dist/tjs-from-ts.js +2 -2
  32. package/dist/tjs-from-ts.js.map +2 -2
  33. package/dist/tjs-lang.js +107 -104
  34. package/dist/tjs-lang.js.map +4 -4
  35. package/dist/tjs-vm.js +53 -51
  36. package/dist/tjs-vm.js.map +3 -3
  37. package/docs/README.md +2 -0
  38. package/docs/lm-studio-setup.md +143 -0
  39. package/docs/universal-endpoint.md +122 -0
  40. package/llms.txt +9 -2
  41. package/package.json +24 -4
  42. package/src/batteries/audit.ts +6 -5
  43. package/src/batteries/llm.ts +8 -3
  44. package/src/builder.ts +0 -3
  45. package/src/cli/commands/check.ts +3 -2
  46. package/src/cli/commands/emit.ts +4 -2
  47. package/src/cli/commands/run.ts +6 -2
  48. package/src/cli/commands/test.ts +1 -1
  49. package/src/cli/commands/types.ts +2 -2
  50. package/src/lang/codegen.test.ts +4 -1
  51. package/src/lang/core.ts +6 -4
  52. package/src/lang/dialect.test.ts +63 -0
  53. package/src/lang/dialect.ts +50 -0
  54. package/src/lang/docs.test.ts +0 -1
  55. package/src/lang/emitters/ast.ts +145 -2
  56. package/src/lang/emitters/from-ts.ts +1 -1
  57. package/src/lang/emitters/js-tests.ts +46 -37
  58. package/src/lang/emitters/js.ts +19 -3
  59. package/src/lang/features.test.ts +6 -5
  60. package/src/lang/index.ts +18 -5
  61. package/src/lang/linter.ts +1 -1
  62. package/src/lang/module-loader.test.ts +9 -5
  63. package/src/lang/module-loader.ts +4 -5
  64. package/src/lang/parser-params.ts +1 -1
  65. package/src/lang/parser-transforms.ts +12 -18
  66. package/src/lang/parser-types.ts +17 -0
  67. package/src/lang/parser.test.ts +12 -6
  68. package/src/lang/parser.ts +113 -3
  69. package/src/lang/perf.test.ts +10 -4
  70. package/src/lang/runtime.ts +0 -1
  71. package/src/lang/subset-invariant.test.ts +90 -0
  72. package/src/lang/transpiler.ts +8 -0
  73. package/src/lang/types.ts +18 -0
  74. package/src/lang/wasm.test.ts +22 -16
  75. package/src/linalg/linalg.test.ts +16 -4
  76. package/src/linalg/vector-search.bench.test.ts +31 -10
  77. package/src/types/Type.ts +6 -6
  78. package/src/use-cases/asymmetric-client-server.test.ts +0 -2
  79. package/src/use-cases/batteries.test.ts +4 -0
  80. package/src/use-cases/client-server.test.ts +1 -1
  81. package/src/use-cases/local-helpers.test.ts +219 -0
  82. package/src/use-cases/timeout-overrides.test.ts +169 -0
  83. package/src/use-cases/unbundled-imports.test.ts +0 -1
  84. package/src/vm/atoms/batteries.ts +17 -3
  85. package/src/vm/runtime.ts +92 -7
  86. package/src/vm/vm.ts +50 -10
@@ -8,7 +8,7 @@ import type { SeqNode } from '../builder';
8
8
  import type { TranspileOptions, TranspileResult, FunctionSignature } from './types';
9
9
  import { type TJSTranspileResult, type TJSTranspileOptions } from './emitters/js';
10
10
  export * from './types';
11
- export { parse, preprocess, extractTDoc, validateSingleFunction, } from './parser';
11
+ export { parse, preprocess, extractTDoc, validateSingleFunction, extractFunctions, } from './parser';
12
12
  export { transformFunction } from './emitters/ast';
13
13
  /**
14
14
  * Transpile JavaScript source code to Agent99 AST
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Dialect resolution for file-based tooling.
3
+ *
4
+ * The subset invariant (see PRINCIPLES.md) says plain JavaScript must transpile
5
+ * through TJS without changing meaning, while native `.tjs` gets the full
6
+ * footgun-removal feature set. The unit of opt-in is the *dialect* — and for a
7
+ * file, the dialect is its extension. This is the one canonical mapping that
8
+ * CLIs, bundler plugins, module loaders, and doc systems should all share so
9
+ * `.js` is never silently "improved" into different semantics.
10
+ */
11
+ /** Source dialect understood by `tjs(src, { dialect })`. */
12
+ export type Dialect = 'js' | 'tjs';
13
+ /** How a file should be transpiled, based on its extension. */
14
+ export type SourceKind = 'js' | 'ts' | 'tjs';
15
+ /**
16
+ * Classify a file by extension into the source kind that determines its
17
+ * transpile path:
18
+ * - `'tjs'` → native TJS: `tjs(src)` (all modes ON — the better language)
19
+ * - `'js'` → plain JS: `tjs(src, { dialect: 'js' })` (semantics preserved)
20
+ * - `'ts'` → TypeScript: route through `fromTS` (TS → TJS → JS)
21
+ *
22
+ * Unknown extensions default to `'tjs'` (the native language). `.d.ts` is
23
+ * treated as TypeScript.
24
+ */
25
+ export declare function sourceKindForFilename(filename: string): SourceKind;
26
+ /**
27
+ * Map a filename to the `dialect` option for `tjs()`. `.js`/`.mjs`/`.cjs` ⇒
28
+ * `'js'` (plain JavaScript, semantics preserved); everything else ⇒ `'tjs'`
29
+ * (native TJS).
30
+ *
31
+ * NOTE: TypeScript files are NOT a `tjs()` dialect — transpile them with
32
+ * `fromTS` first. Use {@link sourceKindForFilename} when you need to tell
33
+ * `.ts` apart from `.js`/`.tjs`.
34
+ */
35
+ export declare function dialectForFilename(filename: string): Dialect;
@@ -9,7 +9,7 @@ import type { TransformContext, TranspileOptions, FunctionSignature, TranspileWa
9
9
  /**
10
10
  * Transform a function declaration into Agent99 AST
11
11
  */
12
- export declare function transformFunction(func: FunctionDeclaration, source: string, returnTypeAnnotation: string | undefined, options?: TranspileOptions, requiredParamsFromPreprocess?: Set<string>): {
12
+ export declare function transformFunction(func: FunctionDeclaration, source: string, returnTypeAnnotation: string | undefined, options?: TranspileOptions, requiredParamsFromPreprocess?: Set<string>, helpers?: Map<string, FunctionDeclaration>): {
13
13
  ast: BaseNode;
14
14
  signature: FunctionSignature;
15
15
  warnings: TranspileWarning[];
@@ -13,6 +13,13 @@ export interface TestResult {
13
13
  error?: string;
14
14
  /** Whether this was an implicit signature test */
15
15
  isSignatureTest?: boolean;
16
+ /**
17
+ * The test could not be *run* (e.g. it references a name TJS can't resolve at
18
+ * build time — like an AJS atom — or the harness couldn't execute the module).
19
+ * Inconclusive ≠ failed: it never blocks transpilation. Surfaced as a warning
20
+ * (e.g. in the playground). See PRINCIPLES.md ("more, never illegal").
21
+ */
22
+ inconclusive?: boolean;
16
23
  /** Source line number (1-indexed) where the test or error occurred */
17
24
  line?: number;
18
25
  /** Source column number (1-indexed) */
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * TJS WASM Bootstrap Generation
3
3
  *
4
- * Compiles inline WASM blocks and generates JavaScript bootstrap code.
4
+ * Compiles the file's WASM blocks into a single WebAssembly.Module with
5
+ * one exported function per block, then emits JavaScript that compiles
6
+ * and instantiates the module once at startup. This is the foundation
7
+ * for cross-file wasm composition (see wasm-library-plan.md, Phase 3) —
8
+ * once everything's in one module, intra-module calls cost nothing.
5
9
  */
6
10
  import type { WasmBlock } from '../parser';
7
11
  export declare function generateWasmBootstrap(blocks: WasmBlock[]): {
@@ -54,6 +54,18 @@ export interface TJSTranspileOptions {
54
54
  sourceMap?: boolean;
55
55
  /** Mode: 'dev' | 'strict' | 'production' */
56
56
  mode?: 'dev' | 'strict' | 'production';
57
+ /**
58
+ * Source dialect — what kind of source this string is:
59
+ * - `'tjs'` (default for a bare string): native TJS, footgun-removal modes ON
60
+ * (structural `==`, `TjsStandard`, etc.).
61
+ * - `'js'`: plain JavaScript — modes OFF, `safety: 'none'`; the source's own
62
+ * semantics are preserved (no `==`→`Eq`, no truthiness rewrite, no input
63
+ * validation). Use this when feeding tjs() a vanilla `.js` string so it
64
+ * transpiles without changing meaning. See PRINCIPLES.md (TJS ⊇ JS).
65
+ *
66
+ * For TypeScript, use the `fromTS` entry point (TS → TJS → JS).
67
+ */
68
+ dialect?: 'js' | 'tjs';
57
69
  /**
58
70
  * Test execution mode:
59
71
  * - true (default): run tests at transpile time, throw on failure
@@ -74,6 +86,15 @@ export interface TJSTranspileOptions {
74
86
  * Used when tests depend on imported modules.
75
87
  */
76
88
  resolvedImports?: Record<string, string>;
89
+ /**
90
+ * Optional ModuleLoader for cross-file `wasm function` composition (Phase 3
91
+ * of the wasm-library plan). When provided, `import { dot } from
92
+ * 'tjs-lang/linalg'`-style imports are resolved at transpile time and any
93
+ * matching `wasm function` declarations are composed into this file's
94
+ * consolidated WebAssembly.Module. When omitted, imports are preserved
95
+ * verbatim (default behavior — runtime resolves them).
96
+ */
97
+ moduleLoader?: any;
77
98
  }
78
99
  /** Result of running tests at transpile time */
79
100
  export type { TestResult } from './js-tests';
@@ -23,7 +23,8 @@
23
23
  import type { SeqNode } from '../builder';
24
24
  import type { TranspileOptions, TranspileResult, FunctionSignature } from './types';
25
25
  export * from './types';
26
- export { parse, preprocess, extractTDoc } from './parser';
26
+ export { parse, preprocess, extractTDoc, validateSingleFunction, extractFunctions, } from './parser';
27
+ export { dialectForFilename, sourceKindForFilename, type Dialect, type SourceKind, } from './dialect';
27
28
  export { transformFunction } from './emitters/ast';
28
29
  export { transpileToJS, stripModuleSyntax, stripTjsPreamble, type TJSTranspileOptions, type TJSTranspileResult, type TJSTypeInfo, } from './emitters/js';
29
30
  export { generateDTS, typeDescriptorToTS, type GenerateDTSOptions, } from './emitters/dts';
@@ -34,6 +35,7 @@ export { typeDescriptorToJSONSchema, exampleToJSONSchema, functionMetaToJSONSche
34
35
  export { MetadataCache, getGlobalCache, setGlobalCache } from './metadata-cache';
35
36
  export { lint, type LintResult, type LintDiagnostic, type LintOptions, } from './linter';
36
37
  export { generateDocs, generateDocsMarkdown, type DocResult, type DocItem, type FunctionTypeInfo, type ParamTypeInfo, } from './docs';
38
+ export { ModuleLoader, inMemoryFileSystem, type ModuleLoaderOptions, type FileSystem, type LoadedModule, type ImportEntry, type ExportEntry, } from './module-loader';
37
39
  export { extractTests, assertFunction, expectFunction, testUtils, type ExtractedTest, type ExtractedMock, type TestExtractionResult, } from './tests';
38
40
  export { runtime, installRuntime, isError, error, typeOf, checkType, validateArgs, wrap, emitRuntimeWrapper, TJS_VERSION, type TJSError, } from './runtime';
39
41
  export { compileToWasm, instantiateWasm, registerWasmBlock, compileWasmBlocks, type WasmCompileResult, } from './wasm';
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Transpile-time module loader.
3
+ *
4
+ * Until now the transpiler has preserved import statements verbatim — runtime
5
+ * resolvers (the bun plugin, the playground service worker, the browser ESM
6
+ * loader) handle them on demand. That's the right default for regular JS
7
+ * interop, but Phase 3 of the wasm-library plan needs *static* visibility into
8
+ * imported sources: given `import { dot } from 'tjs-lang/linalg'`, we have to
9
+ * read linalg's source at transpile time so cross-file `wasm function`
10
+ * declarations can be composed into the consumer's wasm module.
11
+ *
12
+ * This loader is the foundation for that work. It's also useful as a building
13
+ * block for any future cross-file static analysis (type flow, dead code, etc.)
14
+ * — hence the name "module loader" rather than "wasm import resolver."
15
+ *
16
+ * Usage:
17
+ * const loader = new ModuleLoader({ baseDir: '/path/to/project' })
18
+ * const mod = loader.load('./math.tjs', '/path/to/project/app.tjs')
19
+ * if (mod) { console.log(mod.exports) }
20
+ *
21
+ * Resolution rules (in order):
22
+ * - URL specifiers (http://, https://, data:): not loadable, returns null
23
+ * - Relative paths (./foo, ../bar): resolved against importer's dir
24
+ * - Absolute paths (/foo/bar): used as-is
25
+ * - Bare specifiers (foo, foo/bar): walk up importer looking for
26
+ * node_modules/<spec>; also
27
+ * try bareSpecifierRoots
28
+ *
29
+ * For each candidate, we try extensions in order: `.tjs`, `.ts`, `.js`.
30
+ * (Index files: `<dir>/index.<ext>`.)
31
+ *
32
+ * The loader does NOT mutate transpiler behavior. It's an additive capability;
33
+ * Phase 3 (cross-file wasm composition) is the first caller.
34
+ */
35
+ import { parse as parseTjs } from './parser';
36
+ /** Pluggable filesystem hook. Default uses node:fs. */
37
+ export interface FileSystem {
38
+ /** Return source text for an absolute path, or null if it doesn't exist. */
39
+ readFile(path: string): string | null;
40
+ /** Return true if the path exists and is a file. */
41
+ exists(path: string): boolean;
42
+ }
43
+ export interface ModuleLoaderOptions {
44
+ /** Filesystem hook (default: node:fs based) */
45
+ fs?: FileSystem;
46
+ /** Where to resolve bare specifiers from when no importer is given. */
47
+ baseDir?: string;
48
+ /**
49
+ * Extra roots tried for bare specifiers BEFORE the standard node_modules
50
+ * walk. Useful for monorepos or tests that want to point at a virtual
51
+ * package directory. Each root is treated as if it were a `node_modules/`.
52
+ */
53
+ bareSpecifierRoots?: string[];
54
+ /** Cache size cap. 0 disables caching. (default 256) */
55
+ cacheLimit?: number;
56
+ }
57
+ /** A single import / re-export specifier extracted from the AST */
58
+ export interface ImportEntry {
59
+ /** Original module specifier (e.g. './math.tjs', 'tjs-lang/linalg') */
60
+ specifier: string;
61
+ /** Local name in the importing module */
62
+ local: string;
63
+ /** Imported name in the source module ('default' for default imports) */
64
+ imported: string;
65
+ /** True if this came from `import * as X` */
66
+ namespace: boolean;
67
+ }
68
+ /** A top-level export from a module */
69
+ export interface ExportEntry {
70
+ /** Exported name (or 'default' for default exports) */
71
+ name: string;
72
+ /** Kind of declaration being exported */
73
+ kind: 'function' | 'class' | 'variable' | 're-export' | 'unknown';
74
+ /** For re-exports, the source specifier */
75
+ fromSpecifier?: string;
76
+ }
77
+ export interface LoadedModule {
78
+ /** Resolved absolute path */
79
+ path: string;
80
+ /** Original source text */
81
+ source: string;
82
+ /** AST + tjs preprocessing output (lazy — only computed once per module) */
83
+ parseResult: ReturnType<typeof parseTjs>;
84
+ /** Imports declared by this module */
85
+ imports: ImportEntry[];
86
+ /** Top-level exports */
87
+ exports: ExportEntry[];
88
+ }
89
+ export declare class ModuleLoader {
90
+ private cache;
91
+ private fs;
92
+ private baseDir;
93
+ private bareSpecifierRoots;
94
+ private cacheLimit;
95
+ constructor(options?: ModuleLoaderOptions);
96
+ /**
97
+ * Resolve a specifier to an absolute path. Returns null when the specifier
98
+ * is not loadable as a local TJS/TS/JS file (URLs, missing files, unknown
99
+ * bare specifiers all return null — the caller falls back to verbatim
100
+ * import preservation).
101
+ */
102
+ resolve(specifier: string, importerPath?: string): string | null;
103
+ /**
104
+ * Load a module by specifier. Returns null if not resolvable (caller treats
105
+ * this as "leave the import statement alone").
106
+ *
107
+ * Repeated calls with the same specifier+importer combination hit the cache.
108
+ */
109
+ load(specifier: string, importerPath?: string): LoadedModule | null;
110
+ /** Drop all cached modules. */
111
+ clearCache(): void;
112
+ /** Try each supported extension; return the first existing path or null. */
113
+ private tryExtensions;
114
+ /**
115
+ * Resolve a bare specifier (e.g. `tjs-lang/linalg`, `lodash`) by walking
116
+ * up the importer's directory tree looking for `node_modules/<spec>`.
117
+ * Also checks each configured `bareSpecifierRoots` entry first.
118
+ */
119
+ private resolveBare;
120
+ }
121
+ /**
122
+ * Helper for tests / advanced use: build a FileSystem from a plain
123
+ * `Map<string, string>`. Keys must be absolute paths.
124
+ */
125
+ export declare function inMemoryFileSystem(files: Map<string, string> | Record<string, string>): FileSystem;
@@ -32,6 +32,85 @@ export declare function extractWasmBlocks(source: string): {
32
32
  source: string;
33
33
  blocks: WasmBlock[];
34
34
  };
35
+ /**
36
+ * Extract top-level `wasm function NAME(params): RetType { body }` declarations.
37
+ *
38
+ * Unlike `extractWasmBlocks` (which finds `wasm { ... }` blocks nested inside
39
+ * regular tjs functions with auto-captured variables), this extractor finds
40
+ * top-level wasm function declarations with explicit parameters and an
41
+ * optional return type. The body is the wasm-subset source.
42
+ *
43
+ * Each declaration becomes a `WasmBlock` whose `captures` array holds the
44
+ * function's parameters with their type annotations (e.g. `['a: Float32Array',
45
+ * 'b: Float32Array', 'n: i32']`). The block ID is derived from the function
46
+ * name (`__tjs_wasm_<name>`), so the JS-side wrapper can reference it.
47
+ *
48
+ * The declaration is replaced in source with a regular JS function that
49
+ * forwards its args to the wasm export, preserving the `export` modifier
50
+ * if present:
51
+ *
52
+ * (export)? wasm function dot(a: Float32Array, b: Float32Array, n: f64): f64 {
53
+ * <wasm-source>
54
+ * }
55
+ *
56
+ * becomes:
57
+ *
58
+ * (export)? function dot(a, b, n) { return globalThis.__tjs_wasm_dot(a, b, n) }
59
+ *
60
+ * This runs BEFORE `extractWasmBlocks` so its output (a regular JS function
61
+ * wrapper) isn't disturbed by the inline-block scanner.
62
+ *
63
+ * Return-type note: the underlying wasm bytecode builder currently emits f64
64
+ * or void return types only. The declared `: RetType` annotation is parsed
65
+ * and preserved on the block, but not yet validated against the backend's
66
+ * capabilities — `: f64` and omitted-return work today; other types (`: i32`,
67
+ * `: f32`, `: v128`) will be supported when the bytecode builder grows
68
+ * per-function return-type encoding.
69
+ */
70
+ export declare function extractWasmFunctions(source: string): {
71
+ source: string;
72
+ blocks: WasmBlock[];
73
+ };
74
+ /**
75
+ * Phase 3: cross-file wasm-function composition.
76
+ *
77
+ * Scans the consumer's source for `import { name1, name2, ... } from 'spec'`
78
+ * statements. For each, resolves the spec via the supplied ModuleLoader; if
79
+ * the imported file is tjs/ts and one of the imported names corresponds to a
80
+ * `wasm function` declared there, the function's WasmBlock is pulled into the
81
+ * consumer's compilation and replaced in source by a local JS wrapper. The
82
+ * import statement is rewritten to remove the satisfied names (or removed
83
+ * entirely if every imported name was wasm-composed).
84
+ *
85
+ * This is the heart of the cross-file composition story: imported wasm
86
+ * functions become local functions in the consumer's single WebAssembly.Module
87
+ * (via the Phase 0.5 consolidated-module path). The library's transpiled .js
88
+ * is NOT involved — the source is consumed at transpile time.
89
+ *
90
+ * Caller is responsible for providing a configured ModuleLoader. When no
91
+ * loader is supplied (the common case before Phase 3 is fully wired up), this
92
+ * function returns the source unchanged with an empty blocks array.
93
+ *
94
+ * @param source the consumer's source (after extractWasmFunctions on its own
95
+ * wasm functions, before transformParenExpressions)
96
+ * @param options.loader the ModuleLoader to resolve imports through
97
+ * @param options.importerPath the path of the file being transpiled (used as
98
+ * the resolver's importer context); optional
99
+ * @returns updated source + the list of imported wasm function blocks
100
+ */
101
+ export declare function composeImportedWasmFunctions(source: string, options: {
102
+ loader?: {
103
+ load(specifier: string, importerPath?: string): {
104
+ parseResult: {
105
+ wasmBlocks: WasmBlock[];
106
+ };
107
+ } | null;
108
+ };
109
+ importerPath?: string;
110
+ }): {
111
+ source: string;
112
+ blocks: WasmBlock[];
113
+ };
35
114
  /**
36
115
  * Transform Is/IsNot infix operators to function calls
37
116
  *
@@ -14,6 +14,28 @@ export interface ParseOptions {
14
14
  * When true, skips == to Is() transformation since the VM handles == correctly.
15
15
  */
16
16
  vmTarget?: boolean;
17
+ /**
18
+ * Source dialect — tells the transpiler what kind of source this string is:
19
+ * - `'tjs'`: native TJS, all footgun-removal modes ON (structural `==`,
20
+ * `TjsStandard`, etc.). This is the default for a bare string.
21
+ * - `'js'`: plain JavaScript — modes OFF and `safety: 'none'`, so the source's
22
+ * own semantics are preserved (no `==`→`Eq`, no truthiness rewrite, etc.).
23
+ *
24
+ * Authoritative when set; otherwise the dialect is inferred (the fromTS
25
+ * annotation and `vmTarget` ⇒ JS-compatible). See PRINCIPLES.md (TJS ⊇ JS).
26
+ */
27
+ dialect?: 'js' | 'tjs';
28
+ /**
29
+ * Optional ModuleLoader for cross-file `wasm function` composition (Phase 3).
30
+ * When provided, imports are resolved at transpile time and matching wasm
31
+ * functions are composed into the consumer's WebAssembly.Module. When
32
+ * omitted, imports are preserved verbatim (the default — runtime resolves
33
+ * them as before).
34
+ *
35
+ * Type is left as `any` here to avoid a circular import with module-loader.ts;
36
+ * callers should pass a `ModuleLoader` instance.
37
+ */
38
+ moduleLoader?: any;
17
39
  }
18
40
  /**
19
41
  * A WASM block extracted from source
@@ -35,6 +57,21 @@ export interface ParseOptions {
35
57
  export interface WasmBlock {
36
58
  /** Unique ID for this block */
37
59
  id: string;
60
+ /**
61
+ * Declared function name (only set for top-level `wasm function NAME(...)`
62
+ * declarations — Phase 1+). Used by Phase 3 cross-file composition to
63
+ * match an imported symbol against a wasm function declaration. Inline
64
+ * `wasm {}` blocks have no name and don't participate in composition.
65
+ */
66
+ name?: string;
67
+ /**
68
+ * Declared return-type annotation, e.g. `'f64'`. Only set for top-level
69
+ * `wasm function NAME(...): RetType` declarations; presence/absence is
70
+ * used to determine `hasReturn` BEFORE the body is compiled, so the
71
+ * function index map can be built up-front for wasm-to-wasm calls.
72
+ * Inline blocks have no declared return type.
73
+ */
74
+ returnType?: string;
38
75
  /** The body (JS subset that compiles to WASM, also used as fallback) */
39
76
  body: string;
40
77
  /** Explicit fallback body (only if different from body) */
@@ -79,6 +116,19 @@ export interface PreprocessOptions {
79
116
  * Default: false (transform == to Is() for TJS code running in regular JS)
80
117
  */
81
118
  vmTarget?: boolean;
119
+ /**
120
+ * Source dialect: `'js'` ⇒ modes OFF / `safety: 'none'` (preserve plain-JS
121
+ * semantics); `'tjs'` ⇒ native modes ON. Authoritative when set; otherwise
122
+ * inferred from the fromTS annotation / `vmTarget`. See ParseOptions.dialect.
123
+ */
124
+ dialect?: 'js' | 'tjs';
125
+ /**
126
+ * Optional ModuleLoader for cross-file `wasm function` composition (Phase 3).
127
+ * See ParseOptions.moduleLoader for details.
128
+ */
129
+ moduleLoader?: any;
130
+ /** Path of the file being preprocessed (used as importer context). */
131
+ filename?: string;
82
132
  }
83
133
  /**
84
134
  * Tokenizer state for tracking context during source transformation
@@ -53,6 +53,24 @@ export declare function parse(source: string, options?: ParseOptions): {
53
53
  * Validate that the source contains exactly one function declaration
54
54
  */
55
55
  export declare function validateSingleFunction(ast: Program, filename?: string): FunctionDeclaration;
56
+ /**
57
+ * Extract top-level function declarations from the parsed program.
58
+ *
59
+ * Returns `{ entry, helpers }` where:
60
+ * - `entry` is the last function declaration (the agent's entry point)
61
+ * - `helpers` are all preceding function declarations, looked up by name
62
+ *
63
+ * This matches the natural "helpers first, agent last" pattern, including
64
+ * the TOOL_LIBRARY use case where helper async wrappers are prepended
65
+ * before the user-supplied agent function.
66
+ *
67
+ * Same top-level construct restrictions as `validateSingleFunction`:
68
+ * imports, exports, and classes are rejected.
69
+ */
70
+ export declare function extractFunctions(ast: Program, filename?: string): {
71
+ entry: FunctionDeclaration;
72
+ helpers: Map<string, FunctionDeclaration>;
73
+ };
56
74
  /**
57
75
  * Extract TDoc comment from before a function
58
76
  *
@@ -11,6 +11,7 @@
11
11
  */
12
12
  export { transpile, ajs, tjs, createAgent, getToolDefinitions } from './core';
13
13
  export { parse, preprocess, extractTDoc } from './parser';
14
+ export { dialectForFilename, sourceKindForFilename, type Dialect, type SourceKind, } from './dialect';
14
15
  export { transformFunction } from './emitters/ast';
15
16
  export { transpileToJS } from './emitters/js';
16
17
  export type { TJSTranspileOptions, TJSTranspileResult, TJSTypeInfo, } from './emitters/js';
@@ -143,6 +143,24 @@ export interface TransformContext {
143
143
  filename: string;
144
144
  /** Options */
145
145
  options: TranspileOptions;
146
+ /**
147
+ * Helper functions (top-level functions declared before the entry function).
148
+ * Calls to these names emit `callLocal` instead of treating them as atom calls.
149
+ */
150
+ helpers?: Map<string, any>;
151
+ /**
152
+ * Cache of transformed helper bodies. Lazily populated as helpers are
153
+ * referenced from the entry function (or from other helpers).
154
+ */
155
+ helperSteps?: Map<string, {
156
+ steps: any[];
157
+ paramNames: string[];
158
+ }>;
159
+ /**
160
+ * Names of helpers currently being transformed. Used to detect direct or
161
+ * transitive recursion.
162
+ */
163
+ helperTransforming?: Set<string>;
146
164
  }
147
165
  /** Create a child context for nested scopes */
148
166
  export declare function createChildContext(parent: TransformContext): TransformContext;
@@ -15,6 +15,29 @@
15
15
  * audio processing, image manipulation, and physics simulations.
16
16
  */
17
17
  import type { WasmBlock } from './parser';
18
+ /** TJS type that maps to WASM */
19
+ type WasmValueType = 'i32' | 'i64' | 'f32' | 'f64' | 'v128';
20
+ /** Parameter with type annotation */
21
+ interface TypedParam {
22
+ name: string;
23
+ type: WasmValueType;
24
+ isArray?: boolean;
25
+ arrayType?: string;
26
+ }
27
+ /**
28
+ * Signature of a wasm function in the current module. Used to resolve
29
+ * cross-function calls (wasm-to-wasm `call <index>` instructions) without
30
+ * routing through JS. Populated by compileBlocksToModule before compiling
31
+ * any individual body, so forward references and mutual recursion work.
32
+ */
33
+ export interface ModuleFunctionSig {
34
+ /** Function index in the composed module */
35
+ index: number;
36
+ /** Parameter types (used for arg-type checking + auto-conversion) */
37
+ params: TypedParam[];
38
+ /** Whether this function returns a value (f64) or is void */
39
+ hasReturn: boolean;
40
+ }
18
41
  /** Compile result */
19
42
  export interface WasmCompileResult {
20
43
  /** The compiled WebAssembly module bytes */
@@ -31,9 +54,51 @@ export interface WasmCompileResult {
31
54
  wat?: string;
32
55
  }
33
56
  /**
34
- * Compile a WASM block to WebAssembly
57
+ * Compile a single WASM block to a complete WebAssembly module.
58
+ * The module exports a single function named `compute`.
35
59
  */
36
60
  export declare function compileToWasm(block: WasmBlock): WasmCompileResult;
61
+ /** Per-export metadata produced by compileBlocksToModule */
62
+ export interface BlockExport {
63
+ /** Original block ID (assigned by the parser) */
64
+ id: string;
65
+ /** Export name in the composed module (e.g. 'compute_0') */
66
+ exportName: string;
67
+ /** Capture annotations (preserved for runtime wrapper) */
68
+ captures: string[];
69
+ /** Whether this function reads/writes memory */
70
+ needsMemory: boolean;
71
+ /** WAT disassembly */
72
+ wat: string;
73
+ }
74
+ /** Result of composing multiple blocks into one module */
75
+ export interface MultiBlockCompileResult {
76
+ /** The composed module bytes, or empty if all blocks failed */
77
+ bytes: Uint8Array;
78
+ /** Per-block compile status (preserves input order) */
79
+ results: {
80
+ id: string;
81
+ success: boolean;
82
+ error?: string;
83
+ /** Index into `exports` (only when success === true) */
84
+ exportIndex?: number;
85
+ }[];
86
+ /** Successfully-compiled exports (in module-index order) */
87
+ exports: BlockExport[];
88
+ /** True if any included function needs memory */
89
+ needsMemory: boolean;
90
+ /** Aggregated warnings from all blocks */
91
+ warnings: string[];
92
+ }
93
+ /**
94
+ * Compile N WASM blocks into a single WebAssembly module with N exports.
95
+ * Failed blocks are skipped (their slot in `results` records the error)
96
+ * but do not abort compilation of the rest.
97
+ *
98
+ * Exports are named `compute_0`, `compute_1`, ... in input order, skipping
99
+ * indices that correspond to failed blocks.
100
+ */
101
+ export declare function compileBlocksToModule(blocks: WasmBlock[]): MultiBlockCompileResult;
37
102
  /**
38
103
  * Instantiate a compiled WASM module
39
104
  */
@@ -81,3 +146,4 @@ export declare function compileWasmBlocksForIframe(blocks: WasmBlock[]): Promise
81
146
  * This code should be injected into the iframe's script
82
147
  */
83
148
  export declare function generateWasmInstantiationCode(compiledBlocks: CompiledWasmData[]): string;
149
+ export {};
@@ -60,6 +60,8 @@ export interface TraceEvent {
60
60
  }
61
61
  /** Cost override: static number or dynamic function */
62
62
  export type CostOverride = number | ((input: any, ctx: RuntimeContext) => number);
63
+ /** Timeout override: static number (ms) or dynamic function. 0 disables. */
64
+ export type TimeoutOverride = number | ((input: any, ctx: RuntimeContext) => number);
63
65
  export interface RuntimeContext {
64
66
  fuel: {
65
67
  current: number;
@@ -76,8 +78,15 @@ export interface RuntimeContext {
76
78
  warnings?: string[];
77
79
  signal?: AbortSignal;
78
80
  costOverrides?: Record<string, CostOverride>;
81
+ timeoutOverrides?: Record<string, TimeoutOverride>;
79
82
  context?: Record<string, any>;
80
83
  runCodeDepth?: number;
84
+ localCall?: boolean;
85
+ helpers?: Record<string, {
86
+ steps: any[];
87
+ paramNames: string[];
88
+ }>;
89
+ callDepth?: number;
81
90
  }
82
91
  export type AtomExec = (step: any, ctx: RuntimeContext) => Promise<void>;
83
92
  export interface AtomDef {
@@ -218,6 +227,14 @@ export declare const varsImport: Atom<Record<string, any>, void>;
218
227
  export declare const varsLet: Atom<Record<string, any>, void>;
219
228
  export declare const varsExport: Atom<Record<string, any>, Record<string, any>>;
220
229
  export declare const scope: Atom<Record<string, any>, void>;
230
+ /**
231
+ * Maximum helper-call nesting depth. Fuel + timeout bound the total *work* a
232
+ * recursive helper can do, but deeply-nested `await seq.exec` would overflow
233
+ * the host JS stack (an uncatchable RangeError) before fuel runs out. This cap
234
+ * converts runaway recursion into a clean monadic error instead.
235
+ */
236
+ export declare const MAX_CALL_DEPTH = 256;
237
+ export declare const callLocal: Atom<Record<string, any>, any>;
221
238
  export declare const map: Atom<Record<string, any>, any[]>;
222
239
  export declare const filter: Atom<Record<string, any>, any[]>;
223
240
  export declare const reduce: Atom<Record<string, any>, any>;
@@ -281,6 +298,7 @@ export declare const coreAtoms: {
281
298
  varsLet: Atom<Record<string, any>, void>;
282
299
  varsExport: Atom<Record<string, any>, Record<string, any>>;
283
300
  scope: Atom<Record<string, any>, void>;
301
+ callLocal: Atom<Record<string, any>, any>;
284
302
  map: Atom<Record<string, any>, any[]>;
285
303
  filter: Atom<Record<string, any>, any[]>;
286
304
  reduce: Atom<Record<string, any>, any>;
@@ -1,8 +1,21 @@
1
- import { type Atom, type Capabilities, type RunResult, type CostOverride, coreAtoms } from './runtime';
1
+ import { type Atom, type Capabilities, type RunResult, type CostOverride, type TimeoutOverride, coreAtoms } from './runtime';
2
2
  import { type BaseNode, type BuilderType } from '../builder';
3
3
  export declare class AgentVM<M extends Record<string, Atom<any, any>>> {
4
4
  readonly atoms: typeof coreAtoms & M;
5
+ private _defaultRunTimeout?;
5
6
  constructor(customAtoms?: M);
7
+ /**
8
+ * Default run-level wall-clock timeout when `run()` is given no explicit
9
+ * `timeoutMs`. Derived as `max(per-atom timeoutMs) × 2` over the registered
10
+ * atoms (with headroom for an agent that chains a couple of slow calls), so
11
+ * the run-level backstop can never be shorter than the slowest single atom's
12
+ * own budget — otherwise that per-atom budget would be dead config (e.g.
13
+ * `llmVision`/`llmPredictBattery` are 120s; a fixed 60s run default would kill
14
+ * them mid-call). Atoms with `timeoutMs: 0` (no timeout, e.g. `seq`) are
15
+ * excluded; the result is floored at {@link MIN_DEFAULT_RUN_TIMEOUT_MS}.
16
+ * Self-adjusting: registering a slower custom atom raises the default.
17
+ */
18
+ get defaultRunTimeout(): number;
6
19
  get builder(): BuilderType<typeof coreAtoms & M>;
7
20
  get Agent(): BuilderType<typeof coreAtoms & M>;
8
21
  /** @deprecated Use `Agent` instead */
@@ -23,6 +36,7 @@ export declare class AgentVM<M extends Record<string, Atom<any, any>>> {
23
36
  timeoutMs?: number;
24
37
  signal?: AbortSignal;
25
38
  costOverrides?: Record<string, CostOverride>;
39
+ timeoutOverrides?: Record<string, TimeoutOverride>;
26
40
  context?: Record<string, any>;
27
41
  }): Promise<RunResult>;
28
42
  }