@ai-ecoverse/slicc-spectrum 1.98.1 → 1.99.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
@@ -298,6 +298,7 @@ The pickers re-read `models()` on `settings`, `accounts` and `agents`.
298
298
  When a live agent's model, or `Settings.model`, is no longer offered and its provider (the id before `/`) isn't a connected account, it moves to the offered chat model with `default: true`, else the first one; frozen agents are left alone, and so is everything on a follower unless it has Control and is connected.
299
299
  The chat re-runs this on `follow` and `readOnly` too, and `<slicc-app>` does it as well, when it binds, on those events and on `follow` and `readOnly`, so a new cone that starts on a provider that isn't set up moves at once.
300
300
  The transcript pads its end, and its `scroll-padding-block-end`, by the composer's measured height, so at the bottom the last message sits above the composer and a focused element scrolls clear of it.
301
+ The log follows new content only while it sits within one line (its `line-height`) of the bottom. Scrolling up by wheel, touch, keys or the scrollbar stops that, and the log then keeps its place, whatever re-renders or ticks; content that grows above the view, such as a tool card opening, doesn't move what's in view either. New output while the reader is scrolled up shows **Jump to latest** (`data-action="jump-latest"`), floating over the log's bottom edge above the composer, with the number of new items, "Jump to latest (2 new)", once there are any; it, or scrolling back to the bottom, resumes following, and so does the reader's own send.
301
302
  While a reply streams, its status icon follows what it is doing: a pencil for an edit, a page for a read, a magnifier for a search, an eye for the browser and an hourglass for any other tool; thinking and text keep the AI logo.
302
303
  Between the header and the transcript, a `<slicc-notices>` shows the model's notices.
303
304
  On a follower without Control, or while the agent port's `readOnly()` returns a line, a note (`p[data-follow="watch"]`, that line first, else the follow-based one) takes the composer's place, the header shows the model's name instead of the pickers and New conversation, and there's no suggested follow-up.
@@ -349,7 +350,11 @@ On a follower while the link reconnects, the field is disabled and says so; the
349
350
  While the agent port's `starting()` returns a line (no agent yet, but one is on its way), the composer stays usable: its placeholder is that line, there are no `/` or `@` popups, and sending, attachments included, adds a row above the field that reads "Sent when the agent starts" (`li.queued[data-mode="starting"]`), which can be dropped (`data-action="unqueue"`); a row with text and files says how many files go with it (`[data-files]`).
350
351
  Whenever the model changes or its agent port fires `starting` or `agents`, and the current model's port has no line (`null`, or no `starting` at all) and `agent.list()` has an agent, the composer sends the rows in order to that model's active agent (else the first one) through `agent.send`, the first like a normal send and the rest with `mode: 'queue'`, and the rows go; if the line clears with no agent, the rows stay until one comes.
351
352
  A line that comes back closes an open `/` or `@` popup.
