@maiaos/node 0.1.0-canary.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.
- package/README.md +281 -0
- package/dist/bin/maia-node.NOTICES.txt +3908 -0
- package/dist/bin/maia-node.cdx.json +2373 -0
- package/dist/bin/maia-node.meta.json +1 -0
- package/dist/bin/maia-node.mjs +8356 -0
- package/dist/bin/maia-node.mjs.map +6 -0
- package/dist/bin/maia-node.release.json +90 -0
- package/dist/bin/maia-process-host-darwin-arm64 +0 -0
- package/dist/bin/maia-process-host-darwin-arm64.build.json +59 -0
- package/package.json +85 -0
package/README.md
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Maia Node (`@maiaos/node`)
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@maiaos/node)
|
|
4
|
+
|
|
5
|
+
Maia Node is the local authority of Maia OS: one process per machine and
|
|
6
|
+
profile that owns Sessions, turns, worker accounts, durable state, event order
|
|
7
|
+
and receipts for swarms of AI coding agents such as Claude Code and Codex.
|
|
8
|
+
|
|
9
|
+
Every Maia client (the `maia` CLI, the `maia mcp` MCP server, the
|
|
10
|
+
`@maiaos/sdk` TypeScript SDK and the Desktop app) reaches the Node through its
|
|
11
|
+
authenticated loopback API; none of them owns state. The Node validates and
|
|
12
|
+
authorizes each operation, stores the result in SQLite, writes a receipt and
|
|
13
|
+
runs workers as child processes under a native process host.
|
|
14
|
+
|
|
15
|
+
## Run it
|
|
16
|
+
|
|
17
|
+
In development, run a Node inside an isolated dev worktree, so it never touches
|
|
18
|
+
your real `~/.maia`:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pnpm dev:worktree create try-maia
|
|
22
|
+
cd ../maiaos-worktrees/try-maia
|
|
23
|
+
pnpm dev:node # builds the Node and runs it in the foreground
|
|
24
|
+
pnpm maia status # in a second terminal: the Node reports ready
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The executable itself:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pnpm --filter @maiaos/node build
|
|
31
|
+
node services/node/dist/bin/maia-node.mjs --help
|
|
32
|
+
node services/node/dist/bin/maia-node.mjs --desktop --profile <name>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`--desktop` runs the authenticated local Node in the foreground, and
|
|
36
|
+
`--profile` selects its runtime storage (`default` when omitted).
|
|
37
|
+
For a Desktop-owned child, the native host sets `MAIA_DESKTOP_PARENT_PIPE=1`
|
|
38
|
+
and retains its stdin writer. EOF or a pipe error interrupts the existing
|
|
39
|
+
Node scope, including during startup, to drain and release runtime authority.
|
|
40
|
+
A separate Effect deadline kills this process after 10 seconds if its drain
|
|
41
|
+
stalls. A drain that finishes after SIGINT, SIGTERM or a closed pipe exits 0,
|
|
42
|
+
so systemd logs no failure for a restart; a failed drain or startup keeps its
|
|
43
|
+
non-zero code. Without that explicit flag, stdin closure does not stop the Node;
|
|
44
|
+
attached local services and remote Nodes keep their own lifetimes. The
|
|
45
|
+
self-kill deadline requires a responsive Node event loop.
|
|
46
|
+
`maia-node remote probe|install|rollback|uninstall <ssh-host>` installs a
|
|
47
|
+
pinned Node on a Linux host as a systemd user service, and `maia-node bridge`
|
|
48
|
+
serves that host's Node API over SSH standard input and output, with no
|
|
49
|
+
listener beyond loopback ([remote Node install](../../docs/operations.md#remote-node-install)).
|
|
50
|
+
The local Node also registers remote Nodes (`node.remote.*`, S14): a preview
|
|
51
|
+
probes and plans in a Node-scoped job, a confirmation bound to the plan
|
|
52
|
+
digest records a receipt and runs the same install or uninstall, and the
|
|
53
|
+
install pins `codex` and `claude` with a PATH of their directories, the Maia
|
|
54
|
+
runtime and the system directories ([D-080](../../docs/decisions.md#d-080)).
|
|
55
|
+
A local background service is planned (RL3). `@maiaos/node@canary` is
|
|
56
|
+
published on npm for installation testing only; npm's automatic `latest` tag
|
|
57
|
+
also points at a canary build.
|
|
58
|
+
|
|
59
|
+
## Key concepts
|
|
60
|
+
|
|
61
|
+
- **One authority.** The Node alone validates, authorizes, persists, executes,
|
|
62
|
+
orders events and writes receipts. Clients are projections: a cached client
|
|
63
|
+
state never overwrites newer Node state.
|
|
64
|
+
- **Node API.** The Node binds an ephemeral port on `127.0.0.1` only and writes
|
|
65
|
+
an owner-private connection descriptor and bearer credential to its profile
|
|
66
|
+
runtime directory. Unary operations use `POST /api/v1/operations`;
|
|
67
|
+
observations use the `/api/v1/observations` WebSocket. A restart replaces the
|
|
68
|
+
credential.
|
|
69
|
+
- **Live observation (S12a).** `observe.events` replays committed journal rows
|
|
70
|
+
from SQLite, which stays the source of cursors and replay. An idle
|
|
71
|
+
subscriber wakes as soon as a journal append commits: a TEMP trigger on
|
|
72
|
+
`domain_events` signals every append on the Node's connection, and a burst
|
|
73
|
+
coalesces into one wake. The five-second heartbeat proves liveness and
|
|
74
|
+
publishes filtered progress.
|
|
75
|
+
- **Live Session items (S12b and S12c, source candidates).** A per-Session
|
|
76
|
+
writer assigns each Codex or Claude Code item record its owner sequence. The live lane coalesces
|
|
77
|
+
deltas for 33 ms and offers them to bounded attachment queues (256 records,
|
|
78
|
+
2 MiB); a full queue gets `resync`. The durable lane commits finished items
|
|
79
|
+
in one transaction per 400 ms (`session_stream_records`, migration v50), and
|
|
80
|
+
startup closes what a crashed epoch left open. `session.attach` on the
|
|
81
|
+
observation socket replays or snapshots, synchronizes the open items, then
|
|
82
|
+
streams live records.
|
|
83
|
+
- **Profiles.** A profile has its own runtime directory, database and
|
|
84
|
+
observability files. Each dev worktree runs its own profile and `MAIA_HOME`.
|
|
85
|
+
- **Sessions and executors.** A Session runs one vendor executor, Codex through
|
|
86
|
+
`codex app-server` or Claude Code through the Claude Agent SDK. A reading
|
|
87
|
+
Session runs read-only in its Workspace; a coding Session (`--base`) gets a
|
|
88
|
+
worktree of its own and writes only there, with `session diff` and
|
|
89
|
+
`session archive` to review and retire it
|
|
90
|
+
([coding Sessions](../../docs/maia-swarm/external-executors.md#coding-sessions)).
|
|
91
|
+
A Session is bound to
|
|
92
|
+
one account Connection; when the caller names only the executor kind, the
|
|
93
|
+
Node picks the eligible account with the most usage left. At most 8 Sessions
|
|
94
|
+
run at once on a Node; each account allows 2 by default (1–16, set with
|
|
95
|
+
`maia login <claude|codex> --max-sessions` or `maia connection capacity set`).
|
|
96
|
+
- **Accounts.** A Connection is a named Codex or Claude Code account, either a
|
|
97
|
+
Maia-managed account root or the system default login. The vendor CLI keeps
|
|
98
|
+
custody of the credential; Maia never reads it. Vendor usage-limit signals
|
|
99
|
+
mark an account `limited` until its reset time. A gateway Connection is an
|
|
100
|
+
AI gateway's base URL with its API key in an owner-only `gateway-key` file;
|
|
101
|
+
Claude Code and Codex Sessions on it receive the URL and key through their
|
|
102
|
+
launch environment.
|
|
103
|
+
- **Process hosts.** On macOS a native helper owns each worker's process groups,
|
|
104
|
+
limits and cleanup proof (cooperative, not a sandbox). On Linux a helper puts
|
|
105
|
+
each process tree in its own cgroup v2 directory and confines it with
|
|
106
|
+
Landlock and seccomp; it needs a systemd user unit with `Delegate=yes` and
|
|
107
|
+
Linux 6.12 or later, and it bounds the tree, not the user. Both are source
|
|
108
|
+
candidates. A Windows Job Object helper exists in source but is unqualified:
|
|
109
|
+
Windows is unsupported in v1 and fails with `platform_unsupported`. A host
|
|
110
|
+
without qualified process authority fails closed. A vendor launch permit
|
|
111
|
+
records where its host keeps the tree (the macOS process group, the Linux
|
|
112
|
+
tree cgroups' parent), and after a crash the restarted Node frees the
|
|
113
|
+
vendor only once that record shows the tree ended
|
|
114
|
+
([D-098](../../docs/decisions.md#d-098)).
|
|
115
|
+
- **Mutations.** Every mutation checks a revision or command ID and returns a
|
|
116
|
+
receipt; replaying a command ID returns the first result instead of acting
|
|
117
|
+
twice. Project and container metadata, agent permissions, integration plans
|
|
118
|
+
and the voice relay also need a preview and an explicit confirmation.
|
|
119
|
+
- **Port leases (S16).** `port.lease.acquire|renew|release|list` grant 1–8
|
|
120
|
+
loopback ports from `MAIA_PORT_RANGE` (default `41000-41999`) to a Session
|
|
121
|
+
or the operator. Leases are advisory: the Node never restricts a bind or
|
|
122
|
+
stops a process. A port is granted only when connects to `127.0.0.1` and
|
|
123
|
+
`[::1]` are refused and a loopback bind succeeds. Leases last 15 minutes
|
|
124
|
+
unless renewed, and end with their Session, at expiry or at Node start.
|
|
125
|
+
Codex and Claude Code Sessions that hold leases receive `MAIA_PORTS`.
|
|
126
|
+
- **Emergency holds.** A person holds exact Projects or accounts with a
|
|
127
|
+
previewed, confirmed `hold.place`. Every new Session, start, resume, turn,
|
|
128
|
+
Run dispatch and voice relay on a held target fails with `held`, checked in
|
|
129
|
+
the transaction that admits it; account selection skips held accounts. A
|
|
130
|
+
`stop` hold also stops the Sessions running on its targets in the same
|
|
131
|
+
transaction, then ends their runtimes. Holds survive restarts, recovery never
|
|
132
|
+
resumes held work, and `hold.release` restores admission. See
|
|
133
|
+
[operations](../../docs/operations.md#emergency-holds-implemented-today-source-candidate).
|
|
134
|
+
- **Worker credentials and fan-out guards (S18a, S18b).** Each Codex or
|
|
135
|
+
Claude Code launch gets its own Session credential (`session-credential/`),
|
|
136
|
+
revoked when its runtime ends; `Authorization: Maia-Session` admits that
|
|
137
|
+
Session's role operations (`application/session-request.ts`). Both roles
|
|
138
|
+
list Sessions only in the credential's Project, before pagination; an
|
|
139
|
+
explicit other Project is refused. Human and external-coordinator lists
|
|
140
|
+
retain whole-Node visibility ([D-092](../../docs/decisions.md#d-092)). The Linux
|
|
141
|
+
helper denies Claude Code trees reads of the runtime directory and auth
|
|
142
|
+
file; Codex's profile denies them to Codex commands
|
|
143
|
+
([D-110](../../docs/decisions.md#d-110)).
|
|
144
|
+
`session-guard/` keeps each Session's role, parent and depth and the
|
|
145
|
+
per-Project depth, live-children, messages-per-minute and
|
|
146
|
+
leases-per-Session limits (`project.guard.*`), checked in the write that
|
|
147
|
+
creates, sends or leases. See
|
|
148
|
+
[vendor executors](../../docs/maia-swarm/external-executors.md#session-bound-worker-credential).
|
|
149
|
+
- **Control dispatch.** An operator or coordinator request with a feature
|
|
150
|
+
descriptor goes through `application/feature-handlers.ts`: one
|
|
151
|
+
context-preserving adapter per registered Node handler, which forwards the
|
|
152
|
+
command ID and any attached credential, selects the executor's services and
|
|
153
|
+
fails closed when one is not composed (adopted-Session resume preflight,
|
|
154
|
+
legacy versus interactive `terminal.create`, voice relay availability).
|
|
155
|
+
Undescribed methods keep explicit routes in `application/control-request.ts`
|
|
156
|
+
until their descriptors land.
|
|
157
|
+
- **Model providers.** `provider.pool.list` reads the owner-only `auth.json`
|
|
158
|
+
that `maia login` writes. The managed Pi worker leases one credential in
|
|
159
|
+
memory per Session and releases it after a cleanup proof.
|
|
160
|
+
|
|
161
|
+
## Status
|
|
162
|
+
|
|
163
|
+
Implemented as source candidates, not release-qualified:
|
|
164
|
+
|
|
165
|
+
- the Node API, SQLite storage with checksummed migrations, and receipts with
|
|
166
|
+
command-ID replay;
|
|
167
|
+
- Projects, execution Workspaces, Workspace containers and confirmed Project
|
|
168
|
+
metadata;
|
|
169
|
+
- Sessions with Codex and Claude Code executors: create, start, stop, resume,
|
|
170
|
+
rename, observe, events, with titles (given at creation or taken from the
|
|
171
|
+
first turn); turns: send, wait, show, interrupt;
|
|
172
|
+
- reference resolution (`reference.resolve`): an ID, ID prefix, name or
|
|
173
|
+
Session title, or the registered folder containing a path, to one exact ID
|
|
174
|
+
or its candidates;
|
|
175
|
+
- Connections, sign-in, usage and integration approval of vendor executables;
|
|
176
|
+
- record-only external questions (`interaction.*`), presentation reads, live
|
|
177
|
+
terminals on macOS, and the release-disabled voice orchestrator;
|
|
178
|
+
- emergency holds on exact Projects and accounts (`hold.*`, S20);
|
|
179
|
+
- the Run ledger over executor Sessions (`run.ledger.*`, S19): a task graph
|
|
180
|
+
whose dispatches are Codex or Claude Code Session turns, Task state derived
|
|
181
|
+
from dispatches, three failed dispatches stop a Task, and the
|
|
182
|
+
`session.observe` attention value.
|
|
183
|
+
- Session-bound worker credentials, coding-worker MCP launch configuration,
|
|
184
|
+
roles and per-Project fan-out guards
|
|
185
|
+
(`project.guard.*`, S18a and S18b).
|
|
186
|
+
|
|
187
|
+
Planned: Pi item adapters and the Desktop timeline
|
|
188
|
+
(S12d) on the S12b stream, host and agent observability
|
|
189
|
+
(S15), advisory leases (S16, in progress), Node and capability holds with `capability.revoke`, coding
|
|
190
|
+
workers (S5), the installer and
|
|
191
|
+
background service (RL3) and updates (RL4). The
|
|
192
|
+
[roadmap](../../ROADMAP.md#v1-external-brain-and-vendor-executors) tracks each
|
|
193
|
+
slice.
|
|
194
|
+
|
|
195
|
+
## Direction: Control Nodes, Execution Nodes and principals
|
|
196
|
+
|
|
197
|
+
The Node is designed to grow into an organization's agent hub. None of this is
|
|
198
|
+
implemented:
|
|
199
|
+
|
|
200
|
+
- one **Control Node** owns durable truth for team Workspaces, starting from an
|
|
201
|
+
explicit first-owner bootstrap
|
|
202
|
+
([design](../../docs/team-workspaces-and-control-nodes.md));
|
|
203
|
+
- every request resolves a server-created **principal** with explicit Project
|
|
204
|
+
**grants**; callers never supply identity, operator rights or grants
|
|
205
|
+
([identity, authentication and tenancy](../../docs/maia-node/architecture.md#identity-authentication-and-tenancy));
|
|
206
|
+
- enrolled **Execution Nodes** (your computer, a VPS, other servers) provide
|
|
207
|
+
capacity through one-time pairing and Node-issued certificates;
|
|
208
|
+
- **A2A** peers map each accepted task to one durable Maia task and never
|
|
209
|
+
receive worker leases or mutation authority.
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
pnpm --filter @maiaos/node build
|
|
215
|
+
pnpm --filter @maiaos/node lint
|
|
216
|
+
pnpm --filter @maiaos/node typecheck
|
|
217
|
+
pnpm --filter @maiaos/node test
|
|
218
|
+
pnpm test:resource-diagnostic # from the repository root
|
|
219
|
+
pnpm --filter @maiaos/node test:observation-benchmark
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The observation benchmark commits through a real SQLite journal after seeded
|
|
223
|
+
idle gaps of 0–3 s and times delivery to an SDK subscriber over loopback; it
|
|
224
|
+
fails above a p95 of 50 ms and writes `.artifacts/benchmarks/`. Measured on an
|
|
225
|
+
Apple M4 Pro (12 cores, macOS 27.2, Node 24.19.0) while parallel test runs
|
|
226
|
+
loaded the machine (load average 5–32), 100 samples each:
|
|
227
|
+
|
|
228
|
+
| Measure | Before S12a | After S12a |
|
|
229
|
+
| -------------------------------------- | ------------------------ | -------------------- |
|
|
230
|
+
| Commit to subscriber, p50 / p95 / max | 3,410 / 4,874 / 4,957 ms | 1.9 / 4.7 / 19.8 ms |
|
|
231
|
+
| Idle CPU per 60 s, 8 subscribers | 222 ms (165–279) | 267 ms (207–334) |
|
|
232
|
+
| 10,000 commits, 142 forced resumptions | not measured | none lost or doubled |
|
|
233
|
+
|
|
234
|
+
The idle row is the median and range of six alternating runs each; the spread
|
|
235
|
+
between runs is wider than the difference. The new wait races two Effect
|
|
236
|
+
fibers where the old loop slept, about 60 µs per wait in a microbenchmark,
|
|
237
|
+
roughly 6 ms of CPU per minute for eight idle subscribers.
|
|
238
|
+
|
|
239
|
+
The S12b acceptance test (`src/runtime/node-api/observation/attach.integration.test.ts`)
|
|
240
|
+
drives a synthetic harness into the real hub, the real Node API server, the
|
|
241
|
+
SDK and the in-process `maia session attach` command, and times each delta
|
|
242
|
+
until the CLI writes it. Same machine and load conditions:
|
|
243
|
+
|
|
244
|
+
| Measure (S12b) | Result |
|
|
245
|
+
| ------------------------------------------------- | ---------------------------------- |
|
|
246
|
+
| Harness delta to CLI line, 1 streaming Session | p50 18.8 ms, p95 36.0 ms (150) |
|
|
247
|
+
| Harness delta to CLI line, 100 streaming Sessions | p50 21.1 ms, p95 39.3 ms (900) |
|
|
248
|
+
| Durable writes per streaming Session | at most 2.78 per second |
|
|
249
|
+
| Catch-up of 1,000 records to `synchronized` | p50 15.8 ms, p95 22.9 ms (10 runs) |
|
|
250
|
+
| Viewer beside a stalled subscriber | p95 36.1 ms; stalled one: `resync` |
|
|
251
|
+
|
|
252
|
+
At 100 Sessions six viewers attach (the observation socket admits eight), and
|
|
253
|
+
every Session streams a delta every 20 ms. S12c runs the same test with each
|
|
254
|
+
delta sent as an Agent SDK partial message through the Claude adapter:
|
|
255
|
+
|
|
256
|
+
| Measure (S12c, Claude adapter) | Result |
|
|
257
|
+
| ------------------------------------------------- | ------------------------------ |
|
|
258
|
+
| Harness delta to CLI line, 1 streaming Session | p50 18.8 ms, p95 34.6 ms (150) |
|
|
259
|
+
| Harness delta to CLI line, 100 streaming Sessions | p50 19.2 ms, p95 37.2 ms (900) |
|
|
260
|
+
| Durable writes per streaming Session | at most 2.55 per second |
|
|
261
|
+
|
|
262
|
+
Tests use deterministic clocks, identifiers, transports and disposable SQLite.
|
|
263
|
+
Package tests get a throwaway `MAIA_HOME`; never point a test at your real
|
|
264
|
+
`~/.maia`.
|
|
265
|
+
|
|
266
|
+
## Documentation
|
|
267
|
+
|
|
268
|
+
- [Node architecture](../../docs/maia-node/architecture.md): Sessions,
|
|
269
|
+
activations, supervision, events, observation streaming and recovery
|
|
270
|
+
- [Operations](../../docs/operations.md): deployment, SQLite, identity, backup
|
|
271
|
+
and observability
|
|
272
|
+
- [External brain and vendor executors](../../docs/maia-swarm/external-executors.md):
|
|
273
|
+
executor, account and containment decisions
|
|
274
|
+
- [Release, update and compatibility](../../docs/release.md): the planned
|
|
275
|
+
installer, service and update flow
|
|
276
|
+
- Scope READMEs: [harness adapters](./src/harness/README.md) and
|
|
277
|
+
[Project and Workspace storage](./src/project-workspace/README.md)
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
No license has been published yet. All rights reserved.
|