@ai-ecoverse/slicc-spectrum 1.26.0 → 1.27.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
@@ -136,7 +136,7 @@ Text is Adobe Clean Spectrum VF, falling back to Adobe Clean, and code is Spectr
136
136
  | `<slicc-agents>` | Cones with their scoops, status, context fill and unread count. Selecting one makes it the active agent. |
137
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, and an edit's `diff` shown as a diff view; 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), 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. |
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`, `/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
- | `<slicc-files>` | The file system as a `<slicc-file-tree>`, with pending changes as git status. Opening a file opens it in a tab. |
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`. A failed mount 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. |
141
141
  | `<slicc-changes>` | Pending changes with their author. Opens the diff, accepts or reverts one change or all of them. |
142
142
  | `<slicc-diff-panel>` | The diff of one pending change (`path`), unified or split, with accept and revert. |
@@ -195,7 +195,7 @@ Paths are the agent's: `/shared/…` and `/home/…`, nothing else. A refusal or
195
195
  | Port | Later backed by | |
196
196
  | :--- | :--- | :--- |
197
197
  | `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, 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 (its files stay), and `createCone(name)` resolves with a new cone; the dummy rejects a cone name that is taken. Assistant messages stream: the same message is re-emitted as its parts grow and its tool calls go from `running` to `done`. |
