@ai-ecoverse/slicc-spectrum 1.32.1 → 1.34.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, 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; 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. |
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; 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. |
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. |
@@ -142,7 +142,7 @@ Text is Adobe Clean Spectrum VF, falling back to Adobe Clean, and code is Spectr
142
142
  | `<slicc-diff-panel>` | The diff of one pending change (`path`), unified or split, with accept and revert. |
143
143
  | `<slicc-terminals>` | Terminal sessions in tabs, each a `<slicc-terminal>` on the session's `TerminalBackend`. Sessions stay alive while hidden; `exit` closes the tab. |
144
144
  | `<slicc-browser>` | Browser tabs with the agent driving each, an address bar, reload, and the active tab's screenshot. |
145
- | `<slicc-settings>` | Theme, the model and thinking level for new cones, composer and diff preferences, and accounts to connect, reconnect or disconnect. |
145
+ | `<slicc-settings>` | Theme, the model and thinking level for new cones, composer and diff preferences, and accounts to connect, reconnect or disconnect. An account whose status is `signing-in` (or a non-API-key account while its `connect` is pending) reads “Signing in…”; when the port has `cancel(id)`, its row offers Cancel (`data-action="cancel-sign-in"`), and the rejected `connect` that follows shows no error. |
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. 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). |
@@ -18,6 +18,7 @@ var DummySettings = class extends Emitter {
18
18
  #models;
19
19
  #storage;
20
20
  #clock;
21
+ #signIns = /* @__PURE__ */ new Map();
21
22
  constructor(defaults, models, accounts, storage, clock2) {
22
23
  super();
23
24
  this.#storage = storage;
@@ -54,13 +55,35 @@ var DummySettings = class extends Emitter {
54
55
  this.emit("accounts", this.accounts());
55
56
  }
56
57
  async connect(id, secret) {
57
- await this.#clock.sleep(10);
58
58
  const account = this.#accounts.find((candidate) => candidate.id === id);
59
+ if (account && account.auth !== "api-key" && !await this.#signIn(account)) return;
60
+ if (account?.auth === "api-key" || !account) await this.#clock.sleep(10);
59
61
  if (account?.auth === "api-key" && !secret)
60
62
  throw new Error(`${account.provider} needs an API key`);
61
63
  const identity = account?.auth === "api-key" ? "API key" : account?.identity || `${id}-user@example.com`;
62
64
  this.#set(id, { status: "connected", identity });
63
65
  }
66
+ #signIn(account) {
67
+ const before = this.#signIns.get(account.id)?.before ?? account.status;
68
+ this.#signIns.get(account.id)?.stop();
69
+ this.#set(account.id, { status: "signing-in" });
70
+ return new Promise((resolve) => {
71
+ const stop = () => {
72
+ this.#signIns.delete(account.id);
73
+ this.#set(account.id, { status: before });
74
+ resolve(false);
75
+ };
76
+ this.#signIns.set(account.id, { before, stop });
77
+ void this.#clock.sleep(100).then(() => {
78
+ if (this.#signIns.get(account.id)?.stop !== stop) return;
79
+ this.#signIns.delete(account.id);
80
+ resolve(true);
81
+ });
82
+ });
83
+ }
84
+ cancel(id) {
85
+ this.#signIns.get(id)?.stop();
86
+ }
64
87
  disconnect(id) {
65
88
  this.#set(id, { status: "disconnected" });
66
89
  }
@@ -70,4 +93,4 @@ export {
70
93
  clock,
71
94
  DummySettings
72
95
  };
73
- //# sourceMappingURL=chunk-43NIJXWH.js.map
96
+ //# sourceMappingURL=chunk-UKNZUMF6.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/dummy/clock.ts", "../src/dummy/settings.ts"],
4
+ "sourcesContent": ["export interface Clock {\n delay: number;\n sleep(ticks?: number): Promise<void>;\n}\n\nexport function clock(delay: number): Clock {\n return {\n delay,\n sleep: (ticks = 1) => new Promise((resolve) => setTimeout(resolve, delay * ticks)),\n };\n}\n", "import { Emitter } from '../model/emitter.ts';\nimport type {\n Account,\n AccountStatus,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '../model/types.ts';\nimport type { Clock } from './clock.ts';\n\nconst key = 'slicc-ui.settings';\n\nexport class DummySettings extends Emitter<SettingsEvents> implements SettingsPort {\n #settings: Settings;\n #accounts: Account[];\n #models: readonly ModelOption[];\n #storage: Storage | null;\n #clock: Clock;\n #signIns = new Map<string, { before: AccountStatus; stop: () => void }>();\n\n constructor(\n defaults: Settings,\n models: readonly ModelOption[],\n accounts: readonly Account[],\n storage: Storage | null,\n clock: Clock\n ) {\n super();\n this.#storage = storage;\n this.#clock = clock;\n this.#models = models;\n this.#accounts = accounts.map((account) => ({ ...account }));\n this.#settings = { ...defaults, ...this.#load() };\n }\n\n #load(): Partial<Settings> {\n try {\n return JSON.parse(this.#storage?.getItem(key) ?? '{}');\n } catch {\n return {};\n }\n }\n\n get(): Settings {\n return { ...this.#settings };\n }\n\n update(patch: Partial<Settings>): void {\n this.#settings = { ...this.#settings, ...patch };\n this.#storage?.setItem(key, JSON.stringify(this.#settings));\n this.emit('settings', this.get());\n }\n\n models(): readonly ModelOption[] {\n return this.#models;\n }\n\n accounts(): readonly Account[] {\n return this.#accounts.map((account) => ({ ...account }));\n }\n\n #set(id: string, patch: Partial<Account>): void {\n this.#accounts = this.#accounts.map((account) =>\n account.id === id ? { ...account, ...patch } : account\n );\n this.emit('accounts', this.accounts());\n }\n\n async connect(id: string, secret?: string): Promise<void> {\n const account = this.#accounts.find((candidate) => candidate.id === id);\n if (account && account.auth !== 'api-key' && !(await this.#signIn(account))) return;\n if (account?.auth === 'api-key' || !account) await this.#clock.sleep(10);\n if (account?.auth === 'api-key' && !secret)\n throw new Error(`${account.provider} needs an API key`);\n const identity =\n account?.auth === 'api-key' ? 'API key' : account?.identity || `${id}-user@example.com`;\n this.#set(id, { status: 'connected', identity });\n }\n\n #signIn(account: Account): Promise<boolean> {\n const before = this.#signIns.get(account.id)?.before ?? account.status;\n this.#signIns.get(account.id)?.stop();\n this.#set(account.id, { status: 'signing-in' });\n return new Promise((resolve) => {\n const stop = () => {\n this.#signIns.delete(account.id);\n this.#set(account.id, { status: before });\n resolve(false);\n };\n this.#signIns.set(account.id, { before, stop });\n void this.#clock.sleep(100).then(() => {\n if (this.#signIns.get(account.id)?.stop !== stop) return;\n this.#signIns.delete(account.id);\n resolve(true);\n });\n });\n }\n\n cancel(id: string): void {\n this.#signIns.get(id)?.stop();\n }\n\n disconnect(id: string): void {\n this.#set(id, { status: 'disconnected' });\n }\n}\n"],
5
+ "mappings": ";;;;;AAKO,SAAS,MAAM,OAAsB;AAC1C,SAAO;AAAA,IACL;AAAA,IACA,OAAO,CAAC,QAAQ,MAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,QAAQ,KAAK,CAAC;AAAA,EACnF;AACF;;;ACCA,IAAM,MAAM;AAEL,IAAM,gBAAN,cAA4B,QAAgD;AAAA,EACjF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW,oBAAI,IAAyD;AAAA,EAExE,YACE,UACA,QACA,UACA,SACAA,QACA;AACA,UAAM;AACN,SAAK,WAAW;AAChB,SAAK,SAASA;AACd,SAAK,UAAU;AACf,SAAK,YAAY,SAAS,IAAI,CAAC,aAAa,EAAE,GAAG,QAAQ,EAAE;AAC3D,SAAK,YAAY,EAAE,GAAG,UAAU,GAAG,KAAK,MAAM,EAAE;AAAA,EAClD;AAAA,EAEA,QAA2B;AACzB,QAAI;AACF,aAAO,KAAK,MAAM,KAAK,UAAU,QAAQ,GAAG,KAAK,IAAI;AAAA,IACvD,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAgB;AACd,WAAO,EAAE,GAAG,KAAK,UAAU;AAAA,EAC7B;AAAA,EAEA,OAAO,OAAgC;AACrC,SAAK,YAAY,EAAE,GAAG,KAAK,WAAW,GAAG,MAAM;AAC/C,SAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,KAAK,SAAS,CAAC;AAC1D,SAAK,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,EAClC;AAAA,EAEA,SAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAA+B;AAC7B,WAAO,KAAK,UAAU,IAAI,CAAC,aAAa,EAAE,GAAG,QAAQ,EAAE;AAAA,EACzD;AAAA,EAEA,KAAK,IAAY,OAA+B;AAC9C,SAAK,YAAY,KAAK,UAAU;AAAA,MAAI,CAAC,YACnC,QAAQ,OAAO,KAAK,EAAE,GAAG,SAAS,GAAG,MAAM,IAAI;AAAA,IACjD;AACA,SAAK,KAAK,YAAY,KAAK,SAAS,CAAC;AAAA,EACvC;AAAA,EAEA,MAAM,QAAQ,IAAY,QAAgC;AACxD,UAAM,UAAU,KAAK,UAAU,KAAK,CAAC,cAAc,UAAU,OAAO,EAAE;AACtE,QAAI,WAAW,QAAQ,SAAS,aAAa,CAAE,MAAM,KAAK,QAAQ,OAAO,EAAI;AAC7E,QAAI,SAAS,SAAS,aAAa,CAAC,QAAS,OAAM,KAAK,OAAO,MAAM,EAAE;AACvE,QAAI,SAAS,SAAS,aAAa,CAAC;AAClC,YAAM,IAAI,MAAM,GAAG,QAAQ,QAAQ,mBAAmB;AACxD,UAAM,WACJ,SAAS,SAAS,YAAY,YAAY,SAAS,YAAY,GAAG,EAAE;AACtE,SAAK,KAAK,IAAI,EAAE,QAAQ,aAAa,SAAS,CAAC;AAAA,EACjD;AAAA,EAEA,QAAQ,SAAoC;AAC1C,UAAM,SAAS,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG,UAAU,QAAQ;AAChE,SAAK,SAAS,IAAI,QAAQ,EAAE,GAAG,KAAK;AACpC,SAAK,KAAK,QAAQ,IAAI,EAAE,QAAQ,aAAa,CAAC;AAC9C,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,OAAO,MAAM;AACjB,aAAK,SAAS,OAAO,QAAQ,EAAE;AAC/B,aAAK,KAAK,QAAQ,IAAI,EAAE,QAAQ,OAAO,CAAC;AACxC,gBAAQ,KAAK;AAAA,MACf;AACA,WAAK,SAAS,IAAI,QAAQ,IAAI,EAAE,QAAQ,KAAK,CAAC;AAC9C,WAAK,KAAK,OAAO,MAAM,GAAG,EAAE,KAAK,MAAM;AACrC,YAAI,KAAK,SAAS,IAAI,QAAQ,EAAE,GAAG,SAAS,KAAM;AAClD,aAAK,SAAS,OAAO,QAAQ,EAAE;AAC/B,gBAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,IAAkB;AACvB,SAAK,SAAS,IAAI,EAAE,GAAG,KAAK;AAAA,EAC9B;AAAA,EAEA,WAAW,IAAkB;AAC3B,SAAK,KAAK,IAAI,EAAE,QAAQ,eAAe,CAAC;AAAA,EAC1C;AACF;",
6
+ "names": ["clock"]
7
+ }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DummySettings,
3
3
  clock
