@ai-ecoverse/slicc-spectrum 1.103.3 → 1.104.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
@@ -326,6 +326,7 @@ A request that `secretRules()` flags shows a warning (`[data-secret-rules]`, the
326
326
  The command (`[data-command]`, or a guest tool call's `[data-summary]`) wraps in full; past four lines the dialog shows the first four and **Show the full command** (as in the chat, `data-action="full-command"` with `data-shown`), and shown, it scrolls inside its own box of at most `min(16lh, 50dvh)`. Always allow, its hint and Allow once wait until the end of the command has been in view (a zero-size `[data-end]` after its last line), and so does the element's `decide()` for anything but `deny`; once seen, the end stays seen while the request waits, and hiding the command again hides them again. Until then a subdued line (`[data-allow-hint]`) says "Show the full command to allow it." or, shown, "Scroll to the end of the command to allow it." The dialog keeps one width, so only its height changes. A closed request shows its command whole.
327
327
  Focus starts on Deny (on Close once `decide` rejects) and goes back, after Deny, Always allow, Allow once, <kbd>Esc</kbd> or Close, to what had it when the dialog opened; inside a terminal, to that `<slicc-terminal>` through its own `focus()`, even when it remounted meanwhile.
328
328
  Guest requests (`guest-message`, `guest-tool`) get their own copy, the guest's text as plain text (`[data-guest-text]`), attachments (`[data-attachments]`), the automatic check's verdict (`[data-verdict]`), and Deny and Allow once, plus **Always allow** (`data-action="always"`) with a hint saying what it covers when the approval's `always` is `true`; the dialog body carries `[data-approval-kind]` and fills the dialog's width at every size.
329
+ Tray requests (`tray`) get their own title and one sentence (`#asking`), and only **Deny** (`deny`, focused) and **Allow once** (`once`), never Always allow.
329
330
 
330
331
  #### `<slicc-follow>`
331
332
 
@@ -878,7 +879,7 @@ Join URLs let a device join, so the join URL, the command, the follow link and t
878
879
  A `LinkedDevice` has an `id`, a display `name` (a leading `slicc` is shown as `SLICC`), the kernel `host` and `address`, a `mode` (`local` for a CLI on this computer attached over CDP, or `remote`), `offers` (`net`, `http`, `ssh`; `ssh` adds a copyable `ssh <host>`), a one-line `policy`, `exit` (whether it can carry internet traffic), a `state` (`connecting`, `connected`, `reconnecting` or `offline`, for a device that linked before and isn't there now; every device that ever linked is listed, so one that was refused can be allowed), optional `control` (`{ allowed, fixed? }`; `fixed` for one on this computer, always allowed), optional `controlling` (`prompt` or `watch` while a CLI prompts or watches the agent) optional `browsers` (`{ runtime, name }`, listed in the row as `data-browsers`, with the runtime such as `pier-nuc/chrome` that the 9222 facade drives), optional `kind` (`cli`, or `seven` for another SLICC that follows this one, "Follower" in the row), optional `access` (`{ view?, control?, terminal?, fixed? }`: only the keys present get a switch, so a CLI sends `{ control }`; `fixed` lists them as always allowed from this computer; it replaces `control`), optional `asking` (a follower that waits to be allowed), optional `terminals` (how many terminals it has open here), an optional `note` (one warning line in the row, `[data-note]`, with the alert icon, such as a forgotten device's "Pair it again") and optional `folders` (`LinkedFolder`s the device shares: `name`, `source` such as `pier-nuc/proj`, `readonly`, and `mounted`, the mount point once mounted).
879
880
  Folders list in the row (`[data-folders] li[data-folder]`, with `data-mounted` `yes` or `no`, a **Read-only** badge and "Mounted at …"); `mountFolder(source)` adds **Mount** (`data-action="mount-folder"`, `data-id` the source; only on a connected device) and resolves with the mount point, such as `/mnt/pier-nuc/proj`, and `ejectFolder(source)` adds **Eject** (`data-action="eject-folder"`); a failure shows on the links error line.
880
881
  Each device is an `li[data-device]` with `data-state`, `data-mode` and `data-kind`, and `data-asking` while it asks; once there are devices, the join details fold into **Link another device**.
881
- An optional `pairing` (`LinkPairing`: `state` `idle`, `confirm`, `linking`, `linked` or `failed`, the node's short `code`, the device `name` once linked and a one-sentence `error` once failed) adds **Link this computer** (`section[data-pairing=<state>]`) for `slicc pair` on this computer, inside **Link another device**, which opens by itself while the state isn't `idle` or a device has a `note`; without `pairComputer` its Link stays disabled.
882
+ An optional `pairing` (`LinkPairing`: `state` `idle`, `confirm`, `linking`, `linked` or `failed`, the node's short `code`, the device `name` once linked and a one-sentence `error` once failed) adds **Link this computer** (`section[data-pairing=<state>]`) for `slicc pair` on this computer, inside **Link another device**, which opens by itself while the state isn't `idle` or an `offline` device has a `note` (one that has to be linked again; a note on a device that is still linked, such as an update, leaves it closed); without `pairComputer` its Link stays disabled.
882
883
  `idle` has the **Pairing link** field (`#pair-url`, not kept, cleared once accepted) and **Link** (`data-action="pair"`, disabled while empty; Enter submits) through `pairComputer(url)`, whose rejection shows under the field (`[data-error=pairing]`) and keeps the text.
883
884
  `confirm` shows the code and **Link** (`data-action="confirm-pairing"`, `confirmPairing()`) and **Cancel** (`data-action="cancel-pairing"`); when the state becomes `confirm`, or the panel opens on it, the block scrolls into view and its heading takes focus.
884
885
  `linking` shows a progress circle and **Cancel**, `linked` a positive status light and **Done** (`data-action="done-pairing"`), and `failed` an error box with **Try again** (`data-action="retry-pairing"`, which focuses the field) and **Dismiss** (`data-action="dismiss-pairing"`); Cancel, Done, Try again and Dismiss call `cancelPairing()`, and a polite status line (`[data-pairing-status]`) announces each state.
@@ -949,6 +950,12 @@ It offers **Deny** and **Allow once**, and, when `always` is `true`, **Always al
949
950
  `decide` gets no `reason` from the UI; a rejected `decide` shows its message inline (`[data-error]`) with only **Close**, as for sudo.
950
951
  Ended guest requests read "Answered elsewhere", "Guest message timed out" or "Guest tool call timed out".
951
952
  Without `decide`, a guest request's notice reads "Waiting for approval of a guest’s message" with "{name}'s message to {agentName} waits for the owner's approval.", or "Waiting for approval of a guest’s tool call" with "{agentName} wants to run {tool} for {name}, which waits for the owner's approval.", then "Denied automatically at {time}."
953
+ A tray request (`kind` `tray`, a `TrayApproval`) asks the owner before `slicc tray` hands out or changes this seven's join URL: `op` (`url`, `rotate` or `unlink`), `requester`, an optional `device` (`{ id, name }`, the device to unlink), `createdAt` and `expiresAt`.
954
+ It has no `always`: the dialog never offers Always allow for it, even when the item carries `always: true`, and `decide()` gets only `once` or `deny`.
955
+ Its dialog reads "Share this seven’s join URL?" with "{name} wants this seven’s join URL. Anyone with it can ask to link to this seven.", "Make a new join URL?" with "{name} wants a new join URL. Linked devices move to it, and the old one stops working.", or "Unlink {device.name}?" with "{name} wants to unlink {device.name} from this seven." ("Unlink a device?" and "…unlink a device from this seven." without `device`), then the countdown, **Deny** and **Allow once**.
956
+ Ended tray requests read "Answered in another tab" or "Tray request timed out", and a withdrawn one closes silently.
957
+ Without `decide`, its notice reads "Waiting for tray approval" with the same sentence, then "It waits for the owner's approval. Denied automatically at {time}."
958
+ An approval whose `kind` this build doesn't know shows neither a dialog nor a notice, so it waits until it ends and is never allowed from here.
952
959
 
953
960
  #### `vault` (optional)
954
961
 
@@ -1012,10 +1019,10 @@ Dummy Update now and Retry simulate download and link progress, and a dummy pack
1012
1019
  `model.network.setScenario()` switches it and Check again re-emits after a short wait.
1013
1020
  `createDummyModel({ tailnet: 'off' | 'needs-login' | 'running' | 'failed' })` (or `?tailnet=` on `/ui/`, off by default) adds an invented tailnet: an auth key starting with `tskey-` signs in, `mast-pi` and `keel-router` are online exit nodes and `buoy-laptop` is offline, and Retry on `failed` connects.
1014
1021
  While the dummy tailnet runs with an exit node, the route is `tailnet` and the health `ok`.
1015
- `createDummyModel({ links: 'none' | 'preparing' | 'linked' | 'reconnecting' | 'exit' | 'prompt' | 'denied' | 'remote' | 'followers' | 'pairing' | 'pairing-notray' | 'forgotten' })` (or `?links=` on `/ui/`, off by default) adds invented linked devices and the exit members: `linked` has quay-studio (on this computer) and pier-nuc, which can be exits and have a Chrome each, and dock-builder, which can't; quay-studio shares `sketches` and pier-nuc `proj` and a read-only `archive`, which **Mount** attaches to the dummy files at `/mnt/<source>` (with `mountInfo` naming the source) and **Eject** or the Files strip detaches; with no other browser automation, the dummy reports `via: 'link'`; `exit` sends traffic through quay-studio; `reconnecting` holds traffic for pier-nuc (health `failing`, and `ok` while the exit device is connected, both without a `detail`, as bios reports them); `remote` has pier-nuc allowed and prompting, quay-studio watching, and an offline reef-laptop that only ever connected for control; `prompt` and `denied` wait for Local Network Access, and Try again links quay-studio; `pairing` has a tray, pier-nuc and dock-builder, and `pairing` at `idle`: `pairComputer` takes a URL with a `#slicc-pair=` fragment (as `slicc pair` prints, e.g. `https://seven.sliccy.ai/#slicc-pair=17117.<token>.<fp>`) to `confirm` with the code `ab12…ef` and refuses anything else, and `confirmPairing` goes through `linking` to `linked` as SLICC on quay-studio, adding it as a local CLI row without access, or to `failed` when the URL contains `fail`; `pairing-notray` is the same without `joinUrl` and `joinCommand`, and `forgotten` adds quay-studio offline with a `note`.
1022
+ `createDummyModel({ links: 'none' | 'preparing' | 'linked' | 'reconnecting' | 'exit' | 'prompt' | 'denied' | 'remote' | 'followers' | 'pairing' | 'pairing-notray' | 'forgotten' | 'outdated' })` (or `?links=` on `/ui/`, off by default) adds invented linked devices and the exit members: `linked` has quay-studio (on this computer) and pier-nuc, which can be exits and have a Chrome each, and dock-builder, which can't; quay-studio shares `sketches` and pier-nuc `proj` and a read-only `archive`, which **Mount** attaches to the dummy files at `/mnt/<source>` (with `mountInfo` naming the source) and **Eject** or the Files strip detaches; with no other browser automation, the dummy reports `via: 'link'`; `exit` sends traffic through quay-studio; `reconnecting` holds traffic for pier-nuc (health `failing`, and `ok` while the exit device is connected, both without a `detail`, as bios reports them); `remote` has pier-nuc allowed and prompting, quay-studio watching, and an offline reef-laptop that only ever connected for control; `prompt` and `denied` wait for Local Network Access, and Try again links quay-studio; `pairing` has a tray, pier-nuc and dock-builder, and `pairing` at `idle`: `pairComputer` takes a URL with a `#slicc-pair=` fragment (as `slicc pair` prints, e.g. `https://seven.sliccy.ai/#slicc-pair=17117.<token>.<fp>`) to `confirm` with the code `ab12…ef` and refuses anything else, and `confirmPairing` goes through `linking` to `linked` as SLICC on quay-studio, adding it as a local CLI row without access, or to `failed` when the URL contains `fail`; `pairing-notray` is the same without `joinUrl` and `joinCommand`, `forgotten` adds quay-studio offline with a `note`, and `outdated` adds it connected with a `note` to update slicc.
1016
1023
  New join URL changes the URL, and `model.network.setLinks()` switches scenarios.
1017
1024
  `createDummyModel({ leaders: 'connected' | 'failed' | 'unlinked' | 'disconnected' | 'two' })` (or `?leaders=` on `/ui/`, off by default) adds invented leaders and their members: cove-studio connected, marsh-desk failed with an error, cove-studio unlinked (gone on the next status), cove-studio disconnected, or both cove-studio and marsh-desk; Retry and Reconnect move a leader to connecting, then connected, Disconnect makes it disconnected and Forget drops it, each after a short wait.
1018
- `networkFixtures`, `tailnetFixtures`, `linksFixtures`, `leadersFixtures`, `joinUrl`, `pairingCode`, `pairingRefused`, `pairingStopped`, `pairedDevice`, `forgottenDevice` and `DummyNetwork` are exported from `/dummy`.
1025
+ `networkFixtures`, `tailnetFixtures`, `linksFixtures`, `leadersFixtures`, `joinUrl`, `pairingCode`, `pairingRefused`, `pairingStopped`, `pairedDevice`, `forgottenDevice`, `outdatedDevice` and `DummyNetwork` are exported from `/dummy`.
1019
1026
  `createDummyModel({ guests: 'on' | 'none' | 'unavailable' | 'fail' | 'policy' })` (or `?guests=` on `/ui/`, off by default) adds an invented `guests` port: `on` has Seat 1 (Mara) on sliccy, connected with 2 waiting and 5 days left, and Seat 2 (Ole) on harbor, not connected and without expiry; `none` has no seats; `unavailable` has none and says guests need the tray hub, so minting rejects; `fail` makes `mint` reject with a sentence; and `policy` has the `on` seats and a `gates()` (`guestsPolicy`) that also allows tools `off`.
1020
1027
  `mint` adds a seat after a short wait and resolves with an invented `https://www.example.com/guest/…` URL, `revoke` drops the seat, and `model.guests.setScenario()` switches scenarios.
1021
1028
  `guestsFixtures`, `guestsPolicy`, `guestsUnavailable`, `guestsRefused` and `DummyGuests` are exported from `/dummy`.
@@ -1065,8 +1072,8 @@ The dummy's `docs` and `tickets` MCP accounts have a `url`, the dummy vault reco
1065
1072
  `createDummyModel({ monitor: 'agent' })` (`?monitor=agent` on `/ui/`) adds the agent's Tasks (a tree by `depth`), Processes and Recent problems sections to the monitor, and its Running vital.
1066
1073
  Like slicc-agent's snapshot, the dummy's vitals are the budget, Spend, Active agents, Processes and Process memory (both with a `series`) and Fullest context.
1067
1074
 
1068
- `createDummyModel({ approvals: 'agent' | 'user' | 'many' | 'follower' | 'fail' | 'soon' | 'elsewhere' | 'withdrawn' | 'secrets' | 'guest' | 'guest-follower' | 'guest-always' | 'long' | 'guest-args' | 'guest-empty' | 'guest-truncated' })` (`?approvals=` on `/ui/`) adds waiting sudo requests: one from an agent, one from a terminal user, three in a row (the last as `www-data`), one without `decide`, one whose `decide` fails, one that expires in 24 seconds, or two whose first is answered in another tab or withdrawn after a few seconds, or one from an agent that copies a file over `/etc/fnox/config.toml`, or (`guest`) three guest requests: a message from Seat 2 (Mara) with attachments and an allow verdict, a tool call with a deny verdict, and a message whose `decide` rejects with "Someone else already answered this."
1069
- (`guest-follower`: the same without `decide`), or (`guest-always`) one guest tool call from Seat 2 (Mara) to `read_file` with `always: true`, or (`long`) one sudo request whose seven-line command ends in `curl … | sh`, or one guest `write_file` call with a blank `summary`: with seven lines of `args` ending in the same tail (`guest-args`), with no `args` (`guest-empty`), or with a prefix of them and `argsTruncated` (`guest-truncated`).
1075
+ `createDummyModel({ approvals: 'agent' | 'user' | 'many' | 'follower' | 'fail' | 'soon' | 'elsewhere' | 'withdrawn' | 'secrets' | 'guest' | 'guest-follower' | 'guest-always' | 'long' | 'guest-args' | 'guest-empty' | 'guest-truncated' | 'tray' | 'tray-url' | 'tray-rotate' | 'tray-unlink' | 'tray-expired' })` (`?approvals=` on `/ui/`) adds waiting sudo requests: one from an agent, one from a terminal user, three in a row (the last as `www-data`), one without `decide`, one whose `decide` fails, one that expires in 24 seconds, or two whose first is answered in another tab or withdrawn after a few seconds, or one from an agent that copies a file over `/etc/fnox/config.toml`, or (`guest`) three guest requests: a message from Seat 2 (Mara) with attachments and an allow verdict, a tool call with a deny verdict, and a message whose `decide` rejects with "Someone else already answered this."
1076
+ (`guest-follower`: the same without `decide`), or (`guest-always`) one guest tool call from Seat 2 (Mara) to `read_file` with `always: true`, or (`long`) one sudo request whose seven-line command ends in `curl … | sh`, or one guest `write_file` call with a blank `summary`: with seven lines of `args` ending in the same tail (`guest-args`), with no `args` (`guest-empty`), or with a prefix of them and `argsTruncated` (`guest-truncated`), or tray requests from sliccy: one to share the join URL (`tray-url`), one to make a new one (`tray-rotate`), one to unlink Kitchen tablet (`tray-unlink`), all three in a row (`tray`), or the first, which times out after a few seconds (`tray-expired`).
1070
1077
  `createDummyModel({ asks: 'long' })` (`?asks=long`) adds a pending sudo request with that seven-line command to kitchen-sink (`k-long`).
1071
1078
  Decisions are recorded in `model.approvals.decisions`, and `model.approvals.end(id, reason)` ends a request.
1072
1079
 
@@ -128,6 +128,25 @@ function guestCall(scenario, now2) {
128
128
  always: true
129
129
  };
130
130
  }
131
+ function trayFixtures(scenario, now2) {
132
+ const requester = { kind: "agent", name: "sliccy", agentId: "cone-sliccy" };
133
+ const base = { requester, createdAt: now2, expiresAt: now2 + 5 * minutes };
134
+ const all = [
135
+ { ...base, id: "tray-url", kind: "tray", op: "url" },
136
+ { ...base, id: "tray-rotate", kind: "tray", op: "rotate", createdAt: now2 + 1 },
137
+ {
138
+ ...base,
139
+ id: "tray-unlink",
140
+ kind: "tray",
141
+ op: "unlink",
142
+ device: { id: "device-kitchen", name: "Kitchen tablet" },
143
+ createdAt: now2 + 2
144
+ }
145
+ ];
146
+ if (scenario === "tray") return all;
147
+ const id = scenario === "tray-expired" ? "tray-url" : scenario;
148
+ return all.filter((approval) => approval.id === id);
149
+ }
131
150
  function approvalFixtures(scenario, now2 = Date.now()) {
132
151
  const agent = {
133
152
  id: "sudo-jq",
@@ -173,6 +192,7 @@ function approvalFixtures(scenario, now2 = Date.now()) {
173
192
  ];
174
193
  if (scenario === "guest" || scenario === "guest-follower") return guestFixtures(now2);
175
194
  if (scenario === "guest-always") return guestAlways(now2);
195
+ if (scenario.startsWith("tray")) return trayFixtures(scenario, now2);
176
196
  if (scenario.startsWith("guest-")) return [guestCall(scenario, now2)];
177
197
  if (scenario === "none") return [];
178
198
  if (scenario === "long") return [{ ...agent, id: "sudo-release", command: longCommand }];
@@ -196,6 +216,8 @@ var DummyApprovals = class extends Emitter {
196
216
  this.decide = (id, decision) => this.#decide(id, decision);
197
217
  if (scenario === "elsewhere" || scenario === "withdrawn")
198
218
  void time.sleep(100).then(() => this.end("sudo-jq", scenario === "elsewhere" ? "answered" : "withdrawn"));
219
+ if (scenario === "tray-expired")
220
+ void time.sleep(100).then(() => this.end("tray-url", "expired"));
199
221
  }
200
222
  raise(approval) {
201
223
  this.#approvals = [...this.#approvals, approval];
@@ -4358,6 +4380,10 @@ var forgottenDevice = {
4358
4380
  state: "offline",
4359
4381
  note: "SLICC on quay-studio no longer knows this seven. Pair it again."
4360
4382
  };
4383
+ var outdatedDevice = {
4384
+ ...pairedDevice,
4385
+ note: "Update slicc to 1.47.0 or later to share this computer\u2019s browsers."
4386
+ };
4361
4387
  var others = linkedDevices.filter((device) => device.id !== pairedDevice.id);
4362
4388
  function grant(device, key, allowed) {
4363
4389
  const access = { ...device.access, [key]: allowed };
@@ -4386,9 +4412,10 @@ function linksFixtures(scenario, rotation = 0) {
4386
4412
  return { links: { ...base, devices: others, pairing: { state: "idle" } }, exit: null };
4387
4413
  if (scenario === "pairing-notray")
4388
4414
  return { links: { devices: others, permission: null, pairing: { state: "idle" } }, exit: null };
4389
- if (scenario === "forgotten") {
4415
+ if (scenario === "forgotten" || scenario === "outdated") {
4416
+ const device = scenario === "forgotten" ? forgottenDevice : outdatedDevice;
4390
4417
  return {
4391
- links: { ...base, devices: [forgottenDevice, ...others], pairing: { state: "idle" } },
4418
+ links: { ...base, devices: [device, ...others], pairing: { state: "idle" } },
4392
4419
  exit: null
4393
4420
  };
4394
4421
  }
@@ -6946,6 +6973,7 @@ export {
6946
6973
  networkFixtures,
6947
6974
  noticeFixtures,
6948
6975
  otherGuest,
6976
+ outdatedDevice,
6949
6977
  packageFixtures,
6950
6978
  page,
6951
6979
  pairedDevice,