@ai-ecoverse/slicc-spectrum 1.38.0 → 1.39.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
@@ -134,7 +134,7 @@ Text is Adobe Clean Spectrum VF, falling back to Adobe Clean, and code is Spectr
134
134
  | `<slicc-app>` | The shell: header with the agent picker, status items, the update status, the network indicator, the tray, the View menu and the theme switch, rails, and a `<slicc-dock>`. Saves the dock layout per screen class to `storage` (default `localStorage`) under `layoutKey`. While connected it sets `overscroll-behavior` to `none !important` on the page's `html` and `body`, so only scroll areas inside panels rubber-band. When the last app on the page is removed, the page's own inline values come back. |
135
135
  | `<slicc-dock>` | Generic. [dockview-core](https://dockview.dev/) 8 with a theme mapped to Spectrum tokens, in its own shadow root. Panels come from `factories` (`name → (params) => HTMLElement`). `open`, `close`, `has`, `content`, `focusPanel`, `cycleGroup`, `cycleTab`, `toJSON`, `restore`, `clear`, and the full `api`. Tabs have a context menu (close, close others, float, maximize). Events: `layout-change`, `active-panel-change`, `panel-close`. |
136
136
  | `<slicc-agents>` | Cones with their scoops, status, context fill and unread count. Selecting one makes it the active agent. |
137
- | `<slicc-chat>` | One thread's conversation (its `agent`, or the active agent if unset), by day: user messages (with attachments, tagged as steered or follow-up by `delivered`, or delegated from another cone), assistant messages (Markdown with tables, task lists, quotes, code and images; thinking; tool calls, grouped into a collapsible cluster from three in a row, each with its input (a shell command after `$`), an optional `meta` such as a timeout, an edit's `diff` shown as a diff view, and an `image`, such as a screenshot, shown as a thumbnail under the closed card and in full when open; media galleries with images, video and audio; action cards for tools, pull requests and status; plans and checklists; inline diffs; question prompts; delegations; link previews; errors with a fix-it action; a content-filter stop offers **Drop the last turn**, which rewinds that turn and puts its prompt back into the composer; when the turns right before it failed too, the button reads **Drop N failed turns** and rewinds to before the first of them, putting the latest prompt back), standalone tool results, system messages (compaction markers, error cards, notices) and licks, SLICC's events into a cone, on every channel; a lick's `severity` (`warn` or `error`) turns its channel badge into a notice or negative one with an alert icon. Permission requests are allowed or denied in place. The header shows the status, a model picker that names each model's provider and leaves out classifiers and models with `tools: false`, the thinking picker, the context meter and **New chat**, which calls `clear` and puts focus back in the composer; on a cone, when the port can freeze, its tooltip says the old chat goes to the freezer. |
137
+ | `<slicc-chat>` | One thread's conversation (its `agent`, or the active agent if unset), by day: user messages (with attachments, tagged as steered or follow-up by `delivered`, or delegated from another cone), assistant messages (Markdown with tables, task lists, quotes, code and images; thinking; tool calls, grouped into a collapsible cluster from three in a row, each with its input (a shell command after `$`), an optional `meta` such as a timeout, an edit's `diff` shown as a diff view, and an `image`, such as a screenshot, shown as a thumbnail under the closed card and in full when open; media galleries with images, video and audio; action cards for tools, pull requests and status; plans and checklists; inline diffs; question prompts; delegations; link previews; errors with a fix-it action; a content-filter stop offers **Drop the last turn**, which rewinds that turn and puts its prompt back into the composer; when the turns right before it failed too, the button reads **Drop N failed turns** and rewinds to before the first of them, putting the latest prompt back), standalone tool results, system messages (compaction markers, error cards, notices) and licks, SLICC's events into a cone, on every channel; a lick's `severity` (`warn` or `error`) turns its channel badge into a notice or negative one with an alert icon. Permission requests are allowed or denied in place. The header shows the status, a model picker that names each model's provider and leaves out classifiers and models with `tools: false`, the thinking picker, the context meter and **New conversation**, which calls `clear` and puts focus back in the composer; on a cone, when the port can freeze, its tooltip says the old conversation stays in the Freezer. |
138
138
  | `<slicc-composer>` | The message box. <kbd>Enter</kbd> sends, or steers a running reply: the message joins after the current tool round, without interrupting; <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>Enter</kbd> queues a follow-up that runs after the reply and can be withdrawn; <kbd>Shift</kbd>+<kbd>Enter</kbd> adds a line; <kbd>Tab</kbd> takes the suggested follow-up; <kbd>↑</kbd>/<kbd>↓</kbd> at the start or end walk this agent's history; <kbd>Esc</kbd> stops. `/` completes commands (`/clear`, `/compact`, `/model`, `/thinking`, `/scoop`, `/stop`, `/freeze` when the port can freeze, `/theme`) and their arguments, `@` mentions agents and files. Attach from the computer, from SLICC's files, a browser screenshot or a shared secret, or paste and drop files (25 MB each). Queued messages wait above it, to send now or drop. Model and thinking pickers, and dictation where the browser has speech recognition. Drafts are kept per agent. |