352
- The rows are kept for the page (for the `<slicc-app>` the composer is in, else for the document), not for the model, so a model swap (an embedder that puts a new `SliccModel` on `<slicc-app>` once the agent is there), a layout switch and a dock rebuild keep them; a page reload drops them.
353
+ The rows are kept for the page (for the `<slicc-app>` the composer is in, else for the document), not for the model, so a model swap (an embedder that puts a new `SliccModel` on `<slicc-app>` once the agent is there), a layout switch and a dock rebuild keep them.
354
+ They're also kept in `sessionStorage` (`slicc-spectrum.starting`, so per origin and per tab) until they're sent or dropped: each row's text and `mode`, and only the name, type and size of its attachments, never their contents; each page (each `<slicc-app>`, or the document) rewrites only its own rows there.
355
+ Only a reload of the same tab brings them back (the navigation's type is `reload`); any other load, such as a duplicated tab that got a copy of the storage, clears them, so nothing is sent twice.
356
+ After a reload the rows come back in order with their text, and a second, muted line says "Not kept after reload: tides.csv" (`[data-lost]`), since attachments can't be restored; on arrival the text is sent without them.
357
+ A row that had only attachments shows an alert icon instead of the clock and reads "Not sent. Attach the files again." (`[data-unsent]`, with a **Dismiss** close button) instead of "Sent when the agent starts", isn't sent, and stays until it's dismissed; the "Not kept after reload" line starts with an alert icon too.
353
358
  Text typed but not sent while it starts is kept for the page the same way, and stays in the field once the agent arrives.
354
359
  While the agent holds tool results for the vault (`agent.hold()`), a status line above it says "Waiting for the vault" or "Unlock the vault to continue", with how many results are held, and offers the vault's **Unlock** button when locked and the model has a `vault`; if the browser blocks the vault window, the line says to allow pop-ups.
355
360
  With the model's `seat` set it's a guest's composer: <kbd>Enter</kbd> and the send button send through `agent.send(cone, text)`, <kbd>Shift</kbd>+<kbd>Enter</kbd> adds a line and <kbd>↑</kbd>/<kbd>↓</kbd> walk the guest's own messages; there's no steer, queue, Stop, attachments (the field is `collapsed`, so its + is hidden and inert, and pasted files are ignored), `/` commands, `@` mentions, + menu or push to talk, and it never reads files; its description is "Enter sends, Shift+Enter adds a new line."
@@ -806,6 +811,11 @@ A row with `origin: 'user'` (ids such as `user:user:<name>`) goes into Your pack
806
811
  An installed pin reads "Pinned" with `0.24.0 · seven ships 0.25.0 · in /opt/agent` ("the version seven ships" when they match), and an addition reads "Added" with `^1.2.0 → 1.2.4 · in /opt/user`; `.meta[data-pin]` is `pinned` or `added`.
807
812
  A pin that is `removing` reads "Unpinning"; other states keep their usual lights.
808
813
  Offer `unpin` for pins and `remove` for additions.
814
+ Optional `installs` lists where a package is installed, each with its `version`, `where` (a folder such as `/home/cone/.local/share/pnpm`), an optional `user` and an optional `manager` (`pnpm` or `panel`).
815
+ A row with any install never offers `install`, nor `remove` unless one install's `manager` is `panel`, and one the port still reports as `available` reads Installed, or Update available when `offered` differs, with its first install's `version`.
816
+ Each install the panel doesn't own shows in the meta line as `[data-install]`, such as "for cone, via pnpm" or "in /opt/tools", with its version in front when it differs from the row's; when the panel's copy sits next to others it reads "the panel’s copy" (`[data-install="panel"]`) and Remove's accessible name is "Remove the panel’s copy of <label>", since it removes only that copy.
817
+ When no install is the panel's, `[data-managed]` under it reads "Managed by pnpm. Remove it with `pnpm remove -g <package>`." (the command in a `code` element; or "Installed outside this panel. Remove it where it was installed." for another manager).
818
+ Offer `update` for such a row to update it in place, for that user and with that manager, never as a second copy; never offer `remove` for it.
809
819
  Emit `packages` whenever they change.
810
820
  `actPackage(id, action)` keeps the row's button pending until it settles; it shows any confirm itself, resolves without a change when that confirm is cancelled, and rejects with a displayed error on a real failure.
811
821
  Hooks: `section[data-section="packages"]`, `li[data-id][data-state]`, buttons with `data-action`, `[data-requires]`, `[data-notes]` and `[data-error]`.
@@ -948,7 +958,7 @@ Pass only the surfaces a follower has to `<slicc-app>` (agents, chat, terminal a
948
958
  The dummy supports `createDummyModel({ updates: 'boot' | 'starting' | 'current' | 'available' | 'restart' | 'failure' })`, also selectable with `?updates=` on `/ui/`.
949
959
  Fixtures stay stable for inspection; `model.updates.setScenario('current')` completes boot.
950
960
  Dummy Update now and Retry simulate download and link progress, and a dummy package install reports half its `size` in bytes on the way; Restart agent and Reload only change fixture state, never restart a real process or reload the browser.
951
- `createDummyModel({ packages: 'mixed' | 'empty' | 'fail' | 'user' | 'off' })` (or `?packages=` on `/ui/`, off by default) adds invented optional packages in every state: Install and Retry finish after a short wait and install missing requirements too, `fail` makes them fail, `user` adds a pinned agent-kit and two additions (one with a warning) that Unpin and Remove take out of the list, and removing Python while uv is installed asks through the `confirm` option (the `/ui/` page passes spectrum's `confirm`).
961
+ `createDummyModel({ packages: 'mixed' | 'empty' | 'fail' | 'user' | 'elsewhere' | 'off' })` (or `?packages=` on `/ui/`, off by default) adds invented optional packages in every state: Install and Retry finish after a short wait and install missing requirements too, `fail` makes them fail, `user` adds a pinned agent-kit and two additions (one with a warning) that Unpin and Remove take out of the list, `elsewhere` has git installed for cone with pnpm and TypeScript there at an older version, whose Update keeps it in place, and Biome both from the panel and for cone, whose Remove leaves cone's copy, and removing Python while uv is installed asks through the `confirm` option (the `/ui/` page passes spectrum's `confirm`).
952
962
  `updateFixtures`, `packageFixtures` and `DummyUpdates` are exported from `/dummy`.
953
963
  `createDummyModel({ mounts: 'mount' | 'cancel' | 'fail' | 'needs' })` (or `?mounts=` on `/ui/`) turns on the optional mount members, which are off by default: `mountFolder` mounts invented files at `/mnt/photos`, resolves with `null`, or rejects, as `model.files.mount.scenario` says; `needs` also lists `/mnt/photos` in `needsFolder()` until a folder is inserted there.
954
964
  `createDummyModel({ network: 'ok' | 'limited' | 'failing' | 'off' })` (or `?network=` on `/ui/`, default `limited`) sets the dummy network: slicc on this computer as a linked device (`route: 'link'`, with browser automation through it), the page fetch with CORS failures, or no route, all invented; `off` leaves out the port.
@@ -5131,6 +5131,24 @@ function packageFixtures(scenario) {
5131
5131
  actions: ["retry"],
5132
5132
  log: "Packages: +1\nERR_PNPM_FETCH_TIMEOUT Request timed out after 30000ms"
5133
5133
  });
5134
+ if (scenario === "elsewhere") {
5135
+ const cone = (version) => [
5136
+ { version, where: "/home/cone/.local/share/pnpm", user: "cone", manager: "pnpm" }
5137
+ ];
5138
+ patch("git", { state: "installed", version: "2.47.1", actions: [], installs: cone("2.47.1") });
5139
+ patch("tsc", {
5140
+ state: "outdated",
5141
+ version: "5.8.2",
5142
+ actions: ["update"],
5143
+ installs: cone("5.8.2")
5144
+ });
5145
+ patch("biome", {
5146
+ installs: [
5147
+ { version: "2.2.4", where: "/usr/local/share/pnpm", manager: "panel" },
5148
+ ...cone("2.2.4")
5149
+ ]
5150
+ });
5151
+ }
5134
5152
  return scenario === "user" ? [...items, ...userPackages()] : items;
5135
5153
  }
