myxo-lang 1.5.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/CONCURRENCY.md +207 -0
- package/HOW_IT_WORKS.md +235 -0
- package/INDEPENDENCE.md +24 -0
- package/MYXO_PROMPT.md +139 -0
- package/README.md +494 -0
- package/ROADMAP.md +200 -0
- package/SPEC.md +181 -0
- package/VISION.md +249 -0
- package/builtins.js +361 -0
- package/command-fence.js +75 -0
- package/errors.js +45 -0
- package/examples/agent.myx +31 -0
- package/examples/fenced-agent.js +78 -0
- package/examples/fib.myx +11 -0
- package/examples/fibers.myx +57 -0
- package/examples/flow-routing.myx +12 -0
- package/examples/geo.myx +12 -0
- package/examples/hello.myx +2 -0
- package/examples/host.js +30 -0
- package/examples/james-myxo-demo.js +60 -0
- package/examples/living-mesh.myx +22 -0
- package/examples/match.myx +18 -0
- package/examples/mathlib.js +8 -0
- package/examples/mathlib.pl +11 -0
- package/examples/mathlib.py +23 -0
- package/examples/mcp-host.js +37 -0
- package/examples/nexus-mesh.myx +22 -0
- package/examples/nexus.myx +29 -0
- package/examples/ouroboros.myx +2 -0
- package/examples/outward-gate.myx +23 -0
- package/examples/physarum.myx +75 -0
- package/examples/polyglot-host.js +17 -0
- package/examples/polyglot.myx +11 -0
- package/examples/resilient.myx +28 -0
- package/examples/scheduler.myx +46 -0
- package/examples/the-law.myx +27 -0
- package/examples/use-geo.myx +9 -0
- package/format.js +206 -0
- package/interpreter.js +1092 -0
- package/lexer.js +173 -0
- package/mcp-bridge.js +77 -0
- package/mcp-framing.js +34 -0
- package/mcp-server.js +57 -0
- package/myxo-concurrent.js +110 -0
- package/myxo-live-worker.js +30 -0
- package/myxo-live.js +83 -0
- package/myxo-lsp.js +226 -0
- package/myxo-par-worker.js +39 -0
- package/myxo-plan.js +207 -0
- package/myxo-run.js +63 -0
- package/myxo.js +296 -0
- package/package.json +27 -0
- package/parser.js +662 -0
- package/polyglot-host.js +39 -0
- package/polyglot.js +191 -0
- package/receipt.js +71 -0
- package/std.myx +88 -0
- package/tools/memo-fuzz.js +224 -0
package/README.md
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# Myxo — the language of the Nexus
|
|
2
|
+
|
|
3
|
+
**Myxo 1.5** · a complete, from-scratch, **zero-dependency** language — its own parser, interpreter, stdlib, pattern matching, gradual types, real multi-core concurrency, a capability fence, tests, formatter, REPL, language server, and CLI.
|
|
4
|
+
|
|
5
|
+
A small, standalone and embeddable programming language whose runtime **is** the Nexus law:
|
|
6
|
+
|
|
7
|
+
> *Agents do not merge. Useful pathways reinforce. Useless pathways decay.*
|
|
8
|
+
|
|
9
|
+
That isn't a tagline bolted on top — it's the semantics. Functions are `agent`s.
|
|
10
|
+
Variables are *pathways* that gain strength each time they're read. You `decay` the ones
|
|
11
|
+
that go quiet, and `prune` sweeps the weak. Myxo is written in plain Node with **zero
|
|
12
|
+
dependencies**, so it runs by itself from the CLI, runs anywhere the Nexus runs, and embeds
|
|
13
|
+
inside any Node program.
|
|
14
|
+
|
|
15
|
+
Myxo is not just glue over Python, C++, or JAMES. Today it is a standalone `.myx` language
|
|
16
|
+
implemented in Node, with its own syntax, parser, interpreter, stdlib, tests, module rules,
|
|
17
|
+
formatter, REPL, and CLI. Its independence is semantic and operational: a `.myx` program can
|
|
18
|
+
run without Python, C++, JAMES, or MCP. Python, Node, Perl, shell, MCP tools, and compiled
|
|
19
|
+
binaries are foreign capabilities Myxo can call through the same outer fence and audit ledger.
|
|
20
|
+
See [`INDEPENDENCE.md`](INDEPENDENCE.md) and [`HOW_IT_WORKS.md`](HOW_IT_WORKS.md).
|
|
21
|
+
|
|
22
|
+
```myx
|
|
23
|
+
agent fib(n) {
|
|
24
|
+
when n < 2 { report n }
|
|
25
|
+
report fib(n - 1) + fib(n - 2)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
seed i = 0
|
|
29
|
+
reinforce i < 10 {
|
|
30
|
+
emit fib(i)
|
|
31
|
+
i = i + 1
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Run it
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
node myxo.js examples/fib.myx # run a file
|
|
39
|
+
node myxo.js examples/the-law.myx --trace # run, then print the living mesh
|
|
40
|
+
node myxo.js # multi-line REPL (type an agent across lines)
|
|
41
|
+
npm test # full suite; optional runtimes are skipped if absent
|
|
42
|
+
npm run test:core # core language + formatter + MCP framing
|
|
43
|
+
npm run test:polyglot # Python/Node/Perl bridge checks
|
|
44
|
+
npm run test:myxo # Myxo testing ITSELF, in Myxo
|
|
45
|
+
node myxo.js test tests/ # run .myx test files (test "..." { expect X is Y / to fail })
|
|
46
|
+
node myxo.js fmt examples/fib.myx # print canonical parser-backed Myxo formatting
|
|
47
|
+
node myxo.js fmt file.myx --check # fail if file is not formatted
|
|
48
|
+
node myxo.js fmt file.myx --write # rewrite in place (comments preserved; --drop-comments to strip)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`myxo fmt` is a canonical AST printer that **preserves comments** — it collects them from the real lexer and
|
|
52
|
+
re-attaches them by line (statement-granular: a comment trailing a one-line block or inside an inline
|
|
53
|
+
`agent(){}` may shift to its own line, but none are dropped or fabricated). `--drop-comments` strips them.
|
|
54
|
+
|
|
55
|
+
## The words
|
|
56
|
+
|
|
57
|
+
Myxo keeps the building blocks but names them after the Nexus.
|
|
58
|
+
|
|
59
|
+
| You mean… | Myxo | Example |
|
|
60
|
+
|----------------------|-----------------|------------------------------------------|
|
|
61
|
+
| declare a value | `seed` | `seed power = 9` |
|
|
62
|
+
| change a value | `name = ...` | `power = power + 1` |
|
|
63
|
+
| remove a value | `decay` | `decay power` |
|
|
64
|
+
| print | `emit` | `emit "hi", power` |
|
|
65
|
+
| if / else | `when` / `otherwise` | `when x > 0 { } otherwise { }` |
|
|
66
|
+
| else-if chain | `otherwise when`| `when a {} otherwise when b {}` |
|
|
67
|
+
| while loop | `reinforce` | `reinforce x < 10 { x = x + 1 }` |
|
|
68
|
+
| count loop | `reinforce N times` | `reinforce 3 times { emit "tick" }` |
|
|
69
|
+
| iterate | `for each … in` | `for each x in [1,2,3] { emit x }` |
|
|
70
|
+
| function | `agent` | `agent greet(name) { report "hi " + name }` |
|
|
71
|
+
| anonymous function | `agent(...) {}` | `map(xs, agent(x){ report x * 2 })` |
|
|
72
|
+
| return | `report` | `report total` |
|
|
73
|
+
| true / false | `live` / `dead` | `when ready == live { }` |
|
|
74
|
+
| nothing | `void` | `seed best = void` |
|
|
75
|
+
| logic | `and or not` | `and`/`or` return the **value** — `x or 9` works |
|
|
76
|
+
| handle failure | `attempt` / `rescue` | `attempt { risky() } rescue err { }` |
|
|
77
|
+
| raise a failure | `fail` | `fail "no such pathway"` |
|
|
78
|
+
| import a strand | `weave` | `weave "math.myx"` (or `weave "m.myx" as m`) |
|
|
79
|
+
| export a name | `expose` | `expose area_circle` |
|
|
80
|
+
| declare capabilities | `needs` | `needs lookup, notify, spend(max 5)` |
|
|
81
|
+
|
|
82
|
+
**Values:** numbers, strings `"..."`, `live`/`dead`, `void`, lists `[1, 2, 3]`,
|
|
83
|
+
and a *mesh* (map) `{ "key": value }`. Index with `[]`; a missing mesh key reads as `void`;
|
|
84
|
+
negative list indexes count from the end. Strings interpolate embedded expressions with
|
|
85
|
+
`{ }` — `emit "strength of {key} is {m[key]}"` — and `\{` gives a literal brace.
|
|
86
|
+
|
|
87
|
+
## Pattern matching
|
|
88
|
+
|
|
89
|
+
`match` runs the first arm whose **shape** fits, binding its names:
|
|
90
|
+
|
|
91
|
+
```myx
|
|
92
|
+
match msg {
|
|
93
|
+
["move", x, y] { go(x, y) }
|
|
94
|
+
{ "kind": "ping" } { pong() }
|
|
95
|
+
[head, ...rest] { emit head, rest }
|
|
96
|
+
0 { idle() }
|
|
97
|
+
_ { unknown(msg) }
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Patterns: literals (`0`, `"hi"`, `live`/`dead`/`void`), `_` (wildcard), a name (binds the value), `[list]` with an optional `...rest`, and `{ "key": pattern }` (matches a *subset* of a mesh). Patterns nest. Bindings are **linear** (a name binds once per arm) and scoped to the arm. If no arm matches, nothing runs — add `_` for a catch-all.
|
|
102
|
+
|
|
103
|
+
## Pipelines & destructuring
|
|
104
|
+
|
|
105
|
+
`x | f` is `f(x)`; `x | f(a)` is `f(x, a)` (the piped value becomes the first argument). Pipes read top-to-bottom and chain:
|
|
106
|
+
|
|
107
|
+
```myx
|
|
108
|
+
seed top = scores | filter(passing) | sort | first
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Destructure a list or mesh in one `seed` (reuses the pattern shapes; a non-match errors):
|
|
112
|
+
|
|
113
|
+
```myx
|
|
114
|
+
seed [head, ...tail] = xs # head = xs[0], tail = the remaining list
|
|
115
|
+
seed { name, score } = player # binds the "name" and "score" fields
|
|
116
|
+
seed { "id": pid } = record # explicit key form
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Gradual types
|
|
120
|
+
|
|
121
|
+
Annotations are optional and **off by default** — a normal run stays fully dynamic. Pass `--strict` (and `myxo test` runs strict) to enforce them as runtime contracts:
|
|
122
|
+
|
|
123
|
+
```myx
|
|
124
|
+
seed n: number = 5
|
|
125
|
+
agent area(w: number, h: number = w): number {
|
|
126
|
+
report w * h
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Types: `number`, `string`, `bool`, `list`, `mesh`, `agent`, `void`, `any`. Under `--strict`, a violation at a **seed**, a **parameter**, a **return**, or a **reassignment** of a typed binding raises a clear error. Honest scope: this is runtime contract-checking at those boundaries — not static inference, and containers aren't element-typed (`list` means "any list"). `:` introduces a type; defaults use `=`.
|
|
131
|
+
|
|
132
|
+
## Concurrency: dispatch & gather
|
|
133
|
+
|
|
134
|
+
`dispatch f(x)` builds an **unstarted task**; `gather [...]` runs every task on its **own OS thread, in parallel**, blocks until all finish, and returns the results **in order**:
|
|
135
|
+
|
|
136
|
+
```myx
|
|
137
|
+
agent slow(n) { seed s = 0
|
|
138
|
+
for each i in range(n) { s = s + (i % 7) }
|
|
139
|
+
report s
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
emit gather [dispatch slow(8000000), dispatch slow(8000000), dispatch slow(8000000)]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`gather` is just an expression (its value is a list), so it composes: `gather tasks | sum`, `seed [a, b] = gather [...]`.
|
|
146
|
+
|
|
147
|
+
**Isolation is the safety model.** Each task runs in a *fresh* interpreter — it sees the stdlib, its own arguments, and itself (recursion), and **nothing else**. It can't read or mutate the parent's pathways, so there's no shared state to race on. The trade: values cross **by data** (`number`/`string`/`bool`/`list`/`mesh`/`void`) — handing a task an *agent*, or a non-finite number, is rejected at the boundary rather than silently mangled. A task that fails surfaces as a `gather:` error; a hung worker is reclaimed by a timeout. Zero new dependencies — same worker+`Atomics` bones as the live bridge. Full detail and the measured performance honesty in [`CONCURRENCY.md`](CONCURRENCY.md).
|
|
148
|
+
|
|
149
|
+
### The Physarum scheduler
|
|
150
|
+
|
|
151
|
+
Where `gather` runs a fixed list, `schedule(name, workers, items)` runs a **batch through a learning pool**. It's the reinforce/decay law applied to *execution*: a batch is distributed across a pool of interchangeable worker-agents proportional to **conductance**, run in parallel, and each worker's conductance is updated from its measured speed — so fast workers pull more flux on later calls, and a worker that fails decays and its items reroute to the survivors.
|
|
152
|
+
|
|
153
|
+
```myx
|
|
154
|
+
agent worker(chunk) { # batch in, batch out: one result per item, in order
|
|
155
|
+
seed out = []
|
|
156
|
+
for each x in chunk { out = out + [x * x] }
|
|
157
|
+
report out
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
emit schedule("squares", [worker, worker], [1, 2, 3, 4, 5, 6, 7, 8]) # -> [1, 4, 9, 16, 25, 36, 49, 64]
|
|
161
|
+
emit flows("squares") # each tube's learned conductance
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Adaptation is across calls (explore, then exploit); the invariants that never vary are: every item processed exactly once, results in item order, and a clearly-faster worker ends with higher conductance. Same isolation/data-boundary as `gather`. Detail in [`CONCURRENCY.md`](CONCURRENCY.md).
|
|
165
|
+
|
|
166
|
+
### Cooperative concurrency: fibers & channels
|
|
167
|
+
|
|
168
|
+
`gather`/`schedule` are *parallelism* (many cores). Fibers are the other half — *concurrency*: many tasks interleaving on **one** thread, talking through **channels**. No shared memory; the value passes hand to hand.
|
|
169
|
+
|
|
170
|
+
```myx
|
|
171
|
+
seed ch = channel()
|
|
172
|
+
agent producer(c) { seed i = 0
|
|
173
|
+
reinforce i < 3 { give i to c # send (parks only if a bounded channel is full)
|
|
174
|
+
i = i + 1 } }
|
|
175
|
+
agent consumer(c) { seed k = 0
|
|
176
|
+
reinforce k < 3 { take x from c # receive (parks until a value arrives)
|
|
177
|
+
emit x
|
|
178
|
+
k = k + 1 } }
|
|
179
|
+
spawn producer(ch) # start a fiber
|
|
180
|
+
spawn consumer(ch) # -> 0 1 2
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`spawn` starts a fiber, `yield` reschedules cooperatively, `await(fiber)` gets its result, `drain()` runs them all. The scheduler is single-threaded and **deterministic**, delivery is exactly-once/FIFO, and a fiber's failure or a deadlock always surfaces — never a silent hang. Detail and the honest limits in [`CONCURRENCY.md`](CONCURRENCY.md).
|
|
184
|
+
|
|
185
|
+
## Handling failure
|
|
186
|
+
|
|
187
|
+
A program shouldn't die on one bad index. `attempt` runs a block; if a pathway fails — a
|
|
188
|
+
bad index, a type error, an unknown pathway, or an explicit `fail` — the failure is caught
|
|
189
|
+
and bound to a name as a mesh carrying `message`, `line`, and `value`:
|
|
190
|
+
|
|
191
|
+
```myx
|
|
192
|
+
agent lookup(m, key) {
|
|
193
|
+
attempt {
|
|
194
|
+
when not has(m, key) { fail "no such pathway: " + key }
|
|
195
|
+
report m[key]
|
|
196
|
+
} rescue err {
|
|
197
|
+
report err["message"] # a failure becomes a graceful value, no crash
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`fail expr` raises a failure an enclosing `attempt` can rescue. `report` is **not** caught —
|
|
203
|
+
it still returns from the agent. And because `and`/`or` return the operand value,
|
|
204
|
+
`count[w] or 0` gives `0` when the key is missing. See `examples/resilient.myx`.
|
|
205
|
+
|
|
206
|
+
When a failure *isn't* rescued, it carries a **stack trace** — the chain of agent calls that
|
|
207
|
+
led there, innermost first — not just a line number:
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
Myxo error (line 1): division by zero
|
|
211
|
+
in c (called at line 2)
|
|
212
|
+
in b (called at line 3)
|
|
213
|
+
in a (called at line 4)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Deep traces are truncated, and runaway recursion fails as a clean `Myxo error (… call stack went
|
|
217
|
+
too deep …)` instead of a raw crash.
|
|
218
|
+
|
|
219
|
+
## Modules: weave & expose
|
|
220
|
+
|
|
221
|
+
A file is a *strand*. It keeps everything private unless it `expose`s it; another strand
|
|
222
|
+
pulls those names in with `weave`:
|
|
223
|
+
|
|
224
|
+
```myx
|
|
225
|
+
# geo.myx
|
|
226
|
+
agent area_circle(r) { report PI * r * r }
|
|
227
|
+
seed TAU = PI * 2
|
|
228
|
+
expose area_circle
|
|
229
|
+
expose TAU
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
```myx
|
|
233
|
+
# main.myx
|
|
234
|
+
weave "geo.myx" # flat: area_circle and TAU are now in scope
|
|
235
|
+
emit area_circle(2)
|
|
236
|
+
|
|
237
|
+
weave "geo.myx" as geo # or namespaced into a mesh
|
|
238
|
+
emit geo["TAU"]
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Paths resolve relative to the weaving strand. Modules run **once and are cached**, and
|
|
242
|
+
**circular weaves are caught**, not looped. And because weaving reads a file, **it's a
|
|
243
|
+
granted capability**: an embedded host that builds an interpreter without a module loader
|
|
244
|
+
(`run(src, { moduleLoader: null })`) fences `weave` entirely — a sandboxed agent script
|
|
245
|
+
can't reach the filesystem through it. The fence holds. See `examples/use-geo.myx`.
|
|
246
|
+
|
|
247
|
+
## Capabilities: needs & the audit ledger
|
|
248
|
+
|
|
249
|
+
Myxo is the language you hand an *agent*, so safety is part of the execution model, not a bolt-on.
|
|
250
|
+
The interpreter can only compute; every real-world power (a db lookup, a message, a payment,
|
|
251
|
+
Python eval, a shell command, a compiled binary) is a **capability** the host grants across the
|
|
252
|
+
bridge. Myxo is designed as a capability-first embeddable language for agent scripts:
|
|
253
|
+
|
|
254
|
+
**1. A script declares what it may touch — and how far it may go.** `needs lookup, notify` is a
|
|
255
|
+
manifest. Once declared, calling a capability *not* in it is refused — **even if the host granted
|
|
256
|
+
it.** And a capability can carry a **value budget**: `spend(max 5, total 15)` caps a single call
|
|
257
|
+
(`max`) and the cumulative spend across the whole run (`total`), runtime-enforced. The script is
|
|
258
|
+
bounded by *its own word*, not by the host's restraint:
|
|
259
|
+
|
|
260
|
+
```myx
|
|
261
|
+
needs lookup, notify, spend(max 5, total 15)
|
|
262
|
+
emit lookup("vallartas") # ok — declared
|
|
263
|
+
emit spend(4) # ok — within the per-call max and the run budget
|
|
264
|
+
emit spend(1000000) # refused: exceeds the per-call max of 5
|
|
265
|
+
emit purge("everything") # refused: 'purge' is not declared in this script's 'needs'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
That `spend(max 5, total 15)` is the exact shape of the real Nexus outward gate ($5/tx, $15/day)
|
|
269
|
+
— now a single line of the script's own contract instead of bespoke host code. See
|
|
270
|
+
`examples/outward-gate.myx`.
|
|
271
|
+
|
|
272
|
+
**2. Every privileged call is logged.** The runtime keeps an **audit ledger** — name, args, and
|
|
273
|
+
outcome of each capability call, *including refusals* — handed back to the host after the run
|
|
274
|
+
(even if it failed):
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
run(agentSrc, {
|
|
278
|
+
natives: { lookup, notify, spend }, // what the host is willing to grant
|
|
279
|
+
onAudit: (ledger) => console.log(ledger),
|
|
280
|
+
});
|
|
281
|
+
// [ { cap: 'lookup', args: ['vallartas'], ok: true, result: "..." },
|
|
282
|
+
// { cap: 'spend', args: ['1000000'], ok: false, error: 'not declared in needs' } ]
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Builtins (`len`, `emit`, `map`, …) are **not** capabilities — they're free. Only host-granted
|
|
286
|
+
powers are fenced and logged. See `examples/agent.myx` + `examples/host.js` for the whole story:
|
|
287
|
+
a fenced agent that overreaches, is stopped, and leaves a complete trail.
|
|
288
|
+
|
|
289
|
+
Important boundary: the fence governs the call into a capability. It decides whether the script
|
|
290
|
+
may call that verb, how many times or how much it may spend, and what gets logged. It does not
|
|
291
|
+
sandbox arbitrary code inside a granted runtime. For untrusted or model-written scripts, expose
|
|
292
|
+
narrow structured verbs like `score_lead(id)` or `resize_image(input, output)`, not broad verbs
|
|
293
|
+
like `pyeval`, `jseval`, `sh`, or raw executable launch.
|
|
294
|
+
|
|
295
|
+
## The mesh: bridging MCP tools
|
|
296
|
+
|
|
297
|
+
Capabilities don't have to be hand-written. Hand `run` an **MCP client** and every tool it
|
|
298
|
+
exposes becomes a fenced Myxo capability automatically — so an Myxo script can drive your whole
|
|
299
|
+
tool catalog through one law. Whatever language or service is behind a tool (a Python query, a
|
|
300
|
+
Rust service, a shell command, a model), from inside the script it's just a verb it was granted:
|
|
301
|
+
|
|
302
|
+
```myx
|
|
303
|
+
needs james_db_query, james_telegram_send
|
|
304
|
+
|
|
305
|
+
seed lead = james_db_query("SELECT name FROM leads ORDER BY score DESC LIMIT 1")
|
|
306
|
+
james_telegram_send({ "text": "new top lead: " + lead })
|
|
307
|
+
# james_pm2_action was bridged too, but never declared -> refused before the host is called
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
run(src, {
|
|
312
|
+
mcp: {
|
|
313
|
+
tools: [ { name: 'james_db_query', inputSchema: { properties: { sql: {} }, required: ['sql'] } }, … ],
|
|
314
|
+
call: (name, args) => mcpServer.invoke(name, args), // your transport; returns the result
|
|
315
|
+
},
|
|
316
|
+
onAudit: (ledger) => console.log(ledger),
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
A mesh argument maps to the tool's named arguments; a lone value fills its first required
|
|
321
|
+
property (so `query("SELECT …")` works). MCP-style results (`{ content: [...] }`) unwrap to
|
|
322
|
+
their text, and an error result becomes a rescuable `fail`. Every call is still bounded by the
|
|
323
|
+
script's `needs` manifest and its value budgets, and logged in the audit ledger — the manifest
|
|
324
|
+
decides *which* of the bridged tools this script may actually speak. See `examples/nexus-mesh.myx`
|
|
325
|
+
+ `examples/mcp-host.js`.
|
|
326
|
+
|
|
327
|
+
### Running it live (the wire)
|
|
328
|
+
|
|
329
|
+
Two helpers turn the bridge into a production path an MCP tool can call:
|
|
330
|
+
|
|
331
|
+
- **`myxo-run.js`** — `runScript(script, { client, allow, dir })` → `{ ok, output, audit, error }`.
|
|
332
|
+
Never throws (a tool handler wants a result). Adds a third gate, the **host allowlist** (`allow`
|
|
333
|
+
decides which tools are even *bridged* — absence beats refusal), on top of the script's `needs`
|
|
334
|
+
and its budgets. Production defaults require a `needs` manifest and fence `weave` unless the
|
|
335
|
+
host explicitly grants a module loader.
|
|
336
|
+
- **`myxo-live.js`** — `runLive(script, { tools, onCall, allow })` → `Promise<{ ok, output, audit }>`.
|
|
337
|
+
Real tools are **async**; Myxo is **synchronous**. This bridges them with zero dependencies via
|
|
338
|
+
`worker_threads` + `Atomics`: the script runs in a Worker and each call round-trips to the host's
|
|
339
|
+
async `onCall(name, args)`, the Worker parking until the result is posted back. The transport
|
|
340
|
+
stays abstract, so the same runner wires into any host. `timeoutMs` and `maxSteps` provide wall
|
|
341
|
+
clock and instruction-fuel backstops. See `examples/james-myxo-demo.js`.
|
|
342
|
+
|
|
343
|
+
This is how `james_nx_run` is built in the live Nexus: a curated, safe allowlist of JAMES tools,
|
|
344
|
+
an `onCall` over the real handlers, and `runLive` doing gated, audited work a local model's script
|
|
345
|
+
cannot exceed unless the host exposes a broader capability. The live JAMES surface deliberately
|
|
346
|
+
uses narrow read/request verbs rather than eval, shell, direct messaging, or direct writes.
|
|
347
|
+
|
|
348
|
+
The JAMES bridge deliberately exposes only narrow hands to Myxo. Current capabilities:
|
|
349
|
+
|
|
350
|
+
- `james_db_query`, `james_read_notes` — read paths.
|
|
351
|
+
- `james_approval_request`, `james_approval_list` — human-review queue only; no execution.
|
|
352
|
+
- `james_git_push_request`, `james_action_await` — outwardGate one-tap path for `git_push` only. It is not raw `james_action_stage`; the repo/branch still must pass JAMES's git allowlist and Milton must approve before anything pushes.
|
|
353
|
+
|
|
354
|
+
The unsafe/direct verbs stay absent: no direct memory write, no thought-board post, no Telegram
|
|
355
|
+
send, no PM2 control, no secrets, no sandbox exec, no rollback, and no generic outward action
|
|
356
|
+
staging. Myxo can ask for human review; it cannot quietly perform those side effects through
|
|
357
|
+
`james_nx_run`.
|
|
358
|
+
|
|
359
|
+
## The Law engine
|
|
360
|
+
|
|
361
|
+
Every named pathway carries a **strength** that starts at 1 and rises each time the pathway
|
|
362
|
+
is read — *useful pathways reinforce through use*. Two builtins make the mesh visible and
|
|
363
|
+
self-pruning:
|
|
364
|
+
|
|
365
|
+
- `mesh()` — returns the live pathway graph as a mesh of `name: strength`
|
|
366
|
+
- `prune(threshold)` — removes pathways below `threshold`, returns how many fell
|
|
367
|
+
|
|
368
|
+
Run any script with `--trace` to watch the mesh as a bar chart of strengths after it ends.
|
|
369
|
+
|
|
370
|
+
## Built-in agents
|
|
371
|
+
|
|
372
|
+
**Core:** `len` · `type` · `keys` · `values` · `has` · `range` · `push` · `pop` · `shift` ·
|
|
373
|
+
`unshift` · `str` · `num` · `mesh` · `prune`
|
|
374
|
+
**Strings:** `upper` · `lower` · `trim` · `split` · `join` · `replace` · `repeat` · `chars` ·
|
|
375
|
+
`starts` · `ends`
|
|
376
|
+
**Sequences (string or list):** `slice` · `find` · `reverse` · `sort` · `entries` · `merge`
|
|
377
|
+
**Math:** `abs` · `floor` · `ceil` · `round` · `sqrt` · `pow` · `log` · `sin` · `cos` · `tan` ·
|
|
378
|
+
`max` · `min` · `random`
|
|
379
|
+
|
|
380
|
+
And from `std.myx` (written in Myxo itself): `map` · `filter` · `reduce` · `sum` · `biggest` ·
|
|
381
|
+
`contains` · `count` · `first` · `last` · `clamp` · `words` · `lines`, plus the constants
|
|
382
|
+
`PI` and `E`.
|
|
383
|
+
|
|
384
|
+
> **Where Myxo is headed:** the full design — modules, async agents, the self-optimizing mesh,
|
|
385
|
+
> the finished capability fence, and the honest pitch for why Myxo becomes the go-to language of
|
|
386
|
+
> the agent era — is laid out in [`VISION.md`](VISION.md). *Leave nothing unimagined.*
|
|
387
|
+
|
|
388
|
+
## Running Myxo Independently
|
|
389
|
+
|
|
390
|
+
Myxo is usable without JAMES/Nexus or any foreign runtime:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
node myxo.js examples/fib.myx
|
|
394
|
+
node myxo.js
|
|
395
|
+
node myxo.js fmt examples/fib.myx
|
|
396
|
+
npm test
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
That standalone surface is part of the contract. Polyglot bridges add reach; they do not make
|
|
400
|
+
Myxo dependent on the bridged language.
|
|
401
|
+
|
|
402
|
+
## Editor support (LSP)
|
|
403
|
+
|
|
404
|
+
`node myxo.js lsp` (or `npm run lsp`) starts a stdio JSON-RPC language server: live diagnostics (syntax/parse errors), hover docs on keywords and builtins, completion (keywords + builtins + the top-level names in your file), and format-on-command. Point any LSP client at the `myxo lsp` command.
|
|
405
|
+
|
|
406
|
+
Honest scope (v1): diagnostics are **syntax-only** — a file that parses but is semantically broken (a type error under `--strict`, a wrong arity) shows no squiggle until you run it. Completion is top-level / not scope-aware. No go-to-definition, references, or rename yet. Formatting **preserves comments** (statement-granular).
|
|
407
|
+
|
|
408
|
+
## Previewing a script's reach: `myxo plan`
|
|
409
|
+
|
|
410
|
+
`node myxo.js plan <file>` is the **fence approval surface** — before you (or a gate) run a script, see exactly what host capabilities it *declares* (`needs`) versus what it *reaches for*, with referenced-but-undeclared flagged (the fence would deny) and over-grants called out. Exit `0` clean / `1` not-clean / `2` couldn't analyze, so a gate can branch on it.
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
$ node myxo.js plan job.myx
|
|
414
|
+
Declared capabilities (needs):
|
|
415
|
+
db_query
|
|
416
|
+
Capabilities referenced in code:
|
|
417
|
+
db_query OK declared
|
|
418
|
+
telegram_send XX NOT declared (line 4) — the fence would deny it
|
|
419
|
+
VERDICT: 1 referenced capability(ies) not declared — this script would be REFUSED at runtime ...
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Honest scope: it's a **best-effort preview, not a sound gate** — scope-aware over direct call-sites, but it can't statically tell a host capability from a same-named user agent, nor follow a capability passed as a value or via `weave`. It discloses those blind spots on every run and defers to the **runtime fence** as the real boundary. Use it to see intent and catch mistakes; never approve on it alone.
|
|
423
|
+
|
|
424
|
+
## Embedding Myxo in the Nexus
|
|
425
|
+
|
|
426
|
+
Myxo is built to be driven from Node. `require` it and run source in-process, handing your
|
|
427
|
+
own native functions across the host bridge:
|
|
428
|
+
|
|
429
|
+
```js
|
|
430
|
+
const { run } = require('./myxo');
|
|
431
|
+
|
|
432
|
+
// Capture output as a string:
|
|
433
|
+
const text = run('emit 2 + 3', { capture: true }); // "5\n"
|
|
434
|
+
|
|
435
|
+
// Expose host capabilities to scripts (the path to james.* / db / telegram):
|
|
436
|
+
run('emit lookup("vallartas")', {
|
|
437
|
+
natives: {
|
|
438
|
+
lookup: (args) => queryNexus(args[0]), // your JS, callable as an agent in Myxo
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
`run(src, opts)` options: `capture` (return output instead of printing),
|
|
444
|
+
`output` (a custom writer), `natives` (host capabilities — `name → fn(args, interp)`),
|
|
445
|
+
`mcp` (an MCP client `{ tools, call }` — every tool becomes a fenced capability),
|
|
446
|
+
`onAudit` (receive the capability ledger), `dir` (base directory for `weave`),
|
|
447
|
+
`moduleLoader` (override, or `null` to fence `weave`), `maxDepth` (recursion cap),
|
|
448
|
+
`maxSteps` (instruction-fuel cap), `requireManifest` (refuse capability calls until `needs`
|
|
449
|
+
is declared), and `trace` (append the mesh view).
|
|
450
|
+
|
|
451
|
+
## Evals — the instrument (Phase 1)
|
|
452
|
+
|
|
453
|
+
Myxo's thesis is that a model writes correct, *safe* agent-code more reliably in Myxo than in
|
|
454
|
+
Python-in-a-sandbox. The only honest way to know is to measure it, so `myxo-evals/` is the
|
|
455
|
+
instrument: **30 golden tasks** across six buckets (data · control · agents · fence ·
|
|
456
|
+
concurrency · adversarial), each with a reference solution and discriminating checks.
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
node myxo-evals/run-evals.js # --ref: run every task's reference solution (validates the set)
|
|
460
|
+
node myxo-evals/run-evals.js --model ollama # local free baseline (Ollama on :11434)
|
|
461
|
+
node myxo-evals/run-evals.js --model claude # frontier tier (ANTHROPIC_API_KEY; costs)
|
|
462
|
+
node myxo-evals/run-control.js [--model ...] # the Python control group — the Myxo-vs-Python delta
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The harness is adversarially hardened (two independent review passes): execution is
|
|
466
|
+
fuel-bounded so one runaway solution can't hang the run; **fence tasks are scored on the
|
|
467
|
+
capability audit ledger**, not just stdout, so a hardcoded-emit cheat fails; a required
|
|
468
|
+
construct (`match`, `dispatch`/`gather`, …) is gated against dead-branch and comment/string
|
|
469
|
+
bypasses; and a model's own test blocks are stripped before scoring. First baseline numbers
|
|
470
|
+
and the honest scope live in [`myxo-evals/BASELINE.md`](myxo-evals/BASELINE.md).
|
|
471
|
+
|
|
472
|
+
## How it works
|
|
473
|
+
|
|
474
|
+
A classic tree-walking interpreter in four stages, one file each:
|
|
475
|
+
|
|
476
|
+
```
|
|
477
|
+
.myx source → lexer.js → parser.js → interpreter.js ⇄ builtins.js → output
|
|
478
|
+
tokens AST walk + eval host bridge
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
- **`lexer.js`** — text → tokens, tracking line/col for errors
|
|
482
|
+
- **`parser.js`** — recursive descent + precedence climbing → AST
|
|
483
|
+
- **`interpreter.js`** — the `Environment` (pathways + strength + closures) and evaluator
|
|
484
|
+
- **`builtins.js`** — native functions and `registerNative` (the host bridge)
|
|
485
|
+
- **`mcp-bridge.js`** — turns a catalog of MCP tools into fenced Myxo capabilities
|
|
486
|
+
- **`std.myx`** — the standard library, bootstrapped in Myxo
|
|
487
|
+
- **`myxo.js`** — CLI, REPL, and the `run()` embed API
|
|
488
|
+
|
|
489
|
+
Errors are a single `MyxoError` type carrying a line number, so the lexer, parser, and
|
|
490
|
+
interpreter all point at exactly where a program went wrong.
|
|
491
|
+
|
|
492
|
+
## License
|
|
493
|
+
|
|
494
|
+
MIT.
|