Releases: MesTTo/Meta-TypeScript-Talk
Release list
MeTTa TS 1.1.3
MeTTa TS 1.1.3
MeTTa TS 1.1.3 improves nondeterministic evaluation and bounds every automatic
table allocation. The default evaluator is faster than PeTTa on the four
reported nondeterministic workloads while preserving Hyperon semantics. There
is no benchmark mode, PeTTa mode, curry mode, or manually selected fast path.
Tested on Linux
This release is prepared on Linux with Node 22 and pnpm 11. The release gate
builds every package, typechecks the workspace, runs the full Vitest suite,
builds the GitHub Pages documentation, runs the Hyperon oracle, runs the scale
proof, and runs the benchmark suite before tagging.
The final suite passed 109 test files and 1,063 tests, with 38 optional live
integration tests skipped. The checked 270-assertion oracle passed all 23
corpus files.
The external Core/ST conformance result is unchanged from the untouched 1.1.2
baseline: 431 passed, 77 failed, 60 manifest expected failures, and 0 skipped.
The remaining failures are existing parser, directive, kernel, typing, and
stdlib contract differences. This release does not claim full specification
conformance.
Nondeterministic execution
The new checked benchmark keeps four query shapes reported by Patrick Hammer
as ordinary .metta files. Five-run subprocess medians include startup:
| Program | PeTTa | MeTTa TS | Speedup |
|---|---|---|---|
filtered matespacefast matches |
5738.1 ms | 3344.2 ms | 1.72x |
22^4 superpose cross product |
388.7 ms | 148.5 ms | 2.62x |
nondeterministic tabled fib(7) |
180.1 ms | 99.6 ms | 1.81x |
duplicate-heavy TupleConcat |
178.8 ms | 101.1 ms | 1.77x |
The harness validates 234,256 cross-product results, 196 distinct Fibonacci
answers, the exact TupleConcat sequence, and the embedded matespace
assertion. Run it with pnpm bench:nondeterminism.
A slot-based choice evaluator handles closed pure let, let*, superpose,
integer arithmetic, comparisons, if, and constructor tuples. Unsupported,
redefined, ill-typed, async, or executable-grounded forms stay on the normal
interpreter path. Result order and multiplicity are unchanged.
unique-atom(collapse(call)) can evaluate a supported static pure integer
recurrence as a first-seen answer set. Closed pure choice products also retain
first-seen answers as they emit instead of materializing a duplicate bag first.
An ordinary collapse(call) still returns its exact ordered bag with duplicate
derivations. Ground answer deduplication uses structural hashes with equality
checks instead of a quadratic scan.
Bounded automatic tabling
Automatic table admission remains conservative. The whole rule dependency
graph must be pure, the call key must be safe, and the recursive component must
branch back into itself at least twice. Linear recursion stays on the normal
compiled path.
The policy does not assume that every recursive program is safe to memoize and
does not let admitted tables grow until the process runs out of memory. It
combines the static overlap test with one global runtime budget. Exceeding the
active-state budget returns TableResourceLimit; it does not continue toward
an out-of-memory failure.
Completed and active tables now share these default ceilings:
- 50,000 entries
- 1,000,000 answers
- 1,000,000 retained atom cells
- 100,000 cells in one entry
- 250,000 interned leaves
Completed tables are removed in least-recently-used order. Active tables are
not evicted while their producer runs, so they return TableResourceLimit when
the shared budget cannot fit more state. The consumer-directed recurrence memo
uses the same entry, answer, cell, and per-entry limits. Interner generations
prevent stale tail-call keys from writing after a reset.
Direct active variant recursion still uses local-linear fixed-point completion.
Non-cyclic calls preserve exact ordered bags. The evaluator does not infer
Picat-style min or max answer subsumption.
Matching and scale
Ground runtime facts now have a nested argument-functor index. A pattern such
as (num (M $x)) selects the M bucket instead of scanning every num fact.
The matcher falls back to complete candidates when a non-ground fact could
unify.
A finite in-memory match whose result is discarded by a standard
let ... (empty) is removed before enumeration. The optimization declines for
custom grounded matchers, mutable state handles, changed standard forms, and
non-memory spaces.
The 30,000-fact scale gate also runs larger actual MeTTa workloads:
| Program | Checked result | Time |
|---|---|---|
| 24^4 pure choice product | 331,776 answers | 81.2 ms |
| duplicate tuple product | 50 values from 500,000 branches | 30.8 ms |
nondeterministic fib(10) |
2,817 distinct answers | 73.4 ms |
| nested runtime match | 30,000 answers | 647.3 ms |
Correctness fixes
superposein the choice evaluator now strips a collapsed bag's leading
comma marker. The recursivesupercollapsecorpus case remains empty as
Hyperon requires.- Choice planning now respects application type errors, expression-headed
rewrite rules, and replaced sync or async grounded operations. - Nested indexing no longer changes candidate enumeration when a pattern has
no nested-head constraint. - Active table entries and answers count against the same global resource
budget as completed entries. - The purity firewall now treats custom sync and async grounded operations as
effectful unless they are the unchanged implementation of a known-pure
built-in. File, catalog, random, time, output, fresh-identity, and host calls
cannot enter automatic tables transitively. - File handles can be closed immediately with
file-close!and are also closed
when their grounded atom is collected. Dictionary spaces and file records use
weak-key storage instead of lifetime-unbounded global maps. Grounded behavior
and non-default grounded types remain on the lossless atomspace path. - Grounded-operation registration invalidates evaluated terms, table analyses,
and compiled closures that may encode the previous dispatch behavior. - DAS gateway binding responses must contain exactly one MeTTa atom per value.
Malformed or multi-atom wire values now fail at the decode boundary. - Git imports pass an end-of-options marker before the repository path.
- The unused
streamEmit,tableBackchain, andtrieSpaceexperimental
options have been removed. They never selected an implementation.
Install
npm install @metta-ts/core@1.1.3
npm install -g @metta-ts/node@1.1.3Optional host packages use the same version:
npm install @metta-ts/py@1.1.3 pythonia
npm install @metta-ts/prolog@1.1.3Provenance
- Semantics: hyperon-experimental.
- Verified differential semantics: LeaTTa.
- Host compatibility: PeTTa-compatible Python and Prolog bridge forms where
they do not depend on PeTTa's evaluator. - License: MIT.
MeTTa TS 1.1.2
MeTTa TS 1.1.2
A pure-TypeScript implementation of MeTTa, the
OpenCog Hyperon language. The core engine runs in the browser, Node, Deno, Bun,
edge runtimes, and TypeScript-based agents with no native addon and no required
WASM. Optional host adapters can load Python or Prolog runtimes when a program
asks for them.
Tested on Linux
This release is prepared on Linux with Node 22 and pnpm 11. The release gate
builds every package, typechecks the workspace, runs the full Vitest suite,
builds the GitHub Pages documentation, runs the oracle checks, and runs the
benchmark suite before tagging. Optional Python, Pyodide, SWI-Prolog, and
SWI-WASM adapters are built and covered by their available tests, with live
runtime checks depending on the host tools installed in the release
environment.
The current conformance rerun reports Core/ST at 431 passed, 77 failed, 60
expected failures, and 0 skipped. Full/ST with fileio,json,random reports 470
passed, 174 failed, 4 expected failures, and 26 skipped. The remaining gaps are
the tracked parser/directive, kernel, typing, stdlib, feature, and concurrency
divergences, so this release should not be described as full Hyperon
conformance.
What's new
Integrated host interop release train
This release includes the full local host-interop train that followed 1.1.0:
browser host adapters, Python and Prolog runtime splits, SWI-WASM, Pyodide,
source runners, async effects, CLI checks, and the eDSL host builders.
Browser Python and Prolog
@metta-ts/browser/host composes optional host runtimes into the browser
runner. A browser app can now run the same .metta program shape as Node:
!(import! &self "math.py")
!(py-call (math.add 40 2))
!(import! &self "facts.pl")
!(prolog-call (edge alice $x))Python runs through @metta-ts/py/pyodide. Prolog runs through
@metta-ts/prolog/swi-wasm. The base @metta-ts/browser package stays
runtime-agnostic; Pyodide and SWI-WASM are only included when their adapter
subpaths are imported.
Prolog interop package
@metta-ts/prolog is now part of the release. The root package contains the
generic bridge and MeTTa-side helper source. Runtime adapters live on subpaths:
@metta-ts/prolog/swi-nodetalks to a localswiplexecutable.@metta-ts/prolog/swi-wasmruns throughswipl-wasm.
The supported surface follows PeTTa where the operation is a host Prolog bridge:
Predicate, callPredicate, assertaPredicate, assertzPredicate,
retractPredicate, prolog-call, prolog-consult, and
import_prolog_function.
MeTTa TS keeps Hyperon-style evaluation. There is no PeTTa mode and no curry
mode. Plain .pl imports and predicate calls are host capabilities, not a
second evaluator.
Runtime adapter split
The Python and Prolog roots no longer import runtime backends from their package
roots. Node-specific adapters are explicit subpaths:
@metta-ts/py/pythonia@metta-ts/prolog/swi-node
Browser adapters are explicit subpaths:
@metta-ts/py/pyodide@metta-ts/prolog/swi-wasm
That keeps default imports browser-clean and leaves optional dependencies behind
their adapter subpaths.
eDSL host helpers
@metta-ts/edsl/py and @metta-ts/edsl/prolog provide dependency-free builders
for the host interop forms. They build ordinary atoms such as py-call,
py-atom, prolog-call, Predicate, and import_prolog_function. They do
not load Python, Prolog, Pyodide, SWI-WASM, or Node adapters.
import { vars } from "@metta-ts/edsl";
import { pyCall } from "@metta-ts/edsl/py";
import { prologCall } from "@metta-ts/edsl/prolog";
const { x } = vars();
pyCall("math.add", 40, 2); // (py-call (math.add 40 2))
prologCall(["edge", "alice", x]); // (prolog-call (edge alice $x))Bounded structural tabling
Automatic tabling now uses structural token keys and a bounded table space
instead of recursive printed-form keys. Pure overlapping-recursive calls can be
memoized without table growth being unbounded:
- completed pure entries are capped and LRU-evictable;
- interner resets invalidate old opaque table keys;
- runtime rules are version-keyed;
- impure, meta, state, import,
match,collapse, and concurrency paths stay
outside the table cache; - direct active variant recursion uses local-linear completion for finite answer
sets; - non-cyclic calls keep exact ordered-bag memoization.
The stale-key path reported during release review is covered by a regression:
old tail-call pending keys cannot write into a new interner generation after a
table-space reset.
Install
npm install @metta-ts/core
npm install -g @metta-ts/node
npm install @metta-ts/py pythonia
npm install @metta-ts/prologBrowser projects that use optional host runtimes should also install the runtime
adapter they import:
npm install pyodide swipl-wasmProvenance
- Semantics: hyperon-experimental.
- Python interop surface: PeTTa's
py-calland Hyperon's
py-atom
family. - Prolog interop surface: PeTTa-compatible predicate bridge forms where they do
not depend on PeTTa's evaluator. - Verified spec and differential oracle:
LeaTTa. - License: MIT.
MeTTa TS 1.1.1
MeTTa TS 1.1.1
A pure-TypeScript implementation of MeTTa, the
OpenCog Hyperon language. The core engine runs in the browser, Node, Deno, Bun,
edge runtimes, and TypeScript-based agents with no native addon and no required
WASM. Optional host adapters can load Python or Prolog runtimes when a program
asks for them.
Tested on Linux
This release is prepared on Linux with Node 22 and pnpm 11. The release gate
builds every package, typechecks the workspace, runs the full Vitest suite,
builds the GitHub Pages documentation, runs the live Python, Pyodide,
SWI-Prolog, and SWI-WASM adapter checks, and runs the benchmark suite before
tagging.
The Hyperon experimental conformance run is unchanged from the previous
host-import slice. Core/ST reports 431 passed, 77 failed, 60 expected failures,
and 0 skipped. Full/ST with fileio,json,random reports 470 passed, 174 failed,
4 expected failures, and 26 skipped. The remaining gaps are the tracked
parser/directive, kernel, typing, stdlib, feature, and concurrency divergences,
so this release should not be described as full Hyperon conformance.
What's new
Browser Python and Prolog
@metta-ts/browser/host composes optional host runtimes into the browser
runner. A browser app can now run the same .metta program shape as Node:
!(import! &self "math.py")
!(py-call (math.add 40 2))
!(import! &self "facts.pl")
!(prolog-call (edge alice $x))Python runs through @metta-ts/py/pyodide. Prolog runs through
@metta-ts/prolog/swi-wasm. The base @metta-ts/browser package stays
runtime-agnostic; Pyodide and SWI-WASM are only included when their adapter
subpaths are imported.
Prolog interop package
@metta-ts/prolog is now part of the release. The root package contains the
generic bridge and MeTTa-side helper source. Runtime adapters live on subpaths:
@metta-ts/prolog/swi-nodetalks to a localswiplexecutable.@metta-ts/prolog/swi-wasmruns throughswipl-wasm.
The supported surface follows PeTTa where the operation is a host Prolog bridge:
Predicate, callPredicate, assertaPredicate, assertzPredicate,
retractPredicate, prolog-call, prolog-consult, and
import_prolog_function.
MeTTa TS keeps Hyperon-style evaluation. There is no PeTTa mode and no curry
mode. Plain .pl imports and predicate calls are host capabilities, not a
second evaluator.
Runtime adapter split
The Python and Prolog roots no longer import runtime backends from their package
roots. Node-specific adapters are explicit subpaths:
@metta-ts/py/pythonia@metta-ts/prolog/swi-node
Browser adapters are explicit subpaths:
@metta-ts/py/pyodide@metta-ts/prolog/swi-wasm
That keeps default imports browser-clean and leaves optional dependencies behind
their adapter subpaths.
eDSL host helpers
@metta-ts/edsl/py and @metta-ts/edsl/prolog provide dependency-free builders
for the host interop forms. They build ordinary atoms such as py-call,
py-atom, prolog-call, Predicate, and import_prolog_function. They do
not load Python, Prolog, Pyodide, SWI-WASM, or Node adapters.
import { vars } from "@metta-ts/edsl";
import { pyCall } from "@metta-ts/edsl/py";
import { prologCall } from "@metta-ts/edsl/prolog";
const { x } = vars();
pyCall("math.add", 40, 2); // (py-call (math.add 40 2))
prologCall(["edge", "alice", x]); // (prolog-call (edge alice $x))Install
npm install @metta-ts/core
npm install -g @metta-ts/node
npm install @metta-ts/py pythonia
npm install @metta-ts/prologBrowser projects that use optional host runtimes should also install the runtime
adapter they import:
npm install pyodide swipl-wasmProvenance
- Semantics: hyperon-experimental.
- Python interop surface: PeTTa's
py-calland Hyperon's
py-atom
family. - Prolog interop surface: PeTTa-compatible predicate bridge forms where they do
not depend on PeTTa's evaluator. - Verified spec and differential oracle:
LeaTTa. - License: MIT.
MeTTa TS 1.1.0
MeTTa TS 1.1.0
A pure-TypeScript implementation of MeTTa (Meta Type Talk), the OpenCog Hyperon language. It runs anywhere TypeScript runs: the browser, Node, Deno, Bun, and edge or serverless functions. No native addons, no WASM, no Rust.
Tested on Linux
This release is tested on Linux (Node 20, the CI matrix): lint, format, typecheck, the full test suite, and the build all run there. Because the engine is pure TypeScript with no native addon and no WASM, it is meant to be cross-platform and should run unchanged on any JavaScript runtime. Other operating systems are not yet part of the tested matrix.
What's new: Python interop
This release adds @metta-ts/py, an optional package that lets a MeTTa program call into Python. It carries two surfaces over one bridge: PeTTa's py-call and Hyperon's py-atom family.
py-call dispatches on the head of its argument, the way PeTTa does. A bare name is a builtin, a dotted name is a module function, and a leading-dot name is a method on a live object. py-eval runs a Python expression string and py-str folds a MeTTa list into a Python string:
!(py-call (math.gcd 12 18)) ; 6
!(py-eval "2 ** 10") ; 1024The py-atom family is Hyperon's surface over the same bridge. py-atom resolves a dotted path into an atom you can apply or read as a value, and py-dot, py-list, py-tuple, py-dict, and py-chain round it out:
!((py-atom operator.add) 40 2) ; 42
!(py-atom math.pi) ; 3.141592653589793Python runs in a separate CPython process and MeTTa talks to it over IPC, so the interpreter stays pure TypeScript and as fast as before. The package ships no Python dependency of its own: you pass in a bridge, the way MeTTaGrapher takes a GIF encoder. The reference bridge wraps pythonia. Because a call crosses a process boundary the ops are asynchronous, so you run with runAsync, or from the command line with metta-ts --py program.metta.
Value conversions follow PeTTa and its janus bridge: numbers both ways, a Python string to a Symbol, True/False/None to (@ true)/(@ false)/(@ none), a list to an expression, and anything else to a live handle. A raised Python error becomes an (Error <expr> <message>) atom carrying the real Python message, and evaluation continues, where PeTTa aborts. Enabling this grants the program the host's Python, so it is opt-in and meant for trusted source only.
Two differential oracles pin the behaviour. A byte-parity suite runs the same corpus through a live PeTTa checkout and through this package, comparing the result lines exactly. A second suite runs the py-atom surface through pip hyperon, comparing results on the numeric surface where the two marshallings coincide. Both are gated behind environment flags so the default suite needs no Python.
The one change to the engine is that a grounded atom's executor may now return a Promise, which is what lets an applied py-atom run asynchronously. Nothing returned a Promise from that path before, so the synchronous behaviour is unchanged: the 270-assertion Hyperon oracle is byte-identical and the corpus microbench is within noise of 1.0.9.
Corpus benchmark
The engine is unchanged for pure-MeTTa programs, so the PeTTa-corpus benchmark (107 shared programs, 97 both engines pass, median 2.01x, geomean 2.06x) is identical to 1.0.9. See packages/node/bench/RESULTS-corpus.md for the full per-program table.
Major performance gains (since 1.0.0)
The speed comes from general engine work:
- an O(1)-stack reduce-loop trampoline and worklist, so deep recursion does not grow the JS stack;
- deferred rule-RHS freshening with a head-shape candidate pre-filter;
- Prolog-style clause indexing by head functor and by every ground-leaf argument, so a keyed query over a 1,000,000-atom space resolves in about 0.2 to 1.4 ms;
- ground-atom type memoisation and an exact-match ground-fact index;
- automatic tabling of pure functions, including ones defined at runtime, and moded (variant) tabling for non-ground pure calls;
- a native-code compiler for the pure deterministic int/bool/tuple subset, with tail-recursion compiled to loops and higher-order specialisation;
- worker-thread parallelism:
(once (hyperpose ...))races branches across CPU cores on Node, and aSharedArrayBufferflat matcher scans large knowledge bases in parallel; - the compiled clause-skeleton and JavaScript-codegen search for match-free nondeterministic groups.
Every optimisation is verified byte-identical against the 270-assertion Hyperon oracle.
What is in this release
@metta-ts/coreis the interpreter, parser, type system, pattern matching, standard library, and static analyzer, as a single ESM bundle. It passes all 270 assertions of Hyperon's oracle corpus, cross-checked against LeaTTa, the machine-checked (Lean 4) MeTTa semantics pinned to the same commit.@metta-ts/hyperonis a TypeScript class API modeled on Python'shyperon, with a JavaScript interop layer (js-atom,js-dot,js-list,js-dict) that calls into the host runtime directly.@metta-ts/edslis a typed eDSL with term builders, special-form combinators, and a tagged-template surface.@metta-ts/pyis the new Python interop package, described above:py-calland thepy-atomfamily over a caller-supplied pythonia bridge, opt-in and asynchronous.@metta-ts/nodehas themetta-tsCLI, with--checkfor static analysis and--pyfor Python interop, plus fileimport!and the worker-thread parallel matcher.@metta-ts/browseris a browser entry with an in-memory virtual file system forimport!.@metta-ts/grapherrenders a MeTTa reduction as a node graph or a nested-block view, as static SVGs or an animated GIF, with a data-driven stylesheet for node size and colour.@metta-ts/das-clientand@metta-ts/das-gatewayare an optional client to SingularityNET's Distributed AtomSpace, run end to end against a live cluster, with atom handles matching the AtomDB byte for byte.
Install
npm install @metta-ts/core # the interpreter (works in any JS runtime)
npm install -g @metta-ts/node # the metta-ts CLI
npm install @metta-ts/py pythonia # optional: call Python from MeTTaRun a Python-using program from the command line:
metta-ts --py program.metta # needs pythonia installed and python3 on PATHProvenance
- Semantics: hyperon-experimental, pinned to commit
3f76dc4. - Python interop surface: PeTTa's
py-calland Hyperon'spy-atomfamily, over pythonia. - Verified spec and differential oracle: LeaTTa (Lean 4).
- Formal models: Alloy specs in
spec/for the matcher's deep loop rejection and the compiled search's occurs check. - License: MIT.
MeTTa TS 1.0.9
MeTTa TS 1.0.9
A pure-TypeScript implementation of MeTTa (Meta Type Talk), the OpenCog Hyperon language. It runs anywhere TypeScript runs: the browser, Node, Deno, Bun, and edge or serverless functions. No native addons, no WASM, no Rust.
Tested on Linux
This release is tested on Linux (Node 20, the CI matrix): lint, format, typecheck, the full test suite, and the build all run there. Because the engine is pure TypeScript with no native addon and no WASM, it is meant to be cross-platform and should run unchanged on any JavaScript runtime. Other operating systems are not yet part of the tested matrix.
What's new: a recovering CST for editors and language servers
1.0.8 added a span-tracking parse for the static analyzer. This release turns it into a concrete syntax tree an editor can build on. parseCst never throws, so a language server can keep offering features while a document is mid-edit: an unclosed ( closes at end of input, an unexpected ) and an unterminated string each become a diagnostic instead of an exception, and deep nesting is bounded without a recursive overflow. The tree also carries what an editor needs and the analyzer did not: the comments, a syntactic kind per node, the paren spans, and the span of a top-level ! query.
Leaf atoms still come from the interpreter's own reader primitives, so on valid input the CST is byte-identical to parseAll. A 1000-run differential checks the atoms and bang flags against the plain reader, and a 2000-run fuzz checks that the parser never throws on arbitrary input. The diagnostics are shaped as Language Server Protocol Diagnostics with a range, a severity, and a stable code, so an editor consumes them directly. The static analyzer and metta-ts --check from 1.0.8 are unchanged and still read the interpreter's own signature table:
error[arity-mismatch]: prog.metta:1:2
|
1 | !(car-atom 1 2)
| ^^^^^^^^^^^^^^ car-atom expects 1 argument, got 2
None of this touches the evaluator. parseCst is off the runFile hot path; the only change to shared code is that readStringAt now reports whether a string was terminated instead of throwing, and the plain parser re-throws exactly as before. The 270-assertion Hyperon oracle and every byte-identical experimental suite still pass, and a parse/eval microbench is within noise of 1.0.8.
Corpus benchmark
The engine is unchanged in this release, so the PeTTa-corpus benchmark (107 shared programs, 97 both engines pass, median 2.01x, geomean 2.06x) is identical to 1.0.7. See packages/node/bench/RESULTS-corpus.md for the full per-program table.
Major performance gains (since 1.0.0)
The speed comes from general engine work:
- an O(1)-stack reduce-loop trampoline and worklist, so deep recursion does not grow the JS stack;
- deferred rule-RHS freshening with a head-shape candidate pre-filter;
- Prolog-style clause indexing by head functor and by every ground-leaf argument, so a keyed query over a 1,000,000-atom space resolves in about 0.2 to 1.4 ms;
- ground-atom type memoisation and an exact-match ground-fact index;
- automatic tabling of pure functions, including ones defined at runtime, and moded (variant) tabling for non-ground pure calls;
- a native-code compiler for the pure deterministic int/bool/tuple subset, with tail-recursion compiled to loops and higher-order specialisation;
- worker-thread parallelism:
(once (hyperpose ...))races branches across CPU cores on Node, and aSharedArrayBufferflat matcher scans large knowledge bases in parallel; - the compiled clause-skeleton and JavaScript-codegen search for match-free nondeterministic groups, added in 1.0.7.
Every optimisation is verified byte-identical against the 270-assertion Hyperon oracle.
What is in this release
@metta-ts/coreis the interpreter, parser, type system, pattern matching, and standard library, as a single ESM bundle. It now also carries the static analyzer and its diagnostic model, described above. It passes all 270 assertions of Hyperon's oracle corpus (the full dependent-type tier, spaces and mutable state, nondeterminism, grounded operations, and documentation), cross-checked against LeaTTa, the machine-checked (Lean 4) MeTTa semantics pinned to the same commit.@metta-ts/hyperonis a TypeScript class API modeled on Python'shyperon, with a JavaScript interop layer (js-atom,js-dot,js-list,js-dict) that calls into the host runtime directly.@metta-ts/edslis a typed eDSL with term builders, special-form combinators, and a tagged-template surface.@metta-ts/nodehas themetta-tsCLI, now with--checkfor static analysis, plus fileimport!and the worker-thread parallel matcher.@metta-ts/browseris a browser entry with an in-memory virtual file system forimport!.@metta-ts/grapherrenders a MeTTa reduction as a node graph or a nested-block view, as static SVGs or an animated GIF, with a data-driven stylesheet for node size and colour.@metta-ts/das-clientand@metta-ts/das-gatewayare an optional client to SingularityNET's Distributed AtomSpace, run end to end against a live cluster, with atom handles matching the AtomDB byte for byte.
Install
npm install @metta-ts/core # the interpreter (works in any JS runtime)
npm install -g @metta-ts/node # the metta-ts CLICheck a file without running it:
metta-ts --check program.metta # arity errors, rustc-style
metta-ts --check --undefined-symbols program.metta # also "did you mean" on unknown heads
metta-ts --check --json program.metta # diagnostics as an LSP Diagnostic[]Provenance
- Semantics: hyperon-experimental, pinned to commit
3f76dc4. - Verified spec and differential oracle: LeaTTa (Lean 4).
- Formal models: Alloy specs in
spec/for the matcher's deep loop rejection and the compiled search's occurs check. - License: MIT.
MeTTa TS 1.0.8
MeTTa TS 1.0.8
A pure-TypeScript implementation of MeTTa (Meta Type Talk), the OpenCog Hyperon language. It runs anywhere TypeScript runs: the browser, Node, Deno, Bun, and edge or serverless functions. No native addons, no WASM, no Rust.
Tested on Linux
This release is tested on Linux (Node 20, the CI matrix): lint, format, typecheck, the full test suite, and the build all run there. Because the engine is pure TypeScript with no native addon and no WASM, it is meant to be cross-platform and should run unchanged on any JavaScript runtime. Other operating systems are not yet part of the tested matrix.
What's new: a static analyzer and metta-ts --check
This release adds a static analyzer that catches mistakes before a program runs and reports them the way a modern compiler does: the exact source span underlined, an error code, a message, and a suggested fix. It ships in @metta-ts/core as a library and on the metta-ts CLI as --check.
The analyzer reads the interpreter's own signature table, so it flags exactly the calls the interpreter itself would reject. A builtin called with the wrong number of arguments is checked against the same (-> ...) declaration the evaluator uses at run time, not against a second copy of the arity rules that could drift from it:
error[arity-mismatch]: prog.metta:1:2
|
1 | !(car-atom 1 2)
| ^^^^^^^^^^^^^^ car-atom expects 1 argument, got 2
An opt-in --undefined-symbols pass adds a "did you mean" on an unknown head, suggesting the closest defined name by edit distance. It is off by default because MeTTa's add-mode makes an unknown head legal: (foo 1 2) with no rule for foo is data added to the space, not a typo. With the flag on, (fibonaci 10) next to a defined fibonacci becomes a warning that carries the fix, never an error.
Precise spans come from a span-tracking parse that reuses the interpreter's own reader primitives, so the analyzer cannot disagree with the real parser about where a token starts or ends. --json emits the findings as a Language Server Protocol Diagnostic[], each with a range, a severity, a code, and suggestions that carry an applicability tier, so an editor or a language server can consume them directly.
None of this changes the evaluator. The only edit to existing run-time code is a refactor of the parser that exports the primitives the span parser reuses, and it is guarded by the parser's own tests. So the 270-assertion Hyperon oracle and the corpus benchmark below are byte-identical to 1.0.7, and the analyzer adds no dependency.
Corpus benchmark
The engine is unchanged in this release, so the PeTTa-corpus benchmark (107 shared programs, 97 both engines pass, median 2.01x, geomean 2.06x) is identical to 1.0.7. See packages/node/bench/RESULTS-corpus.md for the full per-program table.
Major performance gains (since 1.0.0)
The speed comes from general engine work:
- an O(1)-stack reduce-loop trampoline and worklist, so deep recursion does not grow the JS stack;
- deferred rule-RHS freshening with a head-shape candidate pre-filter;
- Prolog-style clause indexing by head functor and by every ground-leaf argument, so a keyed query over a 1,000,000-atom space resolves in about 0.2 to 1.4 ms;
- ground-atom type memoisation and an exact-match ground-fact index;
- automatic tabling of pure functions, including ones defined at runtime, and moded (variant) tabling for non-ground pure calls;
- a native-code compiler for the pure deterministic int/bool/tuple subset, with tail-recursion compiled to loops and higher-order specialisation;
- worker-thread parallelism:
(once (hyperpose ...))races branches across CPU cores on Node, and aSharedArrayBufferflat matcher scans large knowledge bases in parallel; - the compiled clause-skeleton and JavaScript-codegen search for match-free nondeterministic groups, added in 1.0.7.
Every optimisation is verified byte-identical against the 270-assertion Hyperon oracle.
What is in this release
@metta-ts/coreis the interpreter, parser, type system, pattern matching, and standard library, as a single ESM bundle. It now also carries the static analyzer and its diagnostic model, described above. It passes all 270 assertions of Hyperon's oracle corpus (the full dependent-type tier, spaces and mutable state, nondeterminism, grounded operations, and documentation), cross-checked against LeaTTa, the machine-checked (Lean 4) MeTTa semantics pinned to the same commit.@metta-ts/hyperonis a TypeScript class API modeled on Python'shyperon, with a JavaScript interop layer (js-atom,js-dot,js-list,js-dict) that calls into the host runtime directly.@metta-ts/edslis a typed eDSL with term builders, special-form combinators, and a tagged-template surface.@metta-ts/nodehas themetta-tsCLI, now with--checkfor static analysis, plus fileimport!and the worker-thread parallel matcher.@metta-ts/browseris a browser entry with an in-memory virtual file system forimport!.@metta-ts/grapherrenders a MeTTa reduction as a node graph or a nested-block view, as static SVGs or an animated GIF, with a data-driven stylesheet for node size and colour.@metta-ts/das-clientand@metta-ts/das-gatewayare an optional client to SingularityNET's Distributed AtomSpace, run end to end against a live cluster, with atom handles matching the AtomDB byte for byte.
Install
npm install @metta-ts/core # the interpreter (works in any JS runtime)
npm install -g @metta-ts/node # the metta-ts CLICheck a file without running it:
metta-ts --check program.metta # arity errors, rustc-style
metta-ts --check --undefined-symbols program.metta # also "did you mean" on unknown heads
metta-ts --check --json program.metta # diagnostics as an LSP Diagnostic[]Provenance
- Semantics: hyperon-experimental, pinned to commit
3f76dc4. - Verified spec and differential oracle: LeaTTa (Lean 4).
- Formal models: Alloy specs in
spec/for the matcher's deep loop rejection and the compiled search's occurs check. - License: MIT.
MeTTa TS 1.0.7
MeTTa TS 1.0.7
A pure-TypeScript implementation of MeTTa (Meta Type Talk), the OpenCog Hyperon language. It runs anywhere TypeScript runs: the browser, Node, Deno, Bun, and edge or serverless functions. No native addons, no WASM, no Rust.
Tested on Linux
This release is tested on Linux (Node 20, the CI matrix): lint, format, typecheck, the full test suite, and the build all run there. Because the engine is pure TypeScript with no native addon and no WASM, it is meant to be cross-platform and should run unchanged on any JavaScript runtime. Other operating systems are not yet part of the tested matrix.
What's new: the proof-size-bounded backward chainer, from losing to winning
Nil Geisweiller's bfc-xp benchmark searches a Łukasiewicz propositional calculus for a proof of a target formula under a fixed size bound, backtracking through modus ponens and three axiom schemes. It is a real nondeterministic search, not a lookup, and it was the one place PeTTa still beat MeTTa TS. This release closes that gap by compiling the search itself, not just the terms it searches over.
A match-free nondeterministic group (no clause queries a space, only recursion, if-guards, and integer arithmetic) now compiles in two steps. First, every clause becomes a skeleton: a tree of constant subtrees, clause-variable slots, and structured nodes, computed once per group rather than copied per call. Second, the skeleton compiles to specialized JavaScript, one function per functor, generated once via new Function and shared by every run: head unification becomes read/write-mode code that fails at the first mismatch with no allocation, and body arguments and templates become direct constructor expressions with integer arithmetic unboxed. Underneath both steps, search variables are cell variables: a binding lives in a mutable slot on the variable itself, so dereferencing is pointer-chasing and undoing a failed branch is popping a trail array, no string-keyed map anywhere. Every bind carries an occurs check, so a would-be cyclic binding fails the search instead of looping, exactly the discipline hasLoop enforces on the interpreter's own immutable bindings and SWI-Prolog enforces under occurs_check(true). A model in spec/loop_reject.als proves the two mechanisms reject exactly the same binding sets, in every possible bind order. An environment that forbids dynamic code (a CSP without unsafe-eval) falls back to running the skeleton directly, still far faster than the plain interpreter; a group that does query a space (like nilbc, MeTTa TS's dependently-typed backward chainer) is unaffected and keeps running on the original interpreter-backed search.
The result, measured with hyperfine (mean ± σ, wall clock, engine startup included, each row from one hyperfine invocation so the two columns are directly comparable):
| benchmark | MeTTa TS | PeTTa |
|---|---|---|
jarr (size 13) |
124.8 ms ± 4.2 ms | 182.9 ms ± 10.6 ms |
pm2.27 (size 13) |
121.6 ms ± 1.3 ms | 183.0 ms ± 8.3 ms |
imim1 (size 15) |
152.2 ms ± 4.1 ms | 212.3 ms ± 3.9 ms |
jarr (size 17), PeTTa with occurs_check(true) |
455.5 ms ± 69.5 ms | 476.0 ms ± 11.8 ms |
loowoz (size 19), PeTTa with occurs_check(true) |
2.092 s ± 0.063 s | 2.501 s ± 0.003 s |
The last two rows run PeTTa with occurs_check(true), which its SWI-Prolog translation does not set (the flag defaults to off). At these two deeper searches that gap is not just a speed difference: PeTTa as shipped finds 94 answers for jarr at size 17 where only 91 exist, and 44 for loowoz at size 19 where only 3 exist, the surplus being cyclic-binding artifacts that occurs_check is exactly the guard against (the same requirement bfc-xp's own SWI harness documents). Run correctly, PeTTa is a little slower here too. Every MeTTa TS answer above is checked byte-identical to the plain interpreter by a differential oracle that runs the search both ways (packages/core/src/moded-tabling.test.ts), and the compiled and interpreted engines' outputs are additionally diffed directly at sizes 17 and 19.
Corpus benchmark
The existing PeTTa-corpus benchmark (107 shared programs, 97 both engines pass, median 2.01x, geomean 2.06x) is unaffected by this release: none of those programs exercise a match-free nondeterministic group, so they run the same code as before. See packages/node/bench/RESULTS-corpus.md for the full per-program table.
Major performance gains (since 1.0.0)
The speed comes from general engine work:
- an O(1)-stack reduce-loop trampoline and worklist, so deep recursion does not grow the JS stack;
- deferred rule-RHS freshening with a head-shape candidate pre-filter;
- Prolog-style clause indexing by head functor and by every ground-leaf argument, so a keyed query over a 1,000,000-atom space resolves in about 0.2 to 1.4 ms;
- ground-atom type memoisation and an exact-match ground-fact index;
- automatic tabling of pure functions, including ones defined at runtime, and moded (variant) tabling for non-ground pure calls;
- a native-code compiler for the pure deterministic int/bool/tuple subset, with tail-recursion compiled to loops and higher-order specialisation;
- worker-thread parallelism:
(once (hyperpose ...))races branches across CPU cores on Node, and aSharedArrayBufferflat matcher scans large knowledge bases in parallel; - the compiled clause-skeleton and JavaScript-codegen search described above, for match-free nondeterministic groups.
Every optimisation is verified byte-identical against the 270-assertion Hyperon oracle.
What is in this release
@metta-ts/coreis the interpreter, parser, type system, pattern matching, and standard library, as a single ESM bundle. It passes all 270 assertions of Hyperon's oracle corpus (the full dependent-type tier, spaces and mutable state, nondeterminism, grounded operations, and documentation), cross-checked against LeaTTa, the machine-checked (Lean 4) MeTTa semantics pinned to the same commit.@metta-ts/hyperonis a TypeScript class API modeled on Python'shyperon, with a JavaScript interop layer (js-atom,js-dot,js-list,js-dict) that calls into the host runtime directly.@metta-ts/edslis a typed eDSL with term builders, special-form combinators, and a tagged-template surface.@metta-ts/nodehas themetta-tsCLI, fileimport!, and the worker-thread parallel matcher.@metta-ts/browseris a browser entry with an in-memory virtual file system forimport!.@metta-ts/grapherrenders a MeTTa reduction as a node graph or a nested-block view, as static SVGs or an animated GIF, with a data-driven stylesheet for node size and colour.@metta-ts/das-clientand@metta-ts/das-gatewayare an optional client to SingularityNET's Distributed AtomSpace, run end to end against a live cluster, with atom handles matching the AtomDB byte for byte.
Install
npm install @metta-ts/core # the interpreter (works in any JS runtime)
npm install -g @metta-ts/node # the metta-ts CLIProvenance
- Semantics: hyperon-experimental, pinned to commit
3f76dc4. - Verified spec and differential oracle: LeaTTa (Lean 4).
- Formal models: Alloy specs in
spec/for the matcher's deep loop rejection and the compiled search's occurs check. - License: MIT.
MeTTa TS 1.0.6
This release adds @metta-ts/grapher, a visual, structural editor for MeTTa that runs entirely in the browser.
@metta-ts/grapher
MeTTaGrapher draws a program two ways and runs it on the same interpreter as the rest of the packages: a node graph of connected boxes, and nested blocks where a form contains its arguments. Both are the same MeTTa atom, so anything that produces atoms feeds it, including the eDSL.
It is a structural editor: you never type into a free-form text box, you select a term and act on it, so the program is always a valid tree. In the blocks view the arrow keys walk the cursor through the tree, typing on a leaf re-parses it back into an atom, and the source panel stays in step. In the graph view you add nodes and drag a node's top port onto another to connect it as a child, drag it onto empty space to detach, and delete with the keyboard. An illegal edge (a self-loop, a duplicate, or a cycle) is rejected, with the connect line green over a node it can legally join and red over one it cannot.
Watch a query reduce. Press Play and the tree folds up to its answer one real reduction at a time, morphing from each state into the next. When a rule fires you see the substitution: the rule body appears with its variables as hollow slots that fill in with the values they matched, read from the engine so a literal in the rule is never mistaken for a variable. A soft glow marks the subterm reducing, an operation morphs into its result, a step's consumed pieces coalesce into the result the way droplets merge, and a nondeterministic step fans out to show every branch. The playthrough matches the interpreter: case and let* keep their structural arguments unevaluated so a case-based recursion terminates, and where a single step cannot advance a query the trace reconciles its endpoint with full evaluation, so Play ends where Run does.
Export a GIF of the reduction: the block view, the node graph, or both side by side. The frame math is pure, so a reduction GIF can be produced from the command line as well as the browser.
Drive the picture from MeTTa through an isolated &grapher space: (color T C), (highlight T), (focus T), (label T text), and (background C), reachable from MeTTa, TypeScript, or the eDSL.
Every runnable example on the docs site now has a Visualize button that opens it in the editor. There is a hands-on walkthrough at https://mestto.github.io/Meta-TypeScript-Talk/tools/grapher.
Install
npm install @metta-ts/grapherAll eight @metta-ts/* packages are published at 1.0.6 with npm provenance. The 270-assertion Hyperon oracle corpus stays byte-identical.
MeTTa TS 1.0.5: ergonomic typed eDSL
An ergonomics release for the typed TypeScript eDSL, @metta-ts/edsl. The engine is unchanged and stays faster than PeTTa on all 97 shared corpus programs; this release is about writing MeTTa from TypeScript with far less ceremony and much stronger types.
eDSL redesign
The old surface (S, v("x"), rel("name"), iff/matchSelf) is replaced. No name is written twice anymore.
- Proxy-minted names and variables.
const { Likes, fact, Ada } = names()andconst { x, thing } = vars(). A bare name grounds to its symbol; a called name applies it, soLikes(Ada, thing)builds(Likes Ada thing). The JS binding is the name. - Capitalized special forms:
If,Case,Let,LetStar,Match,Superpose,Collapse,Empty,Unify,Sealed,Quote, with the grounded ops staying lowercase. - A two-way host bridge.
db.fn/db.fns/db.asyncFnregister plain typed TypeScript functions with arguments auto-unwrapped and the result auto-grounded.db.call.fact(5)anddb.import("fact")call MeTTa functions back from TypeScript.
New in this release
- Optional typed schema.
mettaDB<{ fact: (n: number) => number }>()typescall,import, andfnfrom a declared schema, sodb.call.fact(5)isnumber[],db.import("fact")is(n: number) => number | undefined, anddb.fnis checked against the signature. With no schema the surface stays permissive. - Typed source queries.
db.q("(Likes Ada $thing)")returns rows typed by the pattern's variables,{ thing: unknown }[], with the keys extracted at compile time. A key that is not a variable in the source is a compile error. It types the variable structure, not the values, which come from runtime rewriting. - JSON and dict-spaces.
db.useJson()enables the module, andjsonEncode/jsonDecode/dictSpace/getKeys/getValuebridge JSON and MeTTa spaces, so a decoded JSON object becomes a queryable space.
The m tagged template and raw run surfaces remain, and every builder still produces an ordinary atom that runs on the same faithful engine, verified against the 270-assertion Hyperon oracle and the LeaTTa Lean spec.
Packages
Published to npm with provenance: @metta-ts/core, @metta-ts/node, @metta-ts/browser, @metta-ts/hyperon, @metta-ts/das-client, @metta-ts/das-gateway, @metta-ts/edsl.
MeTTa TS 1.0.4: faster than PeTTa on all 97
The parity release: MeTTa TS is now faster than PeTTa on all 97 shared corpus programs, median about 2×, from pure TypeScript. Verified against the 270-assertion Hyperon oracle and the LeaTTa Lean spec throughout.
Highlights
- Compiled nondeterministic search. A multi-equation function whose clause bodies chain space matches and recursive calls (the backward-chainer class) compiles to a clause-major depth-first collect-all search, the same fragment PeTTa hands to Prolog's clause alternatives. Head unification, solution destructuring, and space matching reuse the interpreter's own primitives, so semantics are inherited rather than reimplemented.
nilbcgoes from 2.2 s to 0.40 s, under PeTTa's 0.71 s. - Compiled add-atom saturation loops. The add-if-absent idiom compiles to one exact-membership probe plus append, and a single-branch
caseover a space match becomes a snapshot-and-thread loop with Empty-pruned branches.peano, the last trailing benchmark, goes from 2.7 s to 0.22 s, 7.7× under PeTTa's 1.69 s, byte-identical. - Identity-preserving evaluation. The reduce loop returns the input expression when every evaluated argument is unchanged instead of rebuilding an equal copy, which keeps the evaluated-mark and type caches hitting for any store that re-materialises canonical terms.
- Flat atomspace, fixed. The opt-in compact store's regressions are gone: a decode cache and an open-addressing intern table put
peanounder the flag at parity and cutmatespacefastto 1.6× the default's time at 3× less peak memory. It stays opt-in as a memory mode.
One documented nuance: the nondeterministic compiler's fresh variables are alpha-equivalent to the interpreter's rather than byte-equal (consistently renamed, deterministic run to run) — the equality the oracle and LeaTTa check. On the corpus exactly one printed line renames one variable.
Benchmark
On the PeTTa example corpus, Hyperon-faithful subset, both engines pass 97 shared programs and MeTTa TS is faster on every one: median 2.01×, geomean 2.06×, with peano 7.7×, fib 5.8×, tilepuzzle 3.9×, matespacefast 2.3×, nilbc 1.8×. matespace and matespace2 remain PeTTa-specific and excluded: run through hyperon-experimental itself they reduce to empty, so no Hyperon-faithful engine reproduces PeTTa's count. Full table in packages/node/bench/RESULTS-corpus.md.
Packages
Published to npm with provenance: @metta-ts/core, @metta-ts/node, @metta-ts/browser, @metta-ts/hyperon, @metta-ts/das-client, @metta-ts/das-gateway, @metta-ts/edsl.