@ai-ecoverse/slicc-spectrum 1.96.1 → 1.97.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 +11 -0
- package/dist/slicc-dummy.js +61 -10
- package/dist/slicc-dummy.js.map +3 -3
- package/dist/slicc-ui.js +206 -46
- package/dist/slicc-ui.js.map +3 -3
- package/dist/types/app/panels.d.ts +1 -1
- package/dist/types/dummy/agent.d.ts +4 -0
- package/dist/types/dummy/index.d.ts +2 -1
- package/dist/types/dummy/notices.d.ts +4 -2
- package/dist/types/model/types.d.ts +2 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -303,6 +303,7 @@ Between the header and the transcript, a `<slicc-notices>` shows the model's not
|
|
|
303
303
|
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.
|
|
304
304
|
An empty chat says "No messages yet.
|
|
305
305
|
Ask sliccy something." with the agent's name, and only "No messages yet." while watch-only.
|
|
306
|
+
While the agent port's `starting()` returns a line, the chat has no empty state (`[data-empty]`): the embedder's notice says what's going on.
|
|
306
307
|
With the model's `seat` set, the chat is a guest's page: the header shows only the cone's name (`[data-seat-cone]`) and its status, with no pickers, meter or New conversation; the guest's own messages (`origin` `guest` whose `seat` is the seat's `id`, or, without a `seat`, whose `from` is its `label`) read as the reader's own, with no name, while other guests keep "From {label}" and the host's messages read "From the host"; an open question or a permission request takes the normal card border, without its buttons, form, countdown or pile (no `data-pending`), and says "The host answers this."
|
|
307
308
|
(`[data-host-answers]`, subdued); once answered, it shows the answer as everyone sees it; a reviewed guest message's outcome (a notice titled "It was approved.", "Nobody approved this in time.", "It was declined." or "It was withdrawn.", with the message as its text) reads as the quoted message, cut to one line, then the outcome (`.marker[data-ended]`); an approved one shows only "It was approved.", since the delivered message follows right after it; inline sprinkles inert with "View only on this device", error cards have no action and there's no suggested follow-up; the models aren't settled, and files can't be dropped.
|
|
308
309
|
An empty guest chat says "No messages yet.
|
|
@@ -345,6 +346,11 @@ There's no row of key hints: the keys are the field's accessible description ("E
|
|
|
345
346
|
The field's status icon, which is decorative, follows the input: the AI logo when empty, a pencil while typing, a brush for a moment after <kbd>Backspace</kbd> or <kbd>Delete</kbd>, a microphone while dictating; it stays the AI logo while a reply streams, and it swaps without animation.
|
|
346
347
|
Drafts are kept per agent.
|
|
347
348
|
On a follower while the link reconnects, the field is disabled and says so; the draft stays.
|
|
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
|
+
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
|
+
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
|
+
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.
|
|
348
354
|
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.
|
|
349
355
|
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."
|
|
350
356
|
A rejected send shows the rejection's message above the field (`[data-send-error]`, `role="alert"`) and puts the draft back, ahead of anything typed since.
|
|
@@ -705,6 +711,7 @@ Assistant messages stream: the same message is re-emitted as its parts grow and
|
|
|
705
711
|
The optional `toolLabel(agentId, callIds)` resolves with a short label for a cluster of tool runs (`callIds` in order), such as "Compare the docs with the live API", or `null`; the agent dedupes, caches and persists it.
|
|
706
712
|
Spectrum asks once a cluster forms and again each time it grows, once per set of ids, and shows the label at the end of the strip.
|
|
707
713
|
The optional `readOnly()` returns a line, or `null`, with a `readOnly` event when it changes: while it's a non-empty string, such as when this follower runs a different major version from its owner, every surface is watch-only just as a follower without Control is (no composer, no cone or conversation actions, no pickers, no Stop, no Accept or Revert in Changes, inline sprinkles view only, no model settling), and the line takes the place of the follow-based watch-only note under the chat.
|
|
714
|
+
The optional `starting()` returns a line, or `null`, with a `starting` event when it changes: while it's a non-empty string, there's no agent yet but one is on its way (such as while seven installs its first agent, or replaces one), the chat drops its empty state and the composer queues what's sent until an agent is there (see `<slicc-composer>`); without it, or at `null`, nothing changes.
|
|
708
715
|
|
|
709
716
|
#### `files`
|
|
710
717
|
|
|
@@ -980,6 +987,10 @@ The SLICC you follow runs a newer version.
|
|
|
980
987
|
Update this one to send prompts.", and shows a warning notice without a close button (`[data-notice="readonly"]`) in place of any other notices.
|
|
981
988
|
`model.agent.setReadOnly(text)` changes it, and `null` ends it.
|
|
982
989
|
|
|
990
|
+
`createDummyModel({ agent: 'starting' | 'waiting' })` (`?agent=` on `/ui/`, default `ready`) starts with no agents while `starting()` returns "Seven is installing its agent.", and shows an info notice without a close button (`[data-notice="starting"]`) that goes with the line; `<slicc-app>` opens the chat with no agent (the `chat` panel, as `show('chat')` does) when it starts while `starting()` has a line and its layout has no chat; once an agent is active, that agent's chat takes the `chat` panel's place in the dock, focused if focus was in it, and the `chat` panel closes, also when a saved layout brings it back.
|
|
991
|
+
With `starting`, after 100 ticks of the dummy clock (3 s at the default delay) the usual cones arrive and the line clears; `waiting` waits for `model.agent.arrive()`, which does the same.
|
|
992
|
+
`model.agent.setStarting(line)` changes the line (and `null` clears it with no agent), and either one cancels the scheduled arrival.
|
|
993
|
+
|
|
983
994
|
`createDummyModel({ import: 'v6' | 'again' | 'fail' | 'cancel-check' })` (`?import=` on `/ui/`, off by default) adds `importProfile`, `importCancel` and `importing` to the agent: any zip previews an invented export from www.sliccy.ai (3 cones, 14 scoops, 2 frozen, Anthropic and OpenAI to sign in to again, 2 mounts and a flagged `files/cones/cone/.env`), and the import ticks to 14 in a few steps and adds 3 frozen cones with `v6: true` and the report `/root/v6-import-2026-10-18.md`; a second preview says when, and **Import again** adds fresh copies. `again` starts with an earlier import, `fail` rejects with "That zip isn’t a SLICC 6 profile export.", and `cancel-check` checks slowly. `DummyImport` and its fixtures (`importPreview`, `importCones`, `importReport`, …) are exported from `/dummy`.
|
|
984
995
|
|
|
985
996
|
The kitchen sink's “docs-pass failed” card is an expired-session error for the invented `ai-adobe` account (Adobe AI, harbor-ops@example.com, `expired`, OAuth): **Log in** goes `signing-in`, then `connected` after a short wait, and the dummy records each `connect` in `settings.connects` (`{ id, gesture }`, whether `navigator.userActivation.isActive` was true at the call); inbox-triage's “Tickets connector” card names `mcp:tickets`, a token account, so its **Log in** opens Settings at that row; `createDummyModel({ login: 'fail' })` (`?login=fail` on `/ui/`) makes OAuth sign-ins reject with “The sign-in window was closed before it finished.”
|
package/dist/slicc-dummy.js
CHANGED
|
@@ -1507,6 +1507,9 @@ var DummyAgent = class extends Emitter {
|
|
|
1507
1507
|
#frozen = [];
|
|
1508
1508
|
#hold = null;
|
|
1509
1509
|
#readOnly = null;
|
|
1510
|
+
#starting = null;
|
|
1511
|
+
#absent = [];
|
|
1512
|
+
#arrival = 0;
|
|
1510
1513
|
#ice = /* @__PURE__ */ new Map();
|
|
1511
1514
|
#suggestions = /* @__PURE__ */ new Map();
|
|
1512
1515
|
#runs = /* @__PURE__ */ new Map();
|
|
@@ -1985,6 +1988,34 @@ var DummyAgent = class extends Emitter {
|
|
|
1985
1988
|
this.#readOnly = text || null;
|
|
1986
1989
|
this.emit("readOnly", this.#readOnly);
|
|
1987
1990
|
}
|
|
1991
|
+
starting() {
|
|
1992
|
+
return this.#starting;
|
|
1993
|
+
}
|
|
1994
|
+
setStarting(line) {
|
|
1995
|
+
this.#arrival++;
|
|
1996
|
+
this.#starting = line || null;
|
|
1997
|
+
this.emit("starting", this.#starting);
|
|
1998
|
+
}
|
|
1999
|
+
withhold(line, ticks2) {
|
|
2000
|
+
this.#absent = [...this.#absent, ...this.#agents];
|
|
2001
|
+
this.#agents = [];
|
|
2002
|
+
this.#active = "";
|
|
2003
|
+
this.#changed();
|
|
2004
|
+
this.setStarting(line);
|
|
2005
|
+
if (ticks2 === void 0) return;
|
|
2006
|
+
const arrival = this.#arrival;
|
|
2007
|
+
void this.#clock.sleep(ticks2).then(() => {
|
|
2008
|
+
if (arrival === this.#arrival) this.arrive();
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
arrive() {
|
|
2012
|
+
this.#agents = [...this.#agents, ...this.#absent];
|
|
2013
|
+
this.#absent = [];
|
|
2014
|
+
this.#active = this.#agents[0]?.id ?? "";
|
|
2015
|
+
this.#changed();
|
|
2016
|
+
this.emit("active", this.#active);
|
|
2017
|
+
this.setStarting(null);
|
|
2018
|
+
}
|
|
1988
2019
|
frozen() {
|
|
1989
2020
|
const live = this.#agents.filter((agent) => !agent.frozen).map((agent) => {
|
|
1990
2021
|
const messages = this.#messages.get(agent.id);
|
|
@@ -4467,6 +4498,7 @@ var DummyNetwork = class extends Emitter {
|
|
|
4467
4498
|
|
|
4468
4499
|
// src/dummy/notices.ts
|
|
4469
4500
|
var readOnlyText = "Watch only. The SLICC you follow runs a newer version. Update this one to send prompts.";
|
|
4501
|
+
var startingText = "Seven is installing its agent.";
|
|
4470
4502
|
var noticeFixtures = {
|
|
4471
4503
|
storage: {
|
|
4472
4504
|
id: "storage",
|
|
@@ -4489,6 +4521,14 @@ var noticeFixtures = {
|
|
|
4489
4521
|
body: "The SLICC you follow moved to a newer version. Until you update SLICC on this device, it can only watch.",
|
|
4490
4522
|
actions: [],
|
|
4491
4523
|
dismissible: false
|
|
4524
|
+
},
|
|
4525
|
+
starting: {
|
|
4526
|
+
id: "starting",
|
|
4527
|
+
tone: "info",
|
|
4528
|
+
title: "Seven is installing its agent",
|
|
4529
|
+
body: "The chat starts once the agent is installed. Messages you send now wait for it.",
|
|
4530
|
+
actions: [],
|
|
4531
|
+
dismissible: false
|
|
4492
4532
|
}
|
|
4493
4533
|
};
|
|
4494
4534
|
var DummyNotices = class extends Emitter {
|
|
@@ -4499,7 +4539,7 @@ var DummyNotices = class extends Emitter {
|
|
|
4499
4539
|
super();
|
|
4500
4540
|
this.#scenario = scenario;
|
|
4501
4541
|
this.#time = time;
|
|
4502
|
-
this.#notices = scenario === "mixed" ? [noticeFixtures.storage, noticeFixtures.welcome] : scenario === "readonly" ? [noticeFixtures
|
|
4542
|
+
this.#notices = scenario === "mixed" ? [noticeFixtures.storage, noticeFixtures.welcome] : scenario === "readonly" || scenario === "starting" ? [noticeFixtures[scenario]] : [noticeFixtures.storage];
|
|
4503
4543
|
}
|
|
4504
4544
|
list() {
|
|
4505
4545
|
return this.#notices;
|
|
@@ -4515,13 +4555,19 @@ var DummyNotices = class extends Emitter {
|
|
|
4515
4555
|
this.#remove(id);
|
|
4516
4556
|
}
|
|
4517
4557
|
readOnly(on) {
|
|
4518
|
-
|
|
4558
|
+
this.#toggle("readonly", on);
|
|
4559
|
+
}
|
|
4560
|
+
starting(on) {
|
|
4561
|
+
this.#toggle("starting", on);
|
|
4562
|
+
}
|
|
4563
|
+
#toggle(id, on) {
|
|
4564
|
+
const shown = this.#notices.some((notice) => notice.id === id);
|
|
4519
4565
|
if (on === shown) return;
|
|
4520
4566
|
if (on) {
|
|
4521
|
-
this.#notices = [noticeFixtures
|
|
4567
|
+
this.#notices = [noticeFixtures[id], ...this.#notices];
|
|
4522
4568
|
this.emit("notices", this.#notices);
|
|
4523
4569
|
} else {
|
|
4524
|
-
this.#remove(
|
|
4570
|
+
this.#remove(id);
|
|
4525
4571
|
}
|
|
4526
4572
|
}
|
|
4527
4573
|
#remove(id) {
|
|
@@ -6050,6 +6096,15 @@ function importPort(scenario, agent, time) {
|
|
|
6050
6096
|
agent.importCancel = port.importCancel.bind(port);
|
|
6051
6097
|
agent.importing = port.importing.bind(port);
|
|
6052
6098
|
}
|
|
6099
|
+
function noticesPort(agent, time, notices, readonly, arrival) {
|
|
6100
|
+
if (readonly !== "off") agent.setReadOnly(readOnlyText);
|
|
6101
|
+
if (arrival !== "ready") agent.withhold(startingText, arrival === "starting" ? 100 : void 0);
|
|
6102
|
+
const notice = readonly !== "off" ? "readonly" : arrival !== "ready" ? "starting" : notices;
|
|
6103
|
+
const port = notice === "off" ? null : new DummyNotices(notice, time);
|
|
6104
|
+
if (port && readonly !== "off") agent.on("readOnly", (text) => port.readOnly(Boolean(text)));
|
|
6105
|
+
if (port && arrival !== "ready") agent.on("starting", (line) => port.starting(Boolean(line)));
|
|
6106
|
+
return port;
|
|
6107
|
+
}
|
|
6053
6108
|
function createDummyModel({
|
|
6054
6109
|
delay = 30,
|
|
6055
6110
|
storage = null,
|
|
@@ -6074,6 +6129,7 @@ function createDummyModel({
|
|
|
6074
6129
|
transport,
|
|
6075
6130
|
hold = "off",
|
|
6076
6131
|
readonly = "off",
|
|
6132
|
+
agent: arrival = "ready",
|
|
6077
6133
|
browser: scenario = "idle",
|
|
6078
6134
|
sprinkles: sprinkles2 = "builtin",
|
|
6079
6135
|
monitor = "dummy",
|
|
@@ -6103,12 +6159,7 @@ function createDummyModel({
|
|
|
6103
6159
|
};
|
|
6104
6160
|
importPort(imports, agent, time);
|
|
6105
6161
|
if (hold !== "off") agent.setHold({ reason: hold, held: 2 });
|
|
6106
|
-
|
|
6107
|
-
const notice = readonly !== "off" ? "readonly" : notices;
|
|
6108
|
-
const noticePort = notice === "off" ? null : new DummyNotices(notice, time);
|
|
6109
|
-
if (noticePort && readonly !== "off") {
|
|
6110
|
-
agent.on("readOnly", (text) => noticePort.readOnly(Boolean(text)));
|
|
6111
|
-
}
|
|
6162
|
+
const noticePort = noticesPort(agent, time, notices, readonly, arrival);
|
|
6112
6163
|
if (scenario === "driving") void browser.run(drive);
|
|
6113
6164
|
const base = {
|
|
6114
6165
|
agent,
|