@ai-ecoverse/slicc-spectrum 2.0.3 → 2.1.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 +3 -0
- package/dist/slicc-dummy.js +40 -3
- package/dist/slicc-dummy.js.map +3 -3
- package/dist/slicc-ui.js +111 -9
- package/dist/slicc-ui.js.map +3 -3
- package/dist/types/app/chat.d.ts +2 -1
- package/dist/types/dummy/agent.d.ts +6 -1
- package/dist/types/dummy/index.d.ts +2 -1
- package/dist/types/model/types.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -307,6 +307,7 @@ When a live agent's model, or `Settings.model`, is no longer offered and its pro
|
|
|
307
307
|
The chat re-runs this on `follow` and `readOnly` too, and `<slicc-app>` does it as well, when it binds, on those events and on `follow` and `readOnly`, so a new cone that starts on a provider that isn't set up moves at once.
|
|
308
308
|
The transcript pads its end, and its `scroll-padding-block-end`, by the composer's measured height plus the height its frosted layer fades out over (`--composer-fade`, 72 px, 0 under `prefers-reduced-transparency` and `forced-colors` and without a composer), so at the bottom the last line sits a line above the glass, and a focused element scrolls clear of it.
|
|
309
309
|
The log follows new content only while it sits within one line (its `line-height`) of the bottom. Scrolling up by wheel, touch, keys or the scrollbar stops that, and the log then keeps its place, whatever re-renders or ticks; content that grows above the view, such as a tool card opening, doesn't move what's in view either. New output while the reader is scrolled up shows **Jump to latest** (`data-action="jump-latest"`), floating over the log's bottom edge above the composer, with the number of new items, "Jump to latest (2 new)", once there are any; it, or scrolling back to the bottom, resumes following, and so does the reader's own send. While it follows, a log that gets shorter (a notice above it, a taller composer, a shorter panel) or a latest card that grows (a lick opened, a sprinkle loading) doesn't scroll that card's top out of view: a card that fits shows whole, and a taller one keeps its top at the top of the log, with the rest under the composer, instead of its end above the composer's padding; a new item, a streaming reply and Jump to latest still go to the very bottom.
|
|
310
|
+
With the agent port's optional `hasOlder(agentId)`, the log pages back through the conversation: while it is less than a screen from its top (which includes a log that doesn't fill the view) and `hasOlder` is true, it calls `older(agentId, id)` with the id of the first message in `messages()`, one load at a time per agent, and renders the earlier messages from `messages()` by id once the port emits `messages`, never from what `older` resolves with. The first turn in view keeps its place: its offset is measured before the render and put back before the next paint (scroll anchoring does nothing at the very top), and the log's follow bookkeeping moves with it, so the jump never reads as the reader scrolling. It loads until the log is more than a screen above the view or the start is reached; an empty answer, or one that leaves the first message as it was, is the start, and the chat doesn't ask again for that agent until its first message changes or it is shown again. While there is more, the top of the log reads "Loading earlier messages…" (`[data-older=loading]`, `aria-busy` while a load runs), and at the start it shows nothing; a rejected `older` shows "Couldn’t load earlier messages." with **Retry** (`[data-older=failed]`, `role="alert"`, `data-action="retry-older"`) instead of trying again. A load that ends after another agent is shown touches only its own conversation. Without `hasOlder` (an older agent), it never calls `older`.
|
|
310
311
|
While a reply streams, its status icon follows what it is doing: a pencil for an edit, a page for a read, a magnifier for a search, an eye for the browser and an hourglass for any other tool; thinking and text keep the AI logo.
|
|
311
312
|
Between the header and the transcript, a `<slicc-notices>` shows the model's notices.
|
|
312
313
|
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.
|
|
@@ -765,6 +766,7 @@ The optional `toolLabel(agentId, callIds)` resolves with a short label for a run
|
|
|
765
766
|
Spectrum asks once a reply's status has two calls and again each time it grows, once per set of ids, and shows the label once the status is complete.
|
|
766
767
|
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.
|
|
767
768
|
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.
|
|
769
|
+
The optional `hasOlder(agentId)` says whether `older(agentId, before)` can load messages before the first one in `messages()`; `older` resolves once at least one earlier message has arrived in `messages()`, emitting `messages`, or with `[]` at the start, and the chat pages back with them (see `<slicc-chat>`).
|
|
768
770
|
|
|
769
771
|
#### `files`
|
|
770
772
|
|
|
@@ -1099,6 +1101,7 @@ Import offers five invented candidates (one replaces a secret, one is a model ke
|
|
|
1099
1101
|
Its buttons are plain Spectrum buttons that answer at once: Unlock unlocks, and created buttons are in `model.vault.buttons`.
|
|
1100
1102
|
`vaultFixtures`, `DummyVault` and `DummyVaultButton` are exported from `/dummy`.
|
|
1101
1103
|
`createDummyModel({ hold: 'waiting' | 'locked' | 'off' })` (`?hold=` on `/ui/`) makes the agent hold two tool results; a `locked` hold clears when the dummy vault unlocks, and `model.agent.setHold(hold)` changes it.
|
|
1104
|
+
`createDummyModel({ older: 'paged' })` (`?older=paged` on `/ui/`, off by default) holds back all but the last six messages of every conversation, and the agent's `older` brings them back four at a time after a short wait (`model.agent.olderTicks`, 10 ticks); `model.agent.paginate(keep, size, ids?)` holds them back again for those agents (all by default), `failOlder(times)` makes the next loads reject, and `olderCalls` lists the agents each load was for. `hasOlder` is false wherever nothing is held back.
|
|
1102
1105
|
|
|
1103
1106
|
On phones the update status and the tray connection show only their status light (the tray's budget is hidden) and the network indicator a cloud icon, with the text in their labels and tooltips; the agent picker keeps room for the cone's name, cut with an ellipsis when long, and the bottom rail scrolls when needed.
|
|
1104
1107
|
On tablets the agent picker gives up room before the View menu does, so the header keeps the View menu and its last button in view from 640 to 1199 px, the tray's chips stay on one line each, the budget chip leaves out its window and a tray that isn't live shows only its state.
|
package/dist/slicc-dummy.js
CHANGED
|
@@ -1916,6 +1916,11 @@ var DummyAgent = class extends Emitter {
|
|
|
1916
1916
|
#ice = /* @__PURE__ */ new Map();
|
|
1917
1917
|
#suggestions = /* @__PURE__ */ new Map();
|
|
1918
1918
|
#runs = /* @__PURE__ */ new Map();
|
|
1919
|
+
#earlier = /* @__PURE__ */ new Map();
|
|
1920
|
+
#page = 0;
|
|
1921
|
+
#failing = 0;
|
|
1922
|
+
olderTicks = 10;
|
|
1923
|
+
olderCalls = [];
|
|
1919
1924
|
#active;
|
|
1920
1925
|
#clock;
|
|
1921
1926
|
#effects;
|
|
@@ -2278,6 +2283,7 @@ var DummyAgent = class extends Emitter {
|
|
|
2278
2283
|
const messages = this.#messages.get(agentId);
|
|
2279
2284
|
if (agent.kind === "cone" && messages.length) this.#shelve(agent, messages);
|
|
2280
2285
|
this.#messages.set(agentId, []);
|
|
2286
|
+
this.#earlier.delete(agentId);
|
|
2281
2287
|
this.#queues.set(agentId, []);
|
|
2282
2288
|
this.#suggestions.delete(agentId);
|
|
2283
2289
|
this.emit("messages", agentId);
|
|
@@ -2309,8 +2315,36 @@ var DummyAgent = class extends Emitter {
|
|
|
2309
2315
|
agent.thinking = thinking;
|
|
2310
2316
|
this.#changed();
|
|
2311
2317
|
}
|
|
2312
|
-
|
|
2313
|
-
|
|
2318
|
+
paginate(keep, size, ids = [...this.#messages.keys()]) {
|
|
2319
|
+
this.#page = size;
|
|
2320
|
+
for (const agentId of ids) {
|
|
2321
|
+
const all = [...this.#earlier.get(agentId) ?? [], ...this.#messages.get(agentId) ?? []];
|
|
2322
|
+
const cut = Math.max(0, all.length - keep);
|
|
2323
|
+
this.#earlier.set(agentId, all.slice(0, cut));
|
|
2324
|
+
this.#messages.set(agentId, all.slice(cut));
|
|
2325
|
+
this.emit("messages", agentId);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
failOlder(times = 1) {
|
|
2329
|
+
this.#failing = times;
|
|
2330
|
+
}
|
|
2331
|
+
hasOlder(agentId) {
|
|
2332
|
+
return !!this.#earlier.get(agentId)?.length;
|
|
2333
|
+
}
|
|
2334
|
+
async older(agentId) {
|
|
2335
|
+
this.olderCalls.push(agentId);
|
|
2336
|
+
await this.#clock.sleep(this.olderTicks);
|
|
2337
|
+
if (this.#failing > 0) {
|
|
2338
|
+
this.#failing--;
|
|
2339
|
+
throw new Error("The earlier messages couldn\u2019t be read.");
|
|
2340
|
+
}
|
|
2341
|
+
const earlier = this.#earlier.get(agentId) ?? [];
|
|
2342
|
+
const page2 = earlier.splice(Math.max(0, earlier.length - this.#page));
|
|
2343
|
+
if (page2.length) {
|
|
2344
|
+
this.#messages.get(agentId)?.unshift(...page2);
|
|
2345
|
+
this.emit("messages", agentId);
|
|
2346
|
+
}
|
|
2347
|
+
return page2;
|
|
2314
2348
|
}
|
|
2315
2349
|
commands(agentId) {
|
|
2316
2350
|
if (this.#agent(agentId)?.kind !== "cone") return [];
|
|
@@ -2354,6 +2388,7 @@ var DummyAgent = class extends Emitter {
|
|
|
2354
2388
|
this.stop(agentId);
|
|
2355
2389
|
this.#agents = this.#agents.filter((candidate) => candidate !== agent);
|
|
2356
2390
|
this.#messages.delete(agentId);
|
|
2391
|
+
this.#earlier.delete(agentId);
|
|
2357
2392
|
this.#queues.delete(agentId);
|
|
2358
2393
|
this.#suggestions.delete(agentId);
|
|
2359
2394
|
if (this.#active === agentId) this.select(agent.parentId ?? this.#agents[0].id);
|
|
@@ -7111,7 +7146,8 @@ function createDummyModel({
|
|
|
7111
7146
|
monitor = "dummy",
|
|
7112
7147
|
import: imports = "off",
|
|
7113
7148
|
login,
|
|
7114
|
-
budget = "plain"
|
|
7149
|
+
budget = "plain",
|
|
7150
|
+
older = "off"
|
|
7115
7151
|
} = {}) {
|
|
7116
7152
|
const time = clock(delay);
|
|
7117
7153
|
const mount = mounts === "off" ? null : { scenario: mounts, point: mountPoint, files: mounted };
|
|
@@ -7136,6 +7172,7 @@ function createDummyModel({
|
|
|
7136
7172
|
browser.release(agentId);
|
|
7137
7173
|
};
|
|
7138
7174
|
importPort(imports, agent, time);
|
|
7175
|
+
if (older === "paged") agent.paginate(6, 4);
|
|
7139
7176
|
if (hold !== "off") agent.setHold({ reason: hold, held: 2 });
|
|
7140
7177
|
const noticePort = noticesPort(agent, time, notices, readonly, arrival);
|
|
7141
7178
|
if (scenario === "driving") void browser.run(drive);
|