@ai-ecoverse/slicc-spectrum 1.98.1 → 1.99.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
@@ -349,7 +349,11 @@ On a follower while the link reconnects, the field is disabled and says so; the
349
349
  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
350
  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
351
  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.
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.
353
+ 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.
354
+ 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.
355
+ 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.
356
+ 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
357
  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
358
  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
359
  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 +810,11 @@ A row with `origin: 'user'` (ids such as `user:user:<name>`) goes into Your pack
806
810
  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
811
  A pin that is `removing` reads "Unpinning"; other states keep their usual lights.
808
812
  Offer `unpin` for pins and `remove` for additions.
813
+ 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`).
814
+ 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`.
815
+ 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.
816
+ 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).
817
+ 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
818
  Emit `packages` whenever they change.
810
819
  `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
820
  Hooks: `section[data-section="packages"]`, `li[data-id][data-state]`, buttons with `data-action`, `[data-requires]`, `[data-notes]` and `[data-error]`.
@@ -948,7 +957,7 @@ Pass only the surfaces a follower has to `<slicc-app>` (agents, chat, terminal a
948
957
  The dummy supports `createDummyModel({ updates: 'boot' | 'starting' | 'current' | 'available' | 'restart' | 'failure' })`, also selectable with `?updates=` on `/ui/`.
949
958
  Fixtures stay stable for inspection; `model.updates.setScenario('current')` completes boot.
950
959
  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`).
960
+ `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
961
  `updateFixtures`, `packageFixtures` and `DummyUpdates` are exported from `/dummy`.
953
962
  `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
963
  `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`