5136
5154
  var DummyUpdates = class extends Emitter {
@@ -5191,6 +5209,16 @@ var DummyUpdates = class extends Emitter {
5191
5209
  }
5192
5210
  this.#patchPackage(id, { state: "removing", error: null, actions: [] });
5193
5211
  await this.#clock.sleep(2);
5212
+ const kept = item.installs?.filter((install) => install.manager !== "panel") ?? [];
5213
+ if (kept.length) {
5214
+ this.#patchPackage(id, {
5215
+ state: "installed",
5216
+ version: kept[0].version,
5217
+ installs: kept,
5218
+ log: void 0
5219
+ });
5220
+ return;
5221
+ }
5194
5222
  this.#patchPackage(id, {
5195
5223
  state: "available",
5196
5224
  version: null,
@@ -5244,7 +5272,13 @@ ERR_PNPM_FETCH_TIMEOUT Request timed out after 30000ms`
5244
5272
  state: "installed",
5245
5273
  version: item.offered,
5246
5274
  progress: null,
5247
- actions: ["remove"],
5275
+ actions: item.installs ? [] : ["remove"],
5276
+ ...item.installs ? {
5277
+ installs: item.installs.map((install) => ({
5278
+ ...install,
5279
+ version: item.offered
5280
+ }))
5281
+ } : {},
5248
5282
  log: `${log}
5249
5283
  Progress: resolved 1, downloaded 1, added 1
5250
5284
  Done in 3.8s`