4
- } from "./chunk-43NIJXWH.js";
4
+ } from "./chunk-UKNZUMF6.js";
5
5
  import {
6
6
  Emitter
7
7
  } from "./chunk-T5NHW4KC.js";
@@ -531,8 +531,10 @@ var DummyAgent = class extends Emitter {
531
531
  }
532
532
  clear(agentId) {
533
533
  if (!this.#messages.has(agentId)) return;
534
+ this.#runs.get(agentId)?.steers.splice(0);
534
535
  this.stop(agentId);
535
536
  this.#messages.set(agentId, []);
537
+ this.#queues.set(agentId, []);
536
538
  this.#suggestions.delete(agentId);
537
539
  this.emit("messages", agentId);
538
540
  }
@@ -3028,10 +3030,12 @@ var pending = [
3028
3030
  }
3029
3031
  ];
3030
3032
  var models = [
3031
- { id: "claude-opus-5-5", label: "Claude Opus 5.5", provider: "Anthropic" },
3032
- { id: "claude-sonnet-5-5", label: "Claude Sonnet 5.5", provider: "Anthropic" },
3033
- { id: "claude-haiku-4-5", label: "Claude Haiku 4.5", provider: "Anthropic" },
3034
- { id: "local-small", label: "Local (ort-llama)", provider: "This browser" }
3033
+ { id: "claude-opus-5-5", label: "Claude Opus 5.5", provider: "Anthropic", images: true },
3034
+ { id: "claude-sonnet-5-5", label: "Claude Sonnet 5.5", provider: "Anthropic", images: true },
3035
+ { id: "claude-haiku-4-5", label: "Claude Haiku 4.5", provider: "Anthropic", images: true },
3036
+ { id: "local-small", label: "Local (ort-llama)", provider: "This browser", images: false },
3037
+ { id: "local-guard", label: "Local guard", provider: "This browser", kind: "classifier" },
3038
+ { id: "local-tiny", label: "Local tiny (no tools)", provider: "This browser", tools: false }
3035
3039
  ];
3036
3040
  var defaults = {
3037
3041
  color: "system",