198
- | `files` | slicc-kernel's OPFS | `list`, `read`, `write`, `remove`, and pending changes with their before and after text (`accept`, `revert`). |
198
+ | `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). The port emits `mounts` and then `files` after every mount and eject, including those made from the shell. |
199
199
  | `terminals` | slicc-kernel's terminal API | Terminal sessions; `backend(id)` returns a `TerminalBackend` for `<slicc-terminal>`. |
200
200
  | `browser` | `@ai-ecoverse/slicc-cdp` | Tabs, the active tab, `open`, `navigate`, `close`, `screenshot`. |
201
201
  | `settings` | local storage | Theme, model, thinking level, composer and diff preferences, the models on offer, and accounts. |
@@ -205,7 +205,7 @@ Paths are the agent's: `/shared/…` and `/home/…`, nothing else. A refusal or
205
205
  | `tray` | the tray protocol | Connection, role, float kind, followers, spend and budget; `reconnect` and `disconnect`. |
206
206
  | `updates` (optional) | BIOS installers | `list()` returns `UpdateItem` rows for BIOS, kernel, agent, grammars, global pnpm packages, skills and UI. `ready()` explicitly reports agent readiness, independently of agent activity. Emit `items` whenever rows or readiness change. `act(id, action)` resolves when dispatched or rejects with a displayed error; actions are `retry`, `update-now`, `restart-agent` and `reload`. Missing ports preserve existing adapters and report disconnected information in the panel. Real installer wiring belongs to ai-ecoverse/slicc-bios#70. |
207
207
 
208
- The dummy supports `createDummyModel({ updates: 'boot' | 'current' | 'available' | 'restart' | 'failure' })`, also selectable with `?updates=` on `/ui/`. Fixtures stay stable for inspection; `model.updates.setScenario('current')` completes boot. Dummy Update now and Retry simulate download and link progress; Restart agent and Reload only change fixture state, never restart a real process or reload the browser. `updateFixtures` and `DummyUpdates` are exported from `/dummy`.
208
+ The dummy supports `createDummyModel({ updates: 'boot' | 'current' | 'available' | 'restart' | 'failure' })`, also selectable with `?updates=` on `/ui/`. Fixtures stay stable for inspection; `model.updates.setScenario('current')` completes boot. Dummy Update now and Retry simulate download and link progress; Restart agent and Reload only change fixture state, never restart a real process or reload the browser. `updateFixtures` and `DummyUpdates` are exported from `/dummy`. `createDummyModel({ mounts: 'mount' | 'cancel' | 'fail' })` (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.
209
209
 
210
210
  On phones the tray uses a compact indicator (its full status remains available on activation), the agent picker shrinks to fit, and the bottom rail scrolls when needed. The update status comes first so progress and failures remain reachable on narrow screens.
211
211
 
@@ -1912,14 +1912,25 @@ function parents(path) {
1912
1912
  return parts.map((_, i) => `/${parts.slice(0, i + 1).join("/")}`);
1913
1913
  }
1914
1914
  var DummyFiles = class extends Emitter {
1915
+ mount;
1916
+ mountFolder;
1917
+ mounts;
1918
+ eject;
1919
+ #mounted = /* @__PURE__ */ new Set();
1915
1920
  #contents = /* @__PURE__ */ new Map();
1916
1921
  #directories = /* @__PURE__ */ new Set();
1917
1922
  #modified = /* @__PURE__ */ new Map();
1918
1923
  #changes = /* @__PURE__ */ new Map();
1919
1924
  #clock;
1920
- constructor(contents, directories2, changes, clock2) {
1925
+ constructor(contents, directories2, changes, clock2, mount = null) {
1921
1926
  super();
1922
1927
  this.#clock = clock2;
1928
+ this.mount = mount;
1929
+ if (mount) {
1930
+ this.mountFolder = () => this.#mountFolder(mount);
1931
+ this.mounts = () => [...this.#mounted];
1932
+ this.eject = (path) => this.#eject(path);
1933
+ }
1923
1934
  for (const directory of directories2) this.#mkdirs(`${directory}/`);
1924
1935
  for (const [path, text] of Object.entries(contents)) this.#put(path, text, 0);
1925
1936
  for (const change of changes) this.#changes.set(change.path, { ...change });
@@ -2004,6 +2015,38 @@ var DummyFiles = class extends Emitter {
2004
2015
  else this.#put(path, change.before, Date.now());
2005
2016
  this.#announce(path);
2006
2017
  }
2018
+ async #mountFolder(mount) {
2019
+ await this.#clock.sleep();
2020
+ if (mount.scenario === "cancel") return null;
2021
+ if (mount.scenario === "fail") throw new Error("The folder couldn\u2019t be mounted.");
2022
+ const files3 = [];
2023
+ for (const [path, text] of Object.entries(mount.files)) {
2024
+ const file = `${mount.point}/${path}`;
2025
+ this.#put(file, text, Date.now());
2026
+ files3.push(file);
2027
+ }
2028
+ this.#mounted.add(mount.point);
2029
+ this.#remount(files3);
2030
+ return mount.point;
2031
+ }
2032
+ async #eject(path) {
2033
+ await this.#clock.sleep();
2034
+ if (!this.#mounted.delete(path)) throw new Error(`Nothing is mounted at ${path}.`);
2035
+ const inside = (candidate) => candidate === path || candidate.startsWith(`${path}/`);
2036
+ const files3 = [...this.#contents.keys()].filter(inside);
2037
+ for (const file of files3) {
2038
+ this.#contents.delete(file);
2039
+ this.#modified.delete(file);
2040
+ }
2041
+ for (const directory of [...this.#directories].filter(inside))
2042
+ this.#directories.delete(directory);
2043
+ this.#remount(files3);
2044
+ }
2045
+ #remount(files3) {
2046
+ this.emit("mounts", [...this.#mounted]);
2047
+ this.emit("files", this.#entries());
2048
+ for (const file of files3) this.emit("file", file);
2049
+ }
2007
2050
  };
2008
2051
 
2009
2052
  // src/dummy/showcase.ts
@@ -2919,6 +2962,13 @@ var files2 = {
2919
2962
  "/tmp/forecast-sample.json": sample
2920
2963
  };
2921
2964
  var directories = ["/sessions", "/home/user/downloads"];
2965
+ var mountPoint = "/mnt/photos";
2966
+ var mounted = {
2967
+ "README.md": "# Photos\n\nHarbor shots for the forecast pages, one album per season.\n",
2968
+ "albums/autumn.json": '{ "title": "Autumn", "shots": ["breakwater-fog", "pier-at-dusk"] }\n',
2969
+ "albums/winter.json": '{ "title": "Winter", "shots": ["ice-on-the-buoys"] }\n',
2970
+ "captions.txt": "breakwater-fog: low cloud over the outer wall\npier-at-dusk: lamps on, tide out\n"
2971
+ };
2922
2972
  var pending = [
2923
2973
  {
2924
2974
  path: "/workspace/harbor/src/lib/cache.ts",
@@ -3999,10 +4049,12 @@ var DummyUpdates = class extends Emitter {
3999
4049
  function createDummyModel({
4000
4050
  delay = 30,
4001
4051
  storage = null,
4002
- updates = "current"
4052
+ updates = "current",
4053
+ mounts = "off"
4003
4054
  } = {}) {
4004
4055
  const time = clock(delay);
4005
- const files3 = new DummyFiles(files2, directories, pending, time);
4056
+ const mount = mounts === "off" ? null : { scenario: mounts, point: mountPoint, files: mounted };
4057
+ const files3 = new DummyFiles(files2, directories, pending, time, mount);
4006
4058
  const browser = new DummyBrowser(tabs, time);
4007
4059
  const agent = new DummyAgent(
4008
4060
  agents,