@ai-ecoverse/slicc-spectrum 1.103.3 → 1.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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
 
@@ -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
 
@@ -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];