139
139
  | `<slicc-files>` | The file system as a `<slicc-file-tree>`, with pending changes as git status. Opening a file opens it in a tab. With `files.mountFolder`, **Mount a folder…** (`data-action="mount-folder"`) mounts a local folder, then selects and expands it in the tree; with `files.mounts`, a **Mounted** strip lists the mount points, each with **Eject** (`data-action="eject"`, `data-path` the mount point) when the port has `eject`. With `files.needsFolder`, a **Needs a folder** section lists the mount points waiting for a folder, each with **Insert folder…** (`data-action="insert-folder"`, `data-id` the mount point), which calls `mountFolder(path)`. A failed mount, insert or eject shows its message under the bar. |
140
140
  | `<slicc-file-view>` | One file (`path`), highlighted, with its pending change and a link to the diff. |
@@ -146,7 +146,7 @@ Text is Adobe Clean Spectrum VF, falling back to Adobe Clean, and code is Spectr
146
146
  | `<slicc-updates>` | Install / Update: component states, version changes, last checks, download and link counts, action buttons, failures with Retry, and collapsible install logs. Opens at boot until the agent is ready without failures. Background updates stay in the header's update status; new failures reopen the panel, and after a reload into a boot that isn't ready yet such a panel closes once the agent is ready. Every automatic close is saved to the layout. Also available through View and the rails. |
147
147
  | `<slicc-memory>` | What agents remember, per scope (everyone, a cone or a role), grouped by section: search, filter by tag, expand, edit, add and forget. Free text outside an entry shows “From MEMORY.md”. |
148
148
  | `<slicc-monitor>` | The live monitor: vitals with sparklines (active agents, spend, budget, fullest context), alerts, and the topology (cones and scoops, terminals, tabs, pending changes, sprinkles, tray followers). |
