@ai-ecoverse/slicc-bios 1.39.17 → 1.40.1

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 CHANGED
@@ -200,17 +200,23 @@ The Files panel lists `/opt/grammars/node_modules` but doesn't scan it.
200
200
 
201
201
  **Release notes (slicc-spectrum#458):** each update check also gives the Kernel, UI, Agent and Tailscale rows a `releases` list from their GitHub repositories (`slicc-kernel`, `slicc-spectrum`, `slicc-agent`, `wasm-tailscale`), and each optional package one from the repository its npm version document names, when that is on GitHub ([`os/releases.js`](src/seed/releases.js)). A row gets the release it has installed and every newer one up to the version it offers, newest first, at most 30. Each release has its `version` (the tag without the `v`, or without the package's name in a shared repository such as homescoop), its `url` and its `notes`, the release body unchanged (the `<!-- cert-verify -->` block included, which spectrum turns into a Certified badge) up to 64 KB. Spectrum renders the notes; bios only fetches them. bios asks GitHub's unauthenticated API (`/repos/<repo>/releases?per_page=100`, page by page down to the installed version, at most 5 pages), without credentials, a referrer or any header but `Accept` and `If-None-Match`. It keeps each list in IndexedDB (`slicc-releases`) for a day and revalidates it with its ETag at every check, and GitHub doesn't count a 304 against its 60 requests an hour. A 403 or 429 backs off until `Retry-After`, the rate limit's reset or 15 minutes, and leaves `releases` out, so the row looks as it does without notes, with no error. The page sets no `connect-src`, so these requests need no change to its CSP.
202
202
 
203
- Below the system rows, Install / Update offers **Optional packages** (spectrum ≥ 1.42.0): tools that seven doesn't preinstall. homescoop's packages (`wasm-*`, `wasi-*`, `wasix-*`) come at npm's `latest`, which under cert-review v2 is certified, since homescoop publishes only from a reviewed main through its ladder. The four that other repos publish, slicc-jsh, mcpcat, wasi-slicc (slicc-cli's) and sliccy-browser (slicc-cdp's), stay bios pins in [`src/packages/optional/package.json`](src/packages/optional/package.json) and its `package-lock.json`, so Renovate bumps them and CI tests each bump (`pins.test.mjs` checks each is its own repository's release from main); the page can't check those repos' CI runs itself (GitHub's API allows 60 unauthenticated requests an hour), so a main-built release alone doesn't reach seven. The catalog, [`src/packages/optional/catalog.json`](src/packages/optional/catalog.json), lists each package by its npm name, with only what npm can't tell: its `id` and `label`, and optionally `requires` (other ids), `notes` (bios's own caveats, one line each, shown next to npm's description), `helpers` (commands to leave out of the list until homescoop marks them `helper: true`), `range` and `abi` (Python's, below) and `kind: "project"`. A project keeps its `description`, `commands` and `size`, since npm doesn't know it.
203
+ Below the system rows, Install / Update offers **Optional packages** (spectrum ≥ 1.42.0): tools that seven doesn't preinstall. Seven pins only what UI boot and the agent install need (Lars, 2026-10-21), so every catalog package comes at npm's `latest`, behind [the gate](#optional-packages). A release passes when npm's provenance shows its publisher's release workflow built it on `main`. homescoop publishes its packages (`wasm-*`, `wasi-*`, `wasix-*`) through its ladder. The other publishers are slicc-jsh, mcpcat, slicc-cli (wasi-slicc) and slicc-cdp (sliccy-browser), and the catalog names each one's repository and workflow in its `source`. The catalog, [`src/packages/optional/catalog.json`](src/packages/optional/catalog.json), lists each package by its npm name, with only what npm can't tell: its `id` and `label`, and optionally `requires` (other ids), `notes` (bios's own caveats, one line each, shown next to npm's description), `helpers` (commands to leave out of the list until homescoop marks them `helper: true`), `range` and `abi` (Python's, below), `source` (a publisher other than homescoop: its `repo` and release `workflow`, and optionally `since`, the first version it certifies, and `deny`, versions it never certifies, each with a reason) and `kind: "project"`. A project keeps its `description`, `commands` and `size`, since npm doesn't know it.
204
204
 
205
- - **What npm tells (`os/packages.js`):** at boot, and at an update check at most every ten minutes, bios fetches each entry's version doc from the registry from the page (`https://registry.npmjs.org/<name>/latest`, which sends CORS `*`; the kernel's network path isn't needed), all in parallel and each with a 15-second timeout. A pinned entry's doc is its pin's (`/<name>/<pin>`), so only its description, commands and size come from npm. It takes `version` (the row's `offered`), `description` (npm's text, verbatim), the commands (the keys of `slicc.commands`, without one marked `helper: true` or listed in `helpers`, and only names a shell can run), `dist.unpackedSize` (the row's `size`, now what the package unpacks to on disk, its own files only, so git reads 56 MB where the old tarball-based size read 19 MB; the row says `unpacked: true`) and `dist.integrity`. An entry with a `range` (Python's is `3.15.0`) takes the highest version in range from the abbreviated packument instead, where the range is matched against the upstream X.Y.Z with homescoop's `-N` build ignored, so `3.15.0` offers 3.15.0-3 but never a 3.15.1 or 3.16 (pydata's builds are for one Python ABI: homescoop keeps the ABI within one X.Y.Z, and its py-* ranges stop at the next patch). With `abi`, a version whose `slicc.python.abi` differs isn't offered.
205
+ - **What npm tells (`os/packages.js`):** at boot, and at an update check at most every ten minutes, bios fetches each entry's version doc from the registry from the page (`https://registry.npmjs.org/<name>/latest`, which sends CORS `*`; the kernel's network path isn't needed), all in parallel and each with a 15-second timeout. It takes `version` (the row's `offered`), `description` (npm's text, verbatim), the commands (the keys of `slicc.commands`, without one marked `helper: true` or listed in `helpers`, and only names a shell can run), `dist.unpackedSize` (the row's `size`, now what the package unpacks to on disk, its own files only, so git reads 56 MB where the old tarball-based size read 19 MB; the row says `unpacked: true`) and `dist.integrity`. An entry with a `range` (Python's is `3.15.0`) takes the highest version in range from the abbreviated packument instead, where the range is matched against the upstream X.Y.Z with homescoop's `-N` build ignored, so `3.15.0` offers 3.15.0-3 but never a 3.15.1 or 3.16 (pydata's builds are for one Python ABI: homescoop keeps the ABI within one X.Y.Z, and its py-* ranges stop at the next patch). With `abi`, a version whose `slicc.python.abi` differs isn't offered.
206
206
  - **Cache:** the last good answer for each entry is kept in `var/lib/slicc/optional/npm.json`, so the panel works offline and shows at once at boot; a later fetch replaces it, an entry that fails keeps its cached answer, and holds only what the rows show. A passing provenance verdict is remembered only in the tab's memory, for the listing (never a refusal, never in OPFS, which kernel processes can write, and never for an install, which always asks npm again).
207
207
  - **No answer and no cache:** boot never waits for npm. Such a row has `registry: 'checking'` while the fetch runs, then `registry: 'unreachable'`, with `offered: null`, an empty description, no commands, no size and no Install (spectrum shows *Couldn't reach npm.*); a row that isn't installed gets **Retry**, which, like the panel's `refreshPackages()`, fetches npm again. An installed row keeps **Remove**.
208
208
  - **The gate** (one function, `gate()` in `os/packages.js`, for the panel's Install and Update, a user's Update and [Your packages](#your-packages)) checks every package in the lock pnpm made. Each must be one of:
209
- - an exact `name@version` with that integrity in the base set's lockfile (`src/packages/package-lock.json`), the catalog's pins (`src/packages/optional/package-lock.json`) or `src/packages/certs.json`, which is the only way a pinned entry passes; or
210
- - an `@ai-ecoverse/*` package whose locked integrity equals the registry's `dist.integrity` for that exact version, and that npm's stored SLSA v1 provenance (`dist.attestations`, which bios trusts as npm stores it) binds to that integrity (its subject's sha512) and to its publisher's repository and release workflow at `refs/heads/main`. The publisher is bios's own, never read from npm: homescoop's `ladder-build.yml` or `publish.yml` for the `wasm-*`, `wasi-*`, `wasix-*` and `py-*` packages, and [`sources.json`](src/packages/optional/sources.json) for slicc-cdp, whose rule is `certified: "tag"`, its npm dist-tag `certified` (from the abbreviated packument), since its main released versions its cert-review didn't pass. A package with no publisher there (slicc-jsh, mcpcat, sliccy-browser at another version than its pin, or wasi-slicc, whose provenance is slicc-cli's, not homescoop's) is refused.
209
+ - an exact `name@version` with that integrity in the base set's lockfile (`src/packages/package-lock.json`) or `src/packages/certs.json`; or
210
+ - an `@ai-ecoverse/*` package whose locked integrity equals the registry's `dist.integrity` for that exact version, and that npm's stored SLSA v1 provenance (`dist.attestations`, which bios trusts as npm stores it) binds to that integrity (its subject's sha512) and to its publisher's repository and release workflow at `refs/heads/main`. The publisher is bios's own, never read from npm:
211
+ - a catalog entry's `source`: slicc-jsh's, mcpcat's and slicc-cli's (for wasi-slicc) own `release.yml`, or their `ci.yml` since they moved onto slicc-shared-web's pipeline, and slicc-cdp's `publish-sliccy-browser.yml` for sliccy-browser;
212
+ - otherwise homescoop's `ladder-build.yml` or `publish.yml` for the `wasm-*`, `wasi-*`, `wasix-*` and `py-*` packages;
213
+ - and [`sources.json`](src/packages/optional/sources.json) for a dependency-only publisher: slicc-cdp, under sliccy-browser, whose rule is `certified: "tag"`, its npm dist-tag `certified` (from the abbreviated packument), since its main released versions its cert-review didn't pass.
214
+
215
+ A release from another repository, another workflow or another ref (a maintenance branch, a tag) is refused, and so is a package with no publisher there.
216
+ - A source's `deny` and `since` refuse those versions, exact matches included: wasi-slicc 1.44.4 and everything before 1.44.1.
211
217
  - Whatever matched, homescoop's certification exceptions then apply, exact matches included: a `fail` verdict refuses that version, and a `requires` verdict refuses it unless every pin of what it requires, in the lock and the base set's lockfile (never `certs.json`'s history), is in range. bios bundles `certification-exceptions.json` at the homescoop commit `pins.test.mjs` pins ([`exceptions.json`](src/packages/optional/exceptions.json); `pins.test` keeps it byte for byte that file) and unions it with homescoop main's (`raw.githubusercontent.com`, CORS `*`), which can only deny or constrain, never grant: a tampered or missing live file is at worst a denial, and the bundled copy decides alone when it can't be fetched. Finally a package whose `engines['slicc-kernel']` or kernel peer range the base set's kernel doesn't meet is refused.
212
218
  - Anything that isn't `@ai-ecoverse/*` and isn't in the base set is refused; homescoop packages have no third-party dependencies.
213
- - **Listing:** each entry's latest goes through the same rules (with its exact `dependencies`) when it's fetched. If latest fails, bios walks back through older versions (in range, not deprecated, at most five; a version whose doc is broken is skipped) and offers the newest that passes, with `newer: { version, reason }` naming latest's blocker. A pinned entry is never walked back. If none passes, the row is `withheld` with that reason, offers no Install or Update, and an installed copy keeps Remove.
219
+ - **Listing:** each entry's latest goes through the same rules (with its exact `dependencies`) when it's fetched. If latest fails, bios walks back through older versions (in range, not deprecated, at most five; a version whose doc is broken is skipped) and offers the newest that passes, with `newer: { version, reason }` naming latest's blocker. If none passes, the row is `withheld` with that reason, offers no Install or Update, and an installed copy keeps Remove.
214
220
 
215
221
  Today it offers git, the GitHub CLI, OpenSSH, `slicc` (see [Sevens this kernel follows](#network)), Python, the Python data tools (numpy, pandas and matplotlib), Node.js (slicc-jsh's `node` and `jsh`), `sliccy:browser` for `.jsh` scripts, Ruby, Perl, Biome, the PDF tools (poppler), qpdf, rsync, screen, SQLite, the BIND tools (`dig`, `host` and `nslookup`), websocat, ntfy, GnuPG, fnox, mcpcat (an MCP client), zip (Info-ZIP's `zip`, `unzip` and `zipinfo`), xxd (vim's hex dump, with `xxd -r`) and ImageMagick. zip's `unzip` refuses an entry whose name isn't UTF-8 (an old Latin-1 or CP437 archive without a Unicode path): it says `cannot create <name>` / `Illegal byte sequence`, extracts the other entries and exits 50, and `unzip -l` still lists the entry. It never extracts it under a changed name, and `unzip -p` still streams its bytes ([homescoop#1174](https://github.com/ai-ecoverse/homescoop/issues/1174)). ImageMagick is homescoop's HEIF build (`wasm-imagemagick-heif`, a superset of the plain build, which isn't offered), with `magick`, `convert`, `mogrify`, `identify` and `magick-open`, and it reads HEIC and AVIF. A failed write leaves an existing target as it was. Its known issues: writing over another user's 0666 file in a sticky directory such as `/tmp` fails with `Operation not permitted`, though writing your own files works ([homescoop#1258](https://github.com/ai-ecoverse/homescoop/issues/1258)); `.mpc` writes fail with `cache resources exhausted` ([homescoop#1208](https://github.com/ai-ecoverse/homescoop/issues/1208)); and the cert's coverage gaps, among them the no-encoder fallback and MPC's two-file write ([homescoop#1209](https://github.com/ai-ecoverse/homescoop/issues/1209)). sudo for `magick`, `convert` or `mogrify` is offered once, never always, since they write files. No two entries' npm `latest` provide the same command, so an install never shadows another entry's; `packages.test` checks that against npm's current `latest`.
216
222
 
@@ -371,7 +377,7 @@ seven can also join a tailnet, with a patched build of Tailscale's wasm client (
371
377
  - **Half-open links:** a link that hasn't said HELLO within 30 s of its offer (a follower that gave up on a bootstrap, say) is closed, and its peer connection with it. A device's row follows its link that said HELLO, never a newer half-open one; with only a half-open link left, a known device shows as away. Until a link says HELLO its row shows only that it's connecting, under its bootstrap id: no *Asks to follow*, no Allow and no switches, because its device isn't known yet. So Allow, which keeps the grant with the device's certificate, always finds the device it was clicked for. Unlinking a device drops every link it has and tells the ones that said HELLO.
372
378
  - **Names:** each link gets an address from `198.18.57.0/24` (the first is `.11`) and the name `<host>.slicc.internal`, from the CLI's hostname, made DNS-safe and unique (`lars-mbp-2`). A link keeps its name when it reconnects, also after a reload and in any order, while the name is free: the page remembers each CLI process's name (by device certificate and process id) in `localStorage` (`slicc-os.link-labels`). A CLI that restarts is a new process, so it may come back as `lars-mbp-2` while its old link is still closing; once that link is gone, the new one takes the name back (and its SSH host key is written under it). Port 22 there is the CLI's built-in SSH server, and any other port is the CLI machine's loopback, under the CLI's policy.
373
379
  - **Into the page (`kernel-in`):** the page's HELLO offers `kernel-in`, so a local CLI serves `http://<port>.kernel.localhost` on its own loopback, on the port its HELLO names as `kernel.port` (80 when it names none; see [Kernel servers](#kernel-servers)) and carries each connection into the page as `OPEN { kind: 'kernel', port }`. The page answers with `kernel.dial({ port })` and pipes both ways with half-close under the same credit window. It refuses port 9222, the CDP facade, with `EACCES`, and anything but a port 1–65535 with `EINVAL`. A port nothing listens on gets `ECONNREFUSED` (the CLI answers 502). Every other node-opened kind is `EOPNOTSUPP`, and an OPEN without a JSON `kind` is `EPROTO`. This replaces slicc-node's kernel tunnel for a linked CLI.
374
- - **Browsers (`cdp`):** a CLI with `cdp` lists its browsers in HELLO, and later in `OPEN { kind: 'browsers' }` updates (the page offers cap `browsers`, answers OPENED and END, and replaces the list). Each browser is a runtime of the 9222 facade named `<device>/<browser>`, so `playwright-cli --runtime lars-mbp/chrome …`, or `/json/list?runtime=lars-mbp/chrome`, drives it. Each facade connection opens its own `OPEN { kind: 'cdp', browser }` stream, framed as a `u32` length plus one JSON message (at most 64 MiB, otherwise the stream is reset with `EMSGSIZE`). Without a runtime, the facade still prefers slicc-extension, then slicc-node, then a linked browser that hosts this seven (`seven: true`), then the first linked one. The Network panel lists each device's browsers with their runtimes (`devices[].browsers`), and browser automation reads `via: 'link'` when linked browsers are what it drives. Seven shares the browsers only of a slicc CLI at **1.47.0 or later** (it relies on slicc-cli #325, #331 and #335), read from its HELLO's `version`: `dev` passes, and otherwise only the numeric `major.minor.patch` counts, so a `-suffix` is ignored (a Go pseudo-version such as `1.47.1-0.2026…-abc123` passes). A HELLO without `version`, or with one that doesn't parse, counts as older. An older CLI that offers `cdp` lists no browsers, on its device row or on the facade, its `OPEN { kind: 'browsers' }` updates are ignored and a `cdp` open to it is refused with `EACCES`; its row says "Update slicc to 1.47.0 or later to share this computer's browsers." Everything else it offers (`net`, `http`, `ssh`, `ws`, `udp`, its folders and `kernel-in`) stays.
380
+ - **Browsers (`cdp`):** a CLI with `cdp` lists its browsers in HELLO, and later in `OPEN { kind: 'browsers' }` updates (the page offers cap `browsers`, answers OPENED and END, and replaces the list). Each browser is a runtime of the 9222 facade named `<device>/<browser>`, so `playwright-cli --runtime lars-mbp/chrome …`, or `/json/list?runtime=lars-mbp/chrome`, drives it. Each facade connection opens its own `OPEN { kind: 'cdp', browser }` stream, framed as a `u32` length plus one JSON message (at most 64 MiB, otherwise the stream is reset with `EMSGSIZE`). Without a runtime, the facade still prefers slicc-extension, then slicc-node, then a linked browser that hosts this seven (`seven: true`), then the first linked one. The Network panel lists each device's browsers with their runtimes (`devices[].browsers`), and browser automation reads `via: 'link'` when linked browsers are what it drives. Seven shares the browsers only of a slicc CLI at **1.47.3 or later** (it relies on slicc-cli #325, #331, #335, #376 and #388), read from its HELLO's `version`: `dev` passes, and otherwise only the numeric `major.minor.patch` counts, so a `-suffix` is ignored (a Go pseudo-version such as `1.47.4-0.2026…-abc123` passes). A HELLO without `version`, or with one that doesn't parse, counts as older. An older CLI that offers `cdp` lists no browsers, on its device row or on the facade, its `OPEN { kind: 'browsers' }` updates are ignored and a `cdp` open to it is refused with `EACCES`; its row says "Update slicc to 1.47.3 or later to share this computer's browsers." Everything else it offers (`net`, `http`, `ssh`, `ws`, `udp`, its folders and `kernel-in`) stays.
375
381
  - **Sync:** the Network panel's Sync section (spectrum's `model.tray`) shows this tray as `os/tray.js` sees it:
376
382
  - **Name and role:** seven's name, `leader` while it leads the tray (`none` when disconnected), and `hosted`.
377
383
  - **Connection:** Live, Connecting, Reconnecting, Error or Offline.
@@ -393,7 +399,7 @@ seven can also join a tailnet, with a patched build of Tailscale's wasm client (
393
399
  - A device that reconnects replaces only its own process's link, so `slicc <join> prompt` doesn't drop that machine's `follow`. Control-only links (`caps: []`) get no `.slicc.internal` name and no exit.
394
400
  - **Folders (`fs`):** a CLI started with `--mount <path>[:<name>][:ro]` lists its folders in HELLO, and later in `OPEN { kind: 'mounts' }` updates; the page offers cap `mounts`. Each is a `hostfs` source named `<device>/<name>`, so `mount -t hostfs lars-mbp/proj /mnt/proj` mounts it, and nothing is mounted unless someone asks. A bare source with no `<device>/`, as slicc-node named its folders (`mount -t hostfs project /mnt/p`, or one remembered from then), is the folder of that name on the CLI linked on this computer, when exactly one local CLI shares one. The device's row in Linked devices lists its folders (`folders`, with a Read-only badge; spectrum ≥ 1.58.0): **Mount** (`mountFolder(source)`) mounts one at `/mnt/<device>/<name>` (a folder named `.` or `..` is never listed), and **Eject** (`ejectFolder(source)`) unmounts it. The Files panel's Mounted strip shows each `hostfs` mount's source (`mountInfo()`), so `/mnt/proj · lars-mbp/proj` reads as another computer's folder. A mounted linked folder stays with the CLI process it was first granted from (`slicc-os.link-sources`), even if that process comes back under another name. If that process isn't linked, the folder goes to whichever process has the name now, but only when no other process with the same host name shares a folder of that name; otherwise it waits. So a remembered `lars-mbp/proj` never lands on another process's `proj`. A remembered linked folder that is mounted again before its CLI is back stays `nomedium`, and fills in when the CLI links. The kernel's `hostfs` hook answers such a source with a `MessagePort` (slicc-kernel ≥ 1.54.9), and the page serves it with `serveHostfsPort` over `OPEN { kind: 'fs', mount, op, … }` streams: `list` collects its NDJSON, a read over 64 KiB and every write get their own channel, and `watch` streams the CLI's change events. A read-only kernel mount, or a read-only export, sends `readonly: true` on every OPEN. A link that drops makes the mount `nomedium` until the CLI is back; files that were open reopen by path and continue if the host didn't change them.
395
401
  - **SSH host key:** the CLI's HELLO carries its host key, and the page writes `<host>.slicc.internal,<address> <key>` into the system-wide `/etc/ssh/ssh_known_hosts`, replacing older entries for that name or address, so `ssh` never trusts a key on first sight, whichever user runs it. Seven manages these keys, not the user, and OpenSSH looks up a user's own file under the passwd home, which isn't always `$HOME`.
396
- - **Tests:** `test/integration/remote.test.mjs` runs the real `slicc watch`, `slicc prompt` and `slicc abort` with `--signal stdio` against seven's real agent and a fake Bedrock. It also leads a fake tray hub and sends a `webhook.event`, which reaches the agent as a webhook lick (without its `authorization` header) and starts a run. `test/integration/tray.test.mjs` leads a fake tray hub and links a follower in the page through it, then rotates and unlinks; a remote follower that names kernel port 17180 leaves `SLICC_PAGE_LOOPBACK`, the stored port and the profile script alone. `test/integration/link.test.mjs` links a fake node in the page over a real `RTCPeerConnection`, with a HELLO that names no kernel port, port 80 or 17180, and checks `SLICC_PAGE_LOOPBACK` in login shells and, after a reload, in every program (one that offers `cdp` as version `1.46.9` or without a version shares no browsers while its net and folders still work; `1.47.0`, `dev` and a pseudo-version above share them), and, with `SLICC_CLI` set, the real `slicc attach --signal stdio`, relaying its NDJSON through the CDP binding: raw TCP and the http kind; kernel `ssh` with strict host-key checking against `--runner sh -c`, after `pnpm add -g @ai-ecoverse/wasix-openssh`; and, with the link as the exit, a name from the CLI's `--hosts` file, then refused under `--deny`; and `<port>.kernel.localhost` through the CLI's listener into a kernel server (`httptest`), with 502 for a closed port and the facade refused; a folder shared with `--mount`, mounted as `ref-node/proj`, read, written, seen changing on the host, and refused for writes when mounted `ro`; a second headless Chromium shared with `--cdp-extra`, listed and driven by `playwright-cli --runtime ref-node/<id>`; and a program WebSocket from kernel `bash` through the proxy to a `ws` echo server on the CLI's loopback: text, binary, the subprotocol, a close from each end, and `EACCES` under `--deny`; kernel `websocat`, installed from the catalog, to a `ws` echo on the CLI's loopback and, without `-k`, to `wss://echo.sliccy.ai/` (SLICC's echo, behind Cloudflare's certificate), so its own TLS checks the chain over raw TCP through the link, then failing at once under Directly and with no exit; and a kernel `python3` UDP socket (after `pnpm add -g @ai-ecoverse/wasix-python`) to a UDP echo server on the CLI's loopback, unconnected and connected, and `EACCES` on a connected socket under `--deny`, as with the fake node's own refusal. CI runs the CLI from npm, the pinned `@ai-ecoverse/slicc` devDependency.
402
+ - **Tests:** `test/integration/remote.test.mjs` runs the real `slicc watch`, `slicc prompt` and `slicc abort` with `--signal stdio` against seven's real agent and a fake Bedrock. It also leads a fake tray hub and sends a `webhook.event`, which reaches the agent as a webhook lick (without its `authorization` header) and starts a run. `test/integration/tray.test.mjs` leads a fake tray hub and links a follower in the page through it, then rotates and unlinks; a remote follower that names kernel port 17180 leaves `SLICC_PAGE_LOOPBACK`, the stored port and the profile script alone. `test/integration/link.test.mjs` links a fake node in the page over a real `RTCPeerConnection`, with a HELLO that names no kernel port, port 80 or 17180, and checks `SLICC_PAGE_LOOPBACK` in login shells and, after a reload, in every program (one that offers `cdp` as version `1.47.2` or without a version shares no browsers while its net and folders still work; `1.47.3`, `dev` and a pseudo-version above share them), and, with `SLICC_CLI` set, the real `slicc attach --signal stdio`, relaying its NDJSON through the CDP binding: raw TCP and the http kind; kernel `ssh` with strict host-key checking against `--runner sh -c`, after `pnpm add -g @ai-ecoverse/wasix-openssh`; and, with the link as the exit, a name from the CLI's `--hosts` file, then refused under `--deny`; and `<port>.kernel.localhost` through the CLI's listener into a kernel server (`httptest`), with 502 for a closed port and the facade refused; a folder shared with `--mount`, mounted as `ref-node/proj`, read, written, seen changing on the host, and refused for writes when mounted `ro`; a second headless Chromium shared with `--cdp-extra`, listed and driven by `playwright-cli --runtime ref-node/<id>`; and a program WebSocket from kernel `bash` through the proxy to a `ws` echo server on the CLI's loopback: text, binary, the subprotocol, a close from each end, and `EACCES` under `--deny`; kernel `websocat`, installed from the catalog, to a `ws` echo on the CLI's loopback and, without `-k`, to `wss://echo.sliccy.ai/` (SLICC's echo, behind Cloudflare's certificate), so its own TLS checks the chain over raw TCP through the link, then failing at once under Directly and with no exit; and a kernel `python3` UDP socket (after `pnpm add -g @ai-ecoverse/wasix-python`) to a UDP echo server on the CLI's loopback, unconnected and connected, and `EACCES` on a connected socket under `--deny`, as with the fake node's own refusal. CI runs the CLI from npm, the pinned `@ai-ecoverse/slicc` devDependency.
397
403
  - **As the exit:** a CLI linked on this computer (`npx sliccy@next`) is the exit by itself until the user chooses one, so the kernel's HTTP and raw TCP get past CORS at once, as slicc-node's proxy did; a Tailscale exit node that is on comes first. A remote device that offers to be a default exit carries other traffic only once it's chosen. The Network panel's **Internet traffic** picker (`setExit`) is one choice across Directly, Tailscale exit nodes and linked devices: choosing a device turns off Tailscale's exit node, and choosing Tailscale clears the device. The choice of a device is kept in `localStorage` (`slicc-os.exit`), and while that device isn't connected, after a reload or while it reconnects, internet traffic **waits**: raw connections get `ENETUNREACH` and HTTP fails, rather than going out directly. Choosing Directly sends it directly again and is kept (`direct`), so a local CLI doesn't take it over, while a Tailscale exit node turned on later still does; unlinking the chosen exit device goes back to the automatic choice.
398
404
  - **What the agent sees:** the transport's `traits` name the default exit while there is one, so the agent can say how its fetches go. `traits.exit` is `{ kind, caps }`:
399
405
  - **`kind`:** `local` (a CLI on this computer), `remote` (a chosen linked device) or `tailnet` (a Tailscale exit node);
@@ -535,15 +541,15 @@ Limits:
535
541
 
536
542
  ## Certified packages
537
543
 
538
- Seven certifies a gated package (slicc-kernel, slicc-cdp, slicc-clipboard, slicc-jsh, mcpcat, and the `wasi-*`, `wasix-*`, `wasm-*` and `py-*` packages) by its integrity: a version is certified when [`src/packages/certs.json`](src/packages/certs.json) (the historic list, with its recorded `exception`s) or one of the lockfiles seven ships (the base set, `/opt/agent`, the optional catalog's pins, Tailscale and the catalog's projects) carries it with that integrity. `src/seed/layer.js`'s `certifiedSet()` reads that union in the page, and a user's or a project's install that needs anything else is refused, except an optional catalog package that passes [the gate](#optional-packages), with the dependencies it pins exactly, so a user's own add of a catalog package passes or fails as the panel's Install does. homescoop's certification exceptions apply to every version there, a `certs.json` or lockfile match included, and when they can't be read the version is refused. A pin on main is certified because it reached main through the merge queue, where every integration test ran on it, and because `pins.test.mjs` checks every gated pin is its own repository's release from main. `test/integration/pins.test.mjs` checks the union covers every gated pin with its integrity (Tailscale's project included), and fails when a pinned package's `engines.slicc-kernel` or kernel peer range isn't met by the pinned kernel.
544
+ Seven certifies a gated package (slicc-kernel, slicc-cdp, slicc-clipboard, slicc-jsh, mcpcat, and the `wasi-*`, `wasix-*`, `wasm-*` and `py-*` packages) by its integrity: a version is certified when [`src/packages/certs.json`](src/packages/certs.json) (the historic list, with its recorded `exception`s) or one of the lockfiles seven ships (the base set, `/opt/agent`, Tailscale and the catalog's projects) carries it with that integrity. `src/seed/layer.js`'s `certifiedSet()` reads that union in the page, and a user's or a project's install that needs anything else is refused, except an optional catalog package that passes [the gate](#optional-packages), with the dependencies it pins exactly, so a user's own add of a catalog package passes or fails as the panel's Install does. homescoop's certification exceptions apply to every version there, a `certs.json` or lockfile match included, and when they can't be read the version is refused. A pin on main is certified because it reached main through the merge queue, where every integration test ran on it, and because `pins.test.mjs` checks every gated pin is its own repository's release from main. `test/integration/pins.test.mjs` checks the union covers every gated pin with its integrity (Tailscale's project included), and fails when a pinned package's `engines.slicc-kernel` or kernel peer range isn't met by the pinned kernel.
539
545
 
540
546
  **Provenance** (`pins.test.mjs`, an ordinary integration test): every gated pin in a lockfile seven ships has npm's SLSA provenance for that exact tarball (its subject's sha512 is the lockfile's integrity), built on `refs/heads/main` of its own repository: slicc-kernel, slicc-jsh, slicc-cdp, slicc-clipboard, mcpcat, wasm-tailscale, slicc-cli for wasi-slicc, and homescoop for the other `wasm-*`, `wasi-*`, `wasix-*` and `py-*`. A homescoop pin that the bundled `certification-exceptions.json` withdraws, or whose `requires` the pins don't meet, fails it too. The test names each pin that fails and why.
541
547
 
542
548
  ### Renovate keeps the pins current
543
549
 
544
- [`renovate.json`](renovate.json) bumps every `@ai-ecoverse/*` pin in the base set (`src/packages/package.json`), the optional catalog's four non-homescoop pins (`src/packages/optional/package.json`) and `/opt/agent` (`src/packages/agent/package.json`), with their npm and pnpm lockfiles. The catalog's homescoop entries aren't pinned, so they need no bumps; the Python data tools' project (`src/packages/optional/projects/pydata`) is pinned by hand, from the dependency dashboard.
550
+ [`renovate.json`](renovate.json) bumps every `@ai-ecoverse/*` pin in the base set (`src/packages/package.json`) and `/opt/agent` (`src/packages/agent/package.json`), with their npm and pnpm lockfiles. The optional catalog pins nothing, so it needs no bumps; the Python data tools' project (`src/packages/optional/projects/pydata`) is pinned by hand, from the dependency dashboard.
545
551
 
546
- - **PRs:** homescoop's packages (`wasi-*`, `wasix-*`, `wasm-*`, `py-*`, `wasi-pnpm` included) come as one PR a day, between 00:00 and 06:00 UTC. The kernel (its base-set and `/opt/agent` pins together), slicc-cdp, slicc-agent, slicc-spectrum and slicc-jsh each get their own PR, and so does every other package. The grammars and Tailscale projects stay on the dependency dashboard.
552
+ - **PRs:** homescoop's packages (`wasi-*`, `wasix-*`, `wasm-*`, `py-*`, `wasi-pnpm` included) come as one PR a day, between 00:00 and 06:00 UTC. The kernel (its base-set and `/opt/agent` pins together), slicc-cdp, slicc-agent and slicc-spectrum each get their own PR, and so does every other package. The grammars and Tailscale projects stay on the dependency dashboard.
547
553
  - **Automerge once CI is green:** Renovate turns on auto-merge, so a PR goes through the merge queue once its required checks, `ci / integration` and `ci / review`, are green (see [CI](#ci)). No bot writes to a Renovate branch: the shipped lockfiles are part of what seven certifies, so a certified pin needs no `certs.json` line.
548
554
  - **Known breaks** are `packageRules` with `allowedVersions`, each with its reason and issue in `description`. Today the only one is wasi-pnpm skipping the deprecated 12.9.1-11 and 12.9.1-12 (homescoop#1128). wasm-bash's hold below 5.3.0-20 went with the pin of kernel 1.135.2, which has slicc-kernel#1146. bash 5.3.0-20 and later count as certified only with kernel 1.130.2 or later (homescoop#1230), and -21 declares `engines.slicc-kernel >=1.130.2`. `test/integration/renovate.test.mjs` checks the config, the gate and the queue in plain node.
549
555
 
@@ -551,7 +557,7 @@ Seven certifies a gated package (slicc-kernel, slicc-cdp, slicc-clipboard, slicc
551
557
 
552
558
  ### The npm package
553
559
 
554
- `@ai-ecoverse/slicc-bios` on npm carries only the manifests a consumer needs to test against what seven ships: `src/packages/package.json`, `package-lock.json`, `pnpm-lock.yaml` and `certs.json`, `src/packages/agent/package.json` and `pnpm-lock.yaml`, and `src/packages/optional/package.json`, `package-lock.json` (the four non-homescoop pins), `catalog.json`, `sources.json` and `exceptions.json`. It has no runtime code and no dependencies, and those paths stay where they are. `ci.yml` releases it from `main` only, after lint, through slicc-shared-web's pipeline: semantic-release reads the conventional commits, tags `v<version>`, writes the GitHub release and publishes to npm with provenance through npm's trusted publishing (`fledgling.workflow` is `ci.yml`). The deploy to seven (`deploy.yml`) is separate and doesn't wait for it.
560
+ `@ai-ecoverse/slicc-bios` on npm carries only the manifests a consumer needs to test against what seven ships: `src/packages/package.json`, `package-lock.json`, `pnpm-lock.yaml` and `certs.json`, `src/packages/agent/package.json` and `pnpm-lock.yaml`, and `src/packages/optional/catalog.json`, `sources.json` and `exceptions.json`. It has no runtime code and no dependencies, and those paths stay where they are. `ci.yml` releases it from `main` only, after lint, through slicc-shared-web's pipeline: semantic-release reads the conventional commits, tags `v<version>`, writes the GitHub release and publishes to npm with provenance through npm's trusted publishing (`fledgling.workflow` is `ci.yml`). The deploy to seven (`deploy.yml`) is separate and doesn't wait for it.
555
561
 
556
562
  ## Develop
557
563
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-bios",
3
- "version": "1.39.17",
3
+ "version": "1.40.1",
4
4
  "description": "Slim loader that boots SLICC into OPFS and serves the UI from there",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -25,8 +25,6 @@
25
25
  "src/packages/certs.json",
26
26
  "src/packages/agent/package.json",
27
27
  "src/packages/agent/pnpm-lock.yaml",
28
- "src/packages/optional/package.json",
29
- "src/packages/optional/package-lock.json",
30
28
  "src/packages/optional/catalog.json",
31
29
  "src/packages/optional/sources.json",
32
30
  "src/packages/optional/exceptions.json"
@@ -26,6 +26,14 @@
26
26
  "@ai-ecoverse/wasi-slicc": {
27
27
  "id": "slicc",
28
28
  "label": "slicc",
29
+ "source": {
30
+ "repo": "ai-ecoverse/slicc-cli",
31
+ "workflow": [".github/workflows/release.yml", ".github/workflows/ci.yml"],
32
+ "since": "1.44.1",
33
+ "deny": {
34
+ "1.44.4": "its release run failed, so it was never certified (slicc-cli coordinator, 10-19)"
35
+ }
36
+ },
29
37
  "notes": [
30
38
  "For root and cones. The other seven must allow remote control from this one in its Linked devices."
31
39
  ]
@@ -49,6 +57,10 @@
49
57
  "@ai-ecoverse/slicc-jsh": {
50
58
  "id": "node",
51
59
  "label": "Node.js (jsh)",
60
+ "source": {
61
+ "repo": "ai-ecoverse/slicc-jsh",
62
+ "workflow": [".github/workflows/release.yml", ".github/workflows/ci.yml"]
63
+ },
52
64
  "notes": [
53
65
  "No fork, worker or asymmetric crypto yet.",
54
66
  "fetch() reaches servers inside seven (localhost, <port>.kernel.localhost)."
@@ -58,6 +70,10 @@
58
70
  "id": "jsh-browser",
59
71
  "label": "Browser for jsh scripts",
60
72
  "requires": ["node"],
73
+ "source": {
74
+ "repo": "ai-ecoverse/slicc-cdp",
75
+ "workflow": ".github/workflows/publish-sliccy-browser.yml"
76
+ },
61
77
  "notes": ["Seven's own tab and the vault stay out of reach."]
62
78
  },
63
79
  "@ai-ecoverse/wasix-ruby": {
@@ -126,6 +142,10 @@
126
142
  "@ai-ecoverse/mcpcat": {
127
143
  "id": "mcpcat",
128
144
  "label": "mcpcat (MCP client)",
145
+ "source": {
146
+ "repo": "ai-ecoverse/mcpcat",
147
+ "workflow": [".github/workflows/release.yml", ".github/workflows/ci.yml"]
148
+ },
129
149
  "notes": [
130
150
  "-H 'Authorization: Bearer ${NAME}' (single quotes) sends the vault's placeholder, which the vault fills in where a secret rule allows that host; mcpcat never prints the value."
131
151
  ]
@@ -1,78 +0,0 @@
1
- {
2
- "name": "slicc-bios-optional",
3
- "lockfileVersion": 3,
4
- "requires": true,
5
- "packages": {
6
- "": {
7
- "name": "slicc-bios-optional",
8
- "dependencies": {
9
- "@ai-ecoverse/mcpcat": "0.20.4",
10
- "@ai-ecoverse/slicc-jsh": "0.54.9",
11
- "@ai-ecoverse/sliccy-browser": "0.1.34",
12
- "@ai-ecoverse/wasi-slicc": "1.47.2"
13
- }
14
- },
15
- "node_modules/@ai-ecoverse/mcpcat": {
16
- "version": "0.20.4",
17
- "resolved": "https://registry.npmjs.org/@ai-ecoverse/mcpcat/-/mcpcat-0.20.4.tgz",
18
- "integrity": "sha512-GaK4LeMjpRT40nnOm/ZtTUKNzvtg0A4+AC/T62Fclivq5oTIBirrZG2USkthAGpFlS3BJDIT5NXaq3OdCSNt4Q==",
19
- "license": "Apache-2.0",
20
- "bin": {
21
- "mcpcat": "dist/host/mcpcat.mjs"
22
- },
23
- "engines": {
24
- "node": ">=26",
25
- "slicc-kernel": ">=1.65.2"
26
- }
27
- },
28
- "node_modules/@ai-ecoverse/slicc-cdp": {
29
- "version": "1.30.18",
30
- "resolved": "https://registry.npmjs.org/@ai-ecoverse/slicc-cdp/-/slicc-cdp-1.30.18.tgz",
31
- "integrity": "sha512-8aty5CgO3hPiZ0H8ikx60j1QzTWJmr6MGRKAFy9DMDrU/nwqPnifx1UXLX6AW6GdvDsZdRR9NpAV2A4hWfj4ZA==",
32
- "license": "Apache-2.0",
33
- "workspaces": [
34
- "packages/sliccy-browser"
35
- ],
36
- "bin": {
37
- "slicc-cdp": "src/cli.js"
38
- },
39
- "engines": {
40
- "node": ">=24"
41
- }
42
- },
43
- "node_modules/@ai-ecoverse/slicc-jsh": {
44
- "version": "0.54.9",
45
- "resolved": "https://registry.npmjs.org/@ai-ecoverse/slicc-jsh/-/slicc-jsh-0.54.9.tgz",
46
- "integrity": "sha512-bl8jBQs+sMWNkxKjsK16P6eFEpgCE/Z9aEeomK1qcu7+bhkH44Y1Ios/DaSJAxPyod+TmzcuT9iyaXYFEzhrxA==",
47
- "license": "Apache-2.0",
48
- "bin": {
49
- "jsh": "dist/host/jsh.mjs"
50
- },
51
- "engines": {
52
- "node": ">=22",
53
- "slicc-kernel": ">=1.53.2"
54
- }
55
- },
56
- "node_modules/@ai-ecoverse/sliccy-browser": {
57
- "version": "0.1.34",
58
- "resolved": "https://registry.npmjs.org/@ai-ecoverse/sliccy-browser/-/sliccy-browser-0.1.34.tgz",
59
- "integrity": "sha512-epSMU6SkUwuhCA6Ods927VtpxbrmsaajJj5o/UZhc6MG0dxj84GpNfExHDGsNvFh+jsM8gwLFFnqsgM4IRCX9w==",
60
- "license": "Apache-2.0",
61
- "dependencies": {
62
- "@ai-ecoverse/slicc-cdp": "1.30.18"
63
- },
64
- "engines": {
65
- "node": ">=24"
66
- }
67
- },
68
- "node_modules/@ai-ecoverse/wasi-slicc": {
69
- "version": "1.47.2",
70
- "resolved": "https://registry.npmjs.org/@ai-ecoverse/wasi-slicc/-/wasi-slicc-1.47.2.tgz",
71
- "integrity": "sha512-ehIKArcaerL0Ak29YeTUAD+xf1o2K0zuvk+KyRDOqvcp7AF1DBjQ8J7xiO0TRskbfWqK/dY6OQMO8HCsAUQVOQ==",
72
- "license": "Apache-2.0",
73
- "engines": {
74
- "slicc-kernel": ">=1.92.0"
75
- }
76
- }
77
- }
78
- }
@@ -1,11 +0,0 @@
1
- {
2
- "name": "slicc-bios-optional",
3
- "private": true,
4
- "description": "The optional packages that seven pins because their publisher isn't homescoop's ladder: slicc-jsh, mcpcat, wasi-slicc (slicc-cli) and sliccy-browser (slicc-cdp). Release-state and cert review certify these pins; every other catalog entry is npm's latest behind the runtime gate. Never installed as a project: the panel runs pnpm add -g for each one.",
5
- "dependencies": {
6
- "@ai-ecoverse/mcpcat": "0.20.4",
7
- "@ai-ecoverse/slicc-jsh": "0.54.9",
8
- "@ai-ecoverse/sliccy-browser": "0.1.34",
9
- "@ai-ecoverse/wasi-slicc": "1.47.2"
10
- }
11
- }