@ai-ecoverse/slicc-spectrum 1.111.4 → 1.112.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
@@ -161,6 +161,8 @@ Things found while building this against wterm 0.5.4, recorded here, not pushed
161
161
  The element runs on `@wterm/ghostty` instead, which reflows on resize, keeps wrap flags in the scrollback and renders 24-bit and 256 colours exactly (xterm's 0/95/135/175/215/255 cube).
162
162
  - Ghostty resolves palette colours 0–15 to RGB inside the WASM, so wterm's `--term-color-N` variables (and `--slicc-terminal-color-N`) would stop applying.
163
163
  The element gives the core a palette of 16 marker colours (`#51cc00` … `#51cc0f`) and maps cells in those colours back to palette indexes, so the CSS variables theme them and follow a theme switch, in the scrollback too. Text drawn in exactly one of those 24-bit colours shows in the theme's palette colour instead.
164
+ From `@wterm/dom` 0.5.7, `WTerm` also reads those CSS variables on init (and on OSC 10/11) and calls `setThemeColors` on the core; the element keeps the marker palette on that path so the CSS mapping still works.
165
+ 0.5.7 also paints cell backgrounds onto a canvas, which would drop live CSS backgrounds from the spans; the element suspends that canvas so palette and 256-colour backgrounds stay on the spans.
164
166
  - Ghostty answers OSC 10 and OSC 11 queries itself, with the colours it was loaded with. The element drops those replies and answers from the theme it shows.
165
167
  - Ghostty's `scrollbackLimit` is in bytes and allocated in pages, so the number of lines it keeps depends on the width. Its default of 10,000 bytes keeps about 300 lines at 200 columns; the element asks for 4 MB.
166
168
  - `WTerm.destroy()` doesn't dispose a core passed in as `core`. The element disposes it.
@@ -324,10 +326,10 @@ The model's `notices` as in-line alerts with their actions and a close button (n
324
326
 
325
327
  The model's waiting `approvals` as a modal alert dialog, one at a time, oldest first; `<slicc-app>` mounts it.
326
328
  Renders nothing without `approvals.decide`.
327
- A request that `secretRules()` flags shows a warning (`[data-secret-rules]`, the path or token) and no **Always allow (**Always allow in this directory**, with the cwd in its hint, when the approval has `alwaysScope: 'cwd'` and a `cwd`)**.
329
+ **Always allow** (**Always allow in this directory**, with the cwd in its hint, when the approval has `alwaysScope: 'cwd'` and a `cwd`) shows when `offersAlways(approval)` is true, and the element's `decide()` sends `always` only when it holds for the live approval with that id in `list()`, so a stale dialog can't send it after the request lost it. With a level 1, 2 or 3 verdict, `offersAlways` is the approval's `always`: the embedder decides. Until every embedder sends levels, an approval without one (any other `level` counts as none) keeps the old gates: a sudo request with a `deny` decision or one `secretRules()` flags offers no Always allow, and the flagged one shows the old warning (`[data-secret-rules]`, the path or token) or the once-only hint. These gates go with the `secretRules` export.
328
330
  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.
329
331
  Control characters in the sudo command, target, requester name and place, and cwd are shown: C0 controls use Unicode Control Pictures (including ␍ for CR, ␊ for LF and ␉ for TAB), DEL uses ␡, and C1, format, default-ignorable characters and line/paragraph separators use uppercase `U+XXXX` notation (at least four digits). LF keeps its line break after ␊ in the command and shows only ␊ in paragraphs; ordinary spaces stay spaces. Each shown control has a chip with its character name or notation as its accessible label and title; Control Pictures also show a readable abbreviation or notation. Joiners and variation selectors use the same chips. The clamped and full views show the same symbols.
330
- Sudo requests show the automatic check’s verdict when supplied (`[data-verdict]`). A deny verdict reads "The automatic check would deny this: {reason}", relabels **Allow once** to **Allow anyway (once)** (`data-action="once"`), and hides Always allow and its hint even when `always` is `true`; `decide()` refuses `always` for it. The secret-rules notice still applies.
332
+ Sudo and guest requests show the verdict under the command, in a live region (`.judge[aria-live=polite]`). A level verdict is a block (`.judged[data-verdict][data-level]`): level 1 "Routine." (`routine`, positive subtle background), level 2 "Changes permissions." (`permissions`, notice) or level 3 "Could harm the system." (`harm`, negative, with the alert-triangle icon), then the reason, then "It fits what you asked for." or "It isn’t clear it serves what you asked for." when `aligned` is set. While `judging` it reads "Checking…" (`checking`); with no verdict and a `verdictError`, "No verdict: {verdictError}" (`none`). A verdict with only a `decision` keeps the line "The automatic check would {decision} this: {reason}" or "The automatic check wasn’t sure: {reason}" (`p.verdict[data-verdict]`, the decision in the attribute). The reason and `verdictError` show control characters as chips, as in the command, and are cut at 300 characters. A sudo request without Always allow says "Always allow isn’t offered for permission changes." at level 2 and "Always allow appears only with a level 1 verdict." with no verdict (`[data-verdict-note]`). No level adds a confirm step: Deny and Allow once work at every level.
331
333
  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.
332
334
  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.
333
335
  Tray requests (`tray`) get their own title and one sentence (`#asking`), and only **Deny** (`deny`, focused) and **Allow once** (`once`), never Always allow.
@@ -691,6 +693,7 @@ It takes a sudo request's command (without `sudo` and its options) and working f
691
693
  All three are `null` for a clean command, and at most one is set.
692
694
  The dialog shows "This changes secret rules" for `certain` and "This may change secret rules" for `maybe`, and offers no Always allow for either.
693
695
  For `once` it shows no notice and offers no Always allow, with "Always allow isn’t offered: xargs adds arguments from its input, and they could include a write flag." (`[data-once-only]`, the program in the attribute) when the request has `always`.
696
+ The dialog runs this check only for a sudo request without a level verdict (see `<slicc-approvals>`); with a level, the judge's verdict replaces it.
694
697
  An embedder that records "always" decisions itself, such as slicc-bios, must refuse "always" (answer "once" at most) whenever any of the three is set, so a decision sent without the dialog can't add a rule that runs it unasked.
695
698
 
696
699
  ```js
@@ -968,15 +971,15 @@ An `alone` tab shows only the brand and "SLICC is open in another tab."
968
971
 
969
972
  Later backed by: BIOS.
970
973
  `list()` returns waiting `Approval`s; emit `approvals` when the list changes.
971
- A `sudo` approval has `id`, `requester` (`kind` `agent` or `user`, `name`, optional `agentId` and `where`, such as a terminal's title), `command` (without `sudo`; bios joins the kernel's argv shell-quoted), optional `target` (the user to run as; shown as `sudo -u {target}` unless `root`), optional `cwd` and `verdict` (`{ decision: 'allow' | 'deny' | 'unsure', reason }`, the automatic check’s view), `createdAt`, `expiresAt` (epoch ms) and `always` (offer Always allow, which covers the requester's user and this exact command).
972
- With `decide(id, 'once' | 'always' | 'deny')`, which resolves once the decision is recorded and then drops the request from `list()`, the tab shows a warning alert dialog (`dialog[data-approval][data-kind][data-requester]`): "Allow sudo?", "{name} wants to run this with sudo[ in {where}]:" for an agent or "You ran this with sudo[ in {where}] (as {name}):" for a user, the command, "In {cwd}", a notice when the command changes secret rules, what Always allow does, "Denied automatically in m:ss." and how many more wait, with **Deny** (`data-action="deny"`, focused; Escape also denies), **Always allow** (`always`) and **Allow once** (`once`).
974
+ A `sudo` approval has `id`, `requester` (`kind` `agent` or `user`, `name`, optional `agentId` and `where`, such as a terminal's title), `command` (without `sudo`; bios joins the kernel's argv shell-quoted), optional `target` (the user to run as; shown as `sudo -u {target}` unless `root`), optional `cwd` and `verdict` (`{ level?: 1 | 2 | 3, aligned?: boolean, decision?: 'allow' | 'deny' | 'unsure', reason }`, the judge's view: `level` 1 routine, 2 changes permissions, 3 could harm the system; `aligned`, whether it serves what the owner asked for; `decision`, the older shape, which may be missing), optional `judging` (`true` while the judge runs) and `verdictError` (why there is no verdict), `createdAt`, `expiresAt` (epoch ms) and `always` (offer Always allow, which covers the requester's user and this exact command).
975
+ With `decide(id, 'once' | 'always' | 'deny')`, which resolves once the decision is recorded and then drops the request from `list()`, the tab shows a warning alert dialog (`dialog[data-approval][data-kind][data-requester]`): "Allow sudo?", "{name} wants to run this with sudo[ in {where}]:" for an agent or "You ran this with sudo[ in {where}] (as {name}):" for a user, the command, "In {cwd}", the verdict, what Always allow does, "Denied automatically in m:ss." and how many more wait, with **Deny** (`data-action="deny"`, focused; Escape also denies), **Always allow** (`always`) and **Allow once** (`once`).
973
976
  A command that names `/etc/fnox` (or anything in it) or `/etc/slicc/secrets-policy.toml`, resolved against `cwd`, globs included (with quotes and backslashes removed, operands after `--` included; a path with `[`, `{` or `$` counts when the part before it could lead to them), or that names `/`, `/etc` or `/etc/slicc` in a command other than a read-only one (such as `ls`, `cat`, `stat`, `grep`, `jq`, `cut`, `od`, `sha256sum` or `md5sum`, and coreutils' pure output programs, which take no output file and run nothing: `basename`, `dirname`, `printf`, `seq`, `tr`, `fold`, `paste`, `comm`, `join`, `expand`, `unexpand`, `fmt`, `numfmt`, `pr`, `expr`, `factor`, `sum`, `tsort`, `pathchk`, `base32`, `basenc`, `true`, `false`, `id`, `groups`, `whoami`, `logname`, `printenv`, `uname` and `nproc`, so `ls | xargs -n1 basename` is a read; `find` without `-exec`, `-execdir`, `-ok`, `-okdir`, `-delete` or `-fprint`/`-fls`; `sort` without `-o`, `-T` or `--compress-program`; `yq` without `-i`, `-s` or `--split-exp-file`; `base64` without `-o`; `tree` without `-o` or `-R`; `file` without `-C`; `uniq` and `xxd` with one operand at most, counting everything after `--`; a flag counts inside a short cluster, with a value attached (`-o/x`, `-i.bak`), as `--name=value` and abbreviated, but for getopt-style programs (`sort`, `uniq`, `base64`, `file`, `yq`) a cluster ends at `=` or at an option that takes a value, so `sort -to /etc` and `yq -o=json` don't count as writes; `tree` takes each option's value from the next word and reads on in the cluster, so any `o` or `R` in a `tree` cluster counts (`tree -Ho /tmp/x /etc` writes); `xxd` is read with its own rules (one option per word, values attached or in the next word, options end at the first operand), and an option's value isn't a `uniq` or `xxd` operand (`uniq -f 1 /etc`, `xxd -l 16 /etc`); `xargs` counts as read-only when every command it may run is read-only and has no gated write flags (`find /etc … | xargs grep x`, but not `xargs sort`, whose input could add `-o`); `less` and other pagers aren't, since they run commands), or runs with `cwd` in `/etc/fnox`, or names a relative path with `fnox` or `secrets-policy` in it wherever `cd` may have led, gets "This changes secret rules." with the path (`[data-secret-rules]`, the path in the attribute).
974
977
  Read-only commands count only by bare name or from `/bin` and `/usr/bin`.
975
978
  In a command made only of read-only commands and with no live expansion, the quoted words count whole: `jq '.a // .b' f` doesn't name `/`, and `jq '{a: .b}' f` in `/etc` isn't a brace that could lead to the rules.
976
979
  `/proc/<pid>/root` counts as `/` and `/proc/self/cwd` as `cwd`.
977
980
  Otherwise a command with a parameter expansion or command substitution (`$…`, backticks), a `~+` or `~-` path or a `~` path that climbs with `..`, a path through another process's `/proc/<pid>/cwd`, `fd` or `map_files` or through `/dev/fd`, an explicit write whose target comes from data (`yq -s`, `--split-exp`, `--split-exp-file`, or `xargs` running a command that isn't read-only), or more than 4096 characters, gets "This may change secret rules." with the first such part in the attribute.
978
981
  Other symlinks aren't followed.
979
- Either way there's no Always allow, even when `always` is `true` (`decide(id, 'always')` isn't called for it), so each change to the rules is asked for. The same goes for `xargs` running a gated read-only program (`once`), which shows no notice but says why Always allow isn’t offered.
982
+ For a request without a level verdict, either way there's no Always allow, even when `always` is `true` (`decide(id, 'always')` isn't called for it), so each change to the rules is asked for. The same goes for `xargs` running a gated read-only program (`once`), which shows no notice but says why Always allow isn’t offered. With a level verdict none of this applies.
980
983
  For half a second after a request shows, only Deny works, so a double-click can't approve it.
981
984
  A rejected `decide` shows its message in the dialog (`[data-error]`, `dialog[data-rejected]`) in place of the countdown, with only **Close** (`data-action="close"`, focused; Escape also closes), which moves on to the next waiting request or closes the dialog.
982
985
  Pass `decide` in every tab: the first decision wins.
@@ -984,8 +987,8 @@ When a request ends without a decision in this tab, emit `ended` with `{ id, rea
984
987
  Without `ended`, a request that leaves the list reads as expired past `expiresAt` and answered before it.
985
988
  Without `decide`, each request is a warning notice in the chat panels: "Waiting for sudo approval", who asks and the command, and "It waits for the owner's approval.
986
989
  Denied automatically at {time}."
987
- A guest request has `kind` `guest-message` (`text`, optional `attachments` of `{ name, mimeType, size }`) or `guest-tool` (`tool`, `summary`, optional `args`, the call's arguments as text, which the embedder pretty-prints, and `argsTruncated`, `true` when only a prefix of them came), plus `requester` (`kind` `guest`), `agentId`, optional `agentName`, `createdAt`, `expiresAt`, `always` (offer Always allow, a policy default the owner can change) and an optional `verdict` (`{ decision: 'allow' | 'deny' | 'unsure', reason }`, the automatic check's view).
988
- Its dialog (`[data-approval-kind]` on the body) reads "Allow a guest’s message?" with "{name} wants to send this to {agentName ?? 'this cone'}:", the text as plain text in a scrolling box (`[data-guest-text]`, never Markdown or HTML) and the attachments as name · type · size (`[data-attachments]`), or "Allow a guest’s tool call?" with "{agentName ?? 'This cone'} wants to run {tool} for {name}:" and the summary (`[data-summary]`); when the summary is blank, the block shows the tool's name on its first line and then `args` instead, under the same four-line and end-of-command rules, and with no `args` either, "This request has no details." (`[data-id="no-details"]`) and Allow as usual; with a blank summary and `argsTruncated`, the dialog never offers Allow once or Always allow and `decide()` takes only `deny`, saying "This request is too long to show in full, so it can’t be allowed." (`[data-allow-hint]`) while the args still show; then "The automatic check would allow this: {reason}", "…deny this: {reason}" or "The automatic check wasn’t sure: {reason}" (`[data-verdict]`, the decision in the attribute).
990
+ A guest request has `kind` `guest-message` (`text`, optional `attachments` of `{ name, mimeType, size }`) or `guest-tool` (`tool`, `summary`, optional `args`, the call's arguments as text, which the embedder pretty-prints, and `argsTruncated`, `true` when only a prefix of them came), plus `requester` (`kind` `guest`), `agentId`, optional `agentName`, `createdAt`, `expiresAt`, `always` (offer Always allow, a policy default the owner can change), an optional `verdict` (the same shape as a sudo approval's), and optional `judging` and `verdictError`, shown in the same verdict block.
991
+ Its dialog (`[data-approval-kind]` on the body) reads "Allow a guest’s message?" with "{name} wants to send this to {agentName ?? 'this cone'}:", the text as plain text in a scrolling box (`[data-guest-text]`, never Markdown or HTML) and the attachments as name · type · size (`[data-attachments]`), or "Allow a guest’s tool call?" with "{agentName ?? 'This cone'} wants to run {tool} for {name}:" and the summary (`[data-summary]`); when the summary is blank, the block shows the tool's name on its first line and then `args` instead, under the same four-line and end-of-command rules, and with no `args` either, "This request has no details." (`[data-id="no-details"]`) and Allow as usual; with a blank summary and `argsTruncated`, the dialog never offers Allow once or Always allow and `decide()` takes only `deny`, saying "This request is too long to show in full, so it can’t be allowed." (`[data-allow-hint]`) while the args still show; then the verdict, as for sudo.
989
992
  It offers **Deny** and **Allow once**, and, when `always` is `true`, **Always allow** (`always`, which calls `decide(id, 'always')`) with the hint "Always allow lets {name} send messages to {agentName ?? 'this cone'} without asking again." or "Always allow lets {agentName ?? 'this cone'} run exactly this {tool} call for {name} without asking again.".
990
993
  `decide` gets no `reason` from the UI; a rejected `decide` shows its message inline (`[data-error]`) with only **Close**, as for sudo.
991
994
  Ended guest requests read "Answered elsewhere", "Guest message timed out" or "Guest tool call timed out".
@@ -1115,7 +1118,7 @@ The dummy's `addServer` and `removeServer` record each call in `settings.serverE
1115
1118
  `createDummyModel({ budget: 'plain' | 'adobe' | 'xai' })` (`?budget=` on `/ui/`, `plain` by default) sets the tray budget's `provider`: none, so it reads "Budget 32%", or Adobe or SuperGrok, which the tray and the monitor's budget vital name.
1116
1119
  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.
1117
1120
 
1118
- `createDummyModel({ approvals: 'agent' | 'user' | 'many' | 'follower' | 'fail' | 'soon' | 'elsewhere' | 'withdrawn' | 'secrets' | 'judge-denied' | 'guest' | 'guest-follower' | 'guest-always' | 'long' | 'controls-cr' | 'controls-lf' | 'controls-place' | 'controls-cwd' | '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 (`judge-denied`) a terminal user’s sudo request the automatic check would deny, 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."
1121
+ `createDummyModel({ approvals: 'agent' | 'user' | 'many' | 'follower' | 'fail' | 'soon' | 'elsewhere' | 'withdrawn' | 'secrets' | 'judge-denied' | 'level-1' | 'level-2' | 'level-3' | 'checking' | 'no-verdict' | 'legacy' | 'guest' | 'guest-follower' | 'guest-always' | 'long' | 'controls-cr' | 'controls-lf' | 'controls-place' | 'controls-cwd' | '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 (`judge-denied`) a terminal user’s sudo request the automatic check would deny, or one sudo request per verdict: `level-1` (docs-pass reading `/etc/slicc/secrets-policy.toml` in `/`, with Always allow), `level-2` (a terminal user editing it), `level-3` (quiet-otter running `rm -rf /`), `checking` (`judging`), `no-verdict` (a `verdictError`) and `legacy` (an older agent's `unsure` decision without a level and `always: true` for a copy over `/etc/fnox/config.toml`, which keeps the old warning and offers no Always allow), 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."
1119
1122
  (`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`).
1120
1123
  `controls-cr` and `controls-lf` show control characters in a sudo command's quoted word; `controls-place` shows them in the requester name, place and target; `controls-cwd` shows them in the cwd.
1121
1124
  `createDummyModel({ asks: 'long' })` (`?asks=long`) adds a pending sudo request with that seven-line command to kitchen-sink (`k-long`).