149
- | `<slicc-freezer>` | Frozen cones, archived with their scoops, and the chats New chat put away: thaw to bring one back, delete, or freeze the active cone (also `/freeze`). |
149
+ | `<slicc-freezer>` | Every conversation in the session, from `frozen()`: cones, scoops and agent runs, each with a kind badge, live ones first with a Live light, then the newest first. A live row offers **Open**, which selects that agent; a stopped one offers **Thaw** and **Remove**, which deletes its working folders after a confirmation. A thawed scoop or agent run keeps its row, which says what it was thawed as. **Delete cone**, in the agents list (or <kbd>Delete</kbd> on a cone's row) and the header's cone picker, stops a cone after a confirmation; `/freeze` does it at once. Either way its conversation stays here, and a row without a `kind` counts as a cone. |
150
150
  | `<slicc-sprinkle>` | A SLICC sprinkle (`.shtml`), as a panel or, with `inline`, as a dip in the chat that grows to fit. A dip has a tab-like handle: drag it into the dock (or onto a rail, or use its open-as-panel button) and it lives on as a sprinkle panel, with a note in the chat where it was; which dips are out is saved in `slicc-ui.dips`. It runs in a sandboxed frame (`allow-scripts` only) with SLICC's sprinkle theme (the `--s2-*` tokens and `.sprinkle-*` classes), the app's fonts, and Lucide icons (`<i data-lucide>`, `LucideIcons.render()`). It talks back through `slicc.lick({ action, data })`, which reaches the owning cone as a `sprinkle` lick, and, when the port has `call`, reads files and keeps state (see [Sprinkle bridge](#sprinkle-bridge)). Panel sprinkles sit at the top of the right rail with their Lucide icon. |
151
151
  | `<slicc-network>` | Network: the route and its health in plain words with the bios `detail`, a line on browser automation (`data-browser`: through the SLICC extension, through slicc-node, not available with how to get it, or `declined`, muted: off for this session, reload to be asked again) when the status has `browser`, **Check again** (`data-action="check"`) when the port has `check`, a **Tailscale** section (`data-tailnet` with the state) when the status has `tailnet`, **Get the whole web** with the Chrome extension link (`data-action="install-extension"`, when `extensionUrl` is set) and `npx @ai-ecoverse/slicc-node` with a copy button (`data-action="copy-command"`), folded away when the route already reaches the whole web, and recent failures with host, error and time. The header's **Network** indicator (`data-network`, `data-health`) opens it: a status light, green for `ok`, yellow for `limited` and red for `failing`, with the state in its text and label and `detail` as its tooltip, which adds "Tailscale is connected." while the tailnet runs; on a phone, a cloud icon for the state. Offered only when the model has a `network` port. |
152
152
  | `<slicc-tray>` | The tray indicator in the header: connection, the float's name, followers and budget; its panel adds role, runtime, spend, the follower list, the join link, and disconnect or reconnect. |
@@ -195,7 +195,7 @@ Paths are the agent's: `/shared/…` and `/home/…`, nothing else. A refusal or
195
195
 
196
196
  | Port | Later backed by | |
197
197
  | :--- | :--- | :--- |
198
- | `agent` | `@ai-ecoverse/slicc-agent` | Cones and scoops, the active agent, conversations, `send` (with attachments, as a send, a steer or a queued follow-up) and `stop`, the queue, suggestions, answers to questions, permission decisions, compaction, clearing (`clear(agentId)` starts a new chat: on a cone, a port with `freeze` first puts the old chat in the freezer as its own entry, which thaws into a cone of its own; a scoop's chat is just reset), the model per agent and new scoops. The optional `rewind(agentId, messageId)` drops the user turn before that message and everything after it, and resolves with the turn's prompt (text and attachments) for the composer, or `null`. The optional `drop(agentId)` removes a scoop: it stops and its working folder is deleted, while its conversation and reports are kept, and `createCone(name)` resolves with a new cone; the dummy rejects a cone name that is taken, and names a thawed cone `<name> (earlier)`, then `(earlier 2)` and so on, when its name is taken. `freeze(agentId)` is optional too: without it, `/freeze` and the Freezer's freeze button don't show. Assistant messages stream: the same message is re-emitted as its parts grow and its tool calls go from `running` to `done`. |
198
+ | `agent` | `@ai-ecoverse/slicc-agent` | Cones and scoops, the active agent, conversations, `send` (with attachments, as a send, a steer or a queued follow-up) and `stop`, the queue, suggestions, answers to questions, permission decisions, compaction, clearing (`clear(agentId)` starts a new conversation: on a cone, a port with `freeze` first puts the old one in the Freezer as its own row, which thaws into a cone of its own; a scoop's is just reset), the model per agent and new scoops. The optional `rewind(agentId, messageId)` drops the user turn before that message and everything after it, and resolves with the turn's prompt (text and attachments) for the composer, or `null`. The optional `drop(agentId)` removes a scoop: it stops and its working folder is deleted, while its conversation and reports are kept, and `createCone(name)` resolves with a new cone; the dummy rejects a cone name that is taken. `freeze(agentId)` is optional too: without it, **Delete cone** and `/freeze` don't show. `frozen()` lists the Freezer's rows; a `FrozenCone` may carry `kind` (`cone`, `scoop` or `agent`), `live` for a conversation that's still running, and `thawedAs`, the id of the cone a scoop or agent run was thawed into. `thaw(id)` brings a cone back, or forks a scoop or agent run into a new cone, and `discard(id)` removes a row. Names don't have to be unique. Assistant messages stream: the same message is re-emitted as its parts grow and its tool calls go from `running` to `done`. |
199
199
  | `files` | slicc-kernel's OPFS | `list`, `read`, `write`, `remove`, and pending changes with their before and after text (`accept`, `revert`). The optional `mountFolder()` asks for a local folder and resolves with its mount point (for example `/mnt/photos`), or `null` when the user cancels; the panel calls it synchronously in the click, so the port can call `showDirectoryPicker()`. The optional `mounts()` lists the mount points, and `eject(path)` unmounts one (the folder stays on disk). `mountFolder(path)` inserts the picked folder at a mount point that waits for one; the optional `needsFolder()` lists those mount points. The port emits `mounts` and then `files` after every mount and eject, including those made from the shell, and `mounts` whenever `needsFolder()` changes. |
200
200
  | `terminals` | slicc-kernel's terminal API | Terminal sessions; `backend(id)` returns a `TerminalBackend` for `<slicc-terminal>`. |
201
201
  | `browser` | `@ai-ecoverse/slicc-cdp` | Tabs, the active tab, `open`, `navigate`, `close`, `screenshot`. |
@@ -265,6 +265,7 @@ var DummyAgent = class extends Emitter {
265
265
  #next = 1;
266
266
  #cones = 1;
267
267
  #chats = 1;
268
+ #thaws = 1;
268
269
  constructor(agents2, conversations2, effects, clock2, queues2 = {}, frozen2 = []) {
269
270
  super();
270
271
  this.#clock = clock2;
@@ -579,6 +580,7 @@ var DummyAgent = class extends Emitter {
579
580
  const first = messages.find((message) => message.role === "user");
580
581
  this.#frozen.unshift({
581
582
  id,
583
+ kind: "cone",
582
584
  name: agent.name,
583
585
  title: first?.role === "user" ? first.text.slice(0, 80) : agent.name,
584
586
  model: agent.model,
@@ -685,7 +687,21 @@ var DummyAgent = class extends Emitter {
685
687
  });
686
688
  }
687
689
  frozen() {
688
- return this.#frozen.map((cone) => ({ ...cone }));
690
+ const live = this.#agents.filter((agent) => !agent.frozen).map((agent) => {
691
+ const messages = this.#messages.get(agent.id);
692
+ const first = messages.find((message) => message.role === "user");
693
+ return {
694
+ id: agent.id,
695
+ kind: agent.kind,
696
+ live: true,
697
+ name: agent.name,
698
+ title: first?.role === "user" ? first.text.slice(0, 80) : agent.name,
699
+ model: agent.model,
700
+ messages: messages.length,
701
+ frozenAt: messages.at(-1)?.createdAt ?? 0
702
+ };
703
+ });
704
+ return [...live, ...this.#frozen.map((cone) => ({ ...cone }))];
689
705
  }
690
706
  freeze(agentId) {
691
707
  const agent = this.#agent(agentId);
@@ -698,6 +714,7 @@ var DummyAgent = class extends Emitter {
698
714
  const first = messages.find((message) => message.role === "user");
699
715
  this.#frozen.unshift({
700
716
  id: agentId,
717
+ kind: "cone",
701
718
  name: agent.name,
702
719
  title: first?.role === "user" ? first.text.slice(0, 80) : agent.name,
703
720
  model: agent.model,
@@ -728,16 +745,25 @@ var DummyAgent = class extends Emitter {
728
745
  this.#changed();
729
746
  return { ...agent };
730
747
  }
731
- this.#ice.delete(id);
732
- this.#frozen = this.#frozen.filter((cone2) => cone2.id !== id);
733
- const cone = ice[0].agent;
734
- const taken = (name) => this.#agents.some((agent) => agent.kind === "cone" && agent.name === name);
735
- if (taken(cone.name)) {
736
- const base = cone.name;
737
- let n = 1;
738
- cone.name = `${base} (earlier)`;
739
- while (taken(cone.name)) cone.name = `${base} (earlier ${++n})`;
748
+ const row = this.#frozen.find((cone) => cone.id === id);
749
+ if (row.kind === "scoop" || row.kind === "agent") {
750
+ const [{ agent, messages }] = ice;
751
+ const fork = {
752
+ ...agent,
753
+ id: `${id}-thaw-${this.#thaws++}`,
754
+ kind: "cone",
755
+ parentId: null
756
+ };
757
+ this.#agents.push(fork);
758
+ this.#messages.set(fork.id, structuredClone(messages));
759
+ this.#queues.set(fork.id, []);
760
+ row.thawedAs = fork.id;
761
+ this.emit("frozen", this.frozen());
762
+ this.select(fork.id);
763
+ return { ...fork };
740
764
  }
765
+ this.#ice.delete(id);
766
+ this.#frozen = this.#frozen.filter((cone) => cone.id !== id);
741
767
  for (const { agent, messages } of ice) {
742
768
  this.#agents.push(agent);
743
769
  this.#messages.set(agent.id, messages);
@@ -1818,6 +1844,7 @@ var frozen = [
1818
1844
  {
1819
1845
  cone: {
1820
1846
  id: "cone-onboarding",
1847
+ kind: "cone",
1821
1848
  name: "onboarding",
1822
1849
  title: "Write the onboarding guide for new contributors",
1823
1850
  model: "claude-sonnet-5-5",
@@ -1832,6 +1859,7 @@ var frozen = [
1832
1859
  {
1833
1860
  cone: {
1834
1861
  id: "cone-kv-spike",
1862
+ kind: "cone",
1835
1863
  name: "kv-spike",
1836
1864
  title: "Spike: can the cache move to KV without a cold start?",
1837
1865
  model: "claude-opus-5-5",
@@ -1846,6 +1874,7 @@ var frozen = [
1846
1874
  {
1847
1875
  cone: {
1848
1876
  id: "cone-q3-report",
1877
+ kind: "cone",
1849
1878
  name: "q3-report",
1850
1879
  title: "Summarize Q3 uptime and incidents",
1851
1880
  model: "claude-haiku-4-5",
@@ -1856,6 +1885,36 @@ var frozen = [
1856
1885
  "Summarize Q3 uptime and incidents",
1857
1886
  "Uptime 99.96%. Two incidents: an expired certificate and an upstream outage."
1858
1887
  )
1888
+ },
1889
+ {
1890
+ cone: {
1891
+ id: "scoop-tide-check",
1892
+ kind: "scoop",
1893
+ name: "tide-check",
1894
+ title: "Check the tide tables against the harbor feed",
1895
+ model: "claude-haiku-4-5",
1896
+ messages: 9,
1897
+ frozenAt: now - 1 * day
1898
+ },
1899
+ messages: frozenConversation(
1900
+ "Check the tide tables against the harbor feed",
1901
+ "They match, except the 3 October low tide, which the feed lists 4 minutes later."
1902
+ )
1903
+ },
1904
+ {
1905
+ cone: {
1906
+ id: "run-nightly-digest",
1907
+ kind: "agent",
1908
+ name: "nightly-digest",
1909
+ title: "Write the nightly digest of merged pull requests",
1910
+ model: "claude-sonnet-5-5",
1911
+ messages: 6,
1912
+ frozenAt: now - 3 * day
1913
+ },
1914
+ messages: frozenConversation(
1915
+ "Write the nightly digest of merged pull requests",
1916
+ "Four pull requests merged: two fixes, a docs change and a dependency bump."
1917
+ )
1859
1918
  }
1860
1919
  ];
1861
1920
  var memories = [