@ai-ecoverse/slicc-spectrum 1.95.0 → 1.96.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 +7 -2
- package/dist/{chunk-QGY4KBEB.js → chunk-CVMI4FSY.js} +35 -2
- package/dist/{chunk-QGY4KBEB.js.map → chunk-CVMI4FSY.js.map} +2 -2
- package/dist/slicc-dummy.js +35 -5
- package/dist/slicc-dummy.js.map +2 -2
- package/dist/slicc-kernel-model.js +1 -1
- package/dist/slicc-ui.js +179 -15
- package/dist/slicc-ui.js.map +3 -3
- package/dist/types/app/settings.d.ts +1 -0
- package/dist/types/dummy/index.d.ts +4 -1
- package/dist/types/dummy/vault.d.ts +1 -1
- package/dist/types/dummy.d.ts +1 -1
- package/dist/types/model/types.d.ts +5 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -284,6 +284,8 @@ The card (`.tool[data-tool][data-status][data-kind]`, a `details.run` under a pi
|
|
|
284
284
|
A lick with a body is a `details.lick` whose head (`summary.lick-head`, with a quiet chevron that turns when open) toggles it with a click, <kbd>Enter</kbd> or <kbd>Space</kbd>; clicking the body doesn't close it.
|
|
285
285
|
Open licks and open tool runs hold for every view of the chat on the same model, so a layout switch keeps them.
|
|
286
286
|
A lick's `severity` (`warn` or `error`) adds an alert icon to its byline, a coloured edge to its box and a spoken prefix.
|
|
287
|
+
A lick's optional `summary` (one plain line) replaces its `text` in the box and wraps instead of being cut, and its optional `report` (a file path) adds **Open the report** (`data-action="open-report"`, which fires `open-file` with the path) to the expanded lick, so a lick with only a `report` expands too.
|
|
288
|
+
A `scoop-notify` lick never shows the agent's bracketed status line: a `text` that is `[scoop … completed]`, `failed` or `stopped` (or `[@… completed]`) reads "Scoop {name} finished", "Scoop {name} failed" or "Scoop {name} stopped", the name being its `title` without the role in brackets, a failed or stopped one takes the `error` tone and a ❌ pill, named and announced as Scoop failed or Scoop stopped, instead of ✅, and its body, without that line, renders as markdown (`.report .text`), its headings stepped down to the chat's `####` size so the card doesn't outshout the chat.
|
|
287
289
|
Pending permission requests (`sudo-request` licks with `state: 'pending'`) and open questions are decision cards in the centre column (`.decision-turn[data-pending]`, `data-id` the lick's or the question's), inset like a tool cluster (a wide margin on each side, a narrower one under 480 px), with their buttons on the right: **Deny**, then **Allow** (`data-action` `deny` and `allow`), or the question's options.
|
|
288
290
|
A request with `expiresAt` counts down as m:ss; the count is hidden from screen readers, which hear its end time, then "Less than a minute left" and "Timed out", so the log's live region doesn't speak every second; a timed-out request (`data-expired`) loses its buttons.
|
|
289
291
|
While pending, cards stick to the top of the log and pile, each a step lower than the one before; a card behind the front one (`data-pinned`, `--depth`) is a little smaller and softer for each card in front of it, and the log's `scroll-padding` keeps the pile from covering whatever has focus.
|
|
@@ -409,6 +411,7 @@ An MCP server with `auth: 'oauth'` signs in in a pop-up: its button reads **Sign
|
|
|
409
411
|
An account with `fields` (`{ name, label, required?, placeholder?, help?, customBase?, options?, value? }`) shows a plain labelled input for each, with its `placeholder`, its current `value` and its `help` as a line under it, and their values go to `connect(id, secret?, fields)`; the key field above them gets a visible `sp-field-label` too ("Amazon Bedrock API key"), and both span the form's width. A field with `options` (`{ value, label }`) is an `sp-picker[data-account-field]` instead, labelled by an `sp-field-label` (clicking it focuses the picker), `aria-required` when required, with its `help` under it and in its `description` slot, and when refused `invalid`, with “Select a {label}.” in its `description` slot and under it without a second icon, so a provider can offer only the values it accepts, such as Bedrock's regions; it starts on the field's current `value`, otherwise, or when `value` is none of the options, on the first option, and it never holds back Connect in the provider picker, since it always has a choice. An empty `options` list is a disabled picker reading “None available”, never a free-text input, with the help text “Nothing to choose from yet. You can connect without it.” under it in the same field group (`[data-empty]`), and doesn't hold up Save even when `required`. The plain inputs' border is `--swc-neutral-visual-color`, which keeps 3:1 against the page. The dummy's Amazon Bedrock account has a region picker; connecting keeps the chosen values as the fields' `value` and shows the chosen options' labels in the row (“API key · Europe (Frankfurt)”).
|
|
410
412
|
An account with `store: 'vault'` keeps its key in the vault when the `vault` port has `put`: the masked key field (C5's value rules) goes to `vault.put(name, key, undefined, { kind: 'model', provider: account.id })` (or `{ kind: 'mcp', server }` for `mcp:<server>` accounts, named `MCP_<SERVER>_TOKEN`; model records use pi-ai's environment-variable name), then `connect(id)` runs without the secret; a connected one says “Key kept in the vault”, “Token kept in the vault” or “Kept in the vault”, and Disconnect removes the record first.
|
|
411
413
|
Without `store: 'vault'` or `vault.put`, the key goes to `connect(id, secret)` as before.
|
|
414
|
+
An `mcp:<server>` account with an `https:` `url` (the server's URL), when the `vault` port has `endpoints`, asks before its token is saved: “Connect to {url}?”, “SLICC sends this token only to {url}.”, **Connect** with Cancel focused; then the token goes to `vault.put`, the URL to `vault.endpoints({ add: { mcp: [{ server, url }] } })` and the account to `connect`, and a refusal shows on the token field and stops. The page keeps its own registrations (server and URL, not secret) in `localStorage` under `slicc-spectrum.mcp-endpoints`, written after a successful add or change and cleared after Disconnect takes the URL back. When the account's `url` differs from that registration, or, with none, from a first-use pin (the record's `url` while `SecretView.pinned` is `true`; a bridge's loopback `url` without `pinned` never counts), the row adds **Reconnect** (`data-action=reconnect-mcp`) and the note “The server moved to {url}. Reconnect to send the token there.” (`[data-notice=moved]`); after the same confirm, one `endpoints` call removes the old registration (none for a pin, which a registration overrides) and adds the new, then `connect` runs. Disconnect takes the URL back after removing the token. Settings registers MCP URLs itself, never through the agent.
|
|
412
415
|
A `show-surface` whose `section` is an account id scrolls to that row and focuses its button, and `accounts` scrolls to the Accounts heading (`h2[data-section=accounts]`).
|
|
413
416
|
With a `vault` port, **Secrets** (`<slicc-secrets>`) follows.
|
|
414
417
|
With `providers()`, Accounts lists only the added providers (`.accounts[data-group=providers]`), connected first, then expired, signing in and not connected; each row adds **Remove** (`data-action="remove-account"`, a quiet delete icon named “Remove {provider}”) when the port has `remove`, which asks “Remove {provider}?” (“Its key is deleted from the vault. Models from it stop working.”, “Its sign-in is deleted from the vault…” for OAuth, only “Models from it stop working.” for a local one) with a destructive **Remove** and Cancel focused, then runs `vault.remove(vaultName, 'model')` for a vaulted key and `settings.remove(id)`, stopping with the vault's error inline when it refuses; Disconnect removes the same record and keeps the row.
|
|
@@ -732,7 +735,7 @@ Later backed by: local storage.
|
|
|
732
735
|
Theme, model, thinking level, composer and diff preferences, the models on offer, and accounts.
|
|
733
736
|
A model can say it is its provider's `default`.
|
|
734
737
|
Emit `settings` or `accounts` when the models on offer change.
|
|
735
|
-
The optional `providers()` returns the `ProviderOption`s that can be added (`{ id, name, description?, auth: 'api-key' | 'oauth' | 'fields' | 'local', fields?, keyUrl?, needs?, blocked?, vaultName? }`, sorted by name, added ones included), with a `providers` event when they change; `id` is pi-ai's provider id and becomes the `Account.id`. With it, `accounts()` lists only added accounts, `connect(id, secret?, fields?)` adds one (`group: 'providers'`; a `fields` provider becomes an `api-key` account with `store: 'vault'` and only its non-secret `fields`), and the optional `remove(id)` drops one. `Account` adds `keyUrl`, `description
|
|
738
|
+
The optional `providers()` returns the `ProviderOption`s that can be added (`{ id, name, description?, auth: 'api-key' | 'oauth' | 'fields' | 'local', fields?, keyUrl?, needs?, blocked?, vaultName? }`, sorted by name, added ones included), with a `providers` event when they change; `id` is pi-ai's provider id and becomes the `Account.id`. With it, `accounts()` lists only added accounts, `connect(id, secret?, fields?)` adds one (`group: 'providers'`; a `fields` provider becomes an `api-key` account with `store: 'vault'` and only its non-secret `fields`), and the optional `remove(id)` drops one. `Account` adds `keyUrl`, `description`, `vaultName`, the vault record for its key (else pi-ai's environment-variable name), and `url`, an `mcp:<server>` account's server URL. `needs: 'cors-free-transport'` on either is static (the provider doesn't answer a browser); `blocked: 'cors-free-transport'` is live, set while the current transport can't reach it, and cleared by a `providers` and an `accounts` event when an extension or a linked slicc appears.
|
|
736
739
|
|
|
737
740
|
#### `changes` (optional)
|
|
738
741
|
|
|
@@ -983,6 +986,7 @@ The kitchen sink's “docs-pass failed” card is an expired-session error for t
|
|
|
983
986
|
`createDummyModel({ providers: 'none' })` (`?providers=none` on `/ui/`) starts with no model provider connected: `models()` offers only the models of connected accounts, and connecting OpenAI offers the invented GPT-6 and GPT-6 mini (its `default`).
|
|
984
987
|
`createDummyModel({ providers: 'all' })` (`?providers=all` on `/ui/`) adds `providers()` and `remove` with 14 invented providers (`providerFixtures` from `/dummy`): API keys for Anthropic, Cerebras, Groq, Mistral, OpenAI and OpenRouter, OAuth for Adobe AI, GitHub Copilot and OpenAI Codex, fields for Amazon Bedrock (region), Azure OpenAI (endpoint, a `customBase`, and deployment) and Cloudflare (account id, CORS-free, its own `vaultName`), and Ollama and LM Studio on this device; today's rows start as added, with Amazon Bedrock and a connected Ollama (`providerAccounts(accounts)`, `providerAccount(option)`). `connect` adds a provider and drops it again when it fails or is cancelled, a key or field starting with `bad` is refused (“{name} didn’t accept that key.”), an endpoint without `https://` is refused, and `remove` drops it.
|
|
985
988
|
`createDummyModel({ providers: 'all', transport: 'none' })` (`?providers=all&transport=none`) has no CORS-free transport: every provider with `needs` is also `blocked`, and Cloudflare starts connected, so it reads Unreachable. `model.settings.setTransport('extension')` (or `'link'`) clears `blocked` live, with a `providers` and an `accounts` event, and `setTransport('none')` sets it again.
|
|
989
|
+
The dummy's `docs` and `tickets` MCP accounts have a `url`, the dummy vault records each `endpoints` change and drops a record's pin once the page registers its server, and `createDummyModel({ mcp: 'moved' })` (`?mcp=moved` on `/ui/`) pins the vault's `MCP_DOCS_TOKEN` to docs' URL and moves `docs` to `movedDocs` (from `/dummy`).
|
|
986
990
|
|
|
987
991
|
`createDummyModel({ monitor: 'agent' })` (`?monitor=agent` on `/ui/`) adds the agent's Tasks (a tree by `depth`), Processes and Recent problems sections to the monitor, and its Running vital.
|
|
988
992
|
Like slicc-agent's snapshot, the dummy's vitals are the budget, Spend, Active agents, Processes and Process memory (both with a `series`) and Fullest context.
|
|
@@ -991,7 +995,7 @@ Like slicc-agent's snapshot, the dummy's vitals are the budget, Spend, Active ag
|
|
|
991
995
|
(`guest-follower`: the same without `decide`), or (`guest-always`) one guest tool call from Seat 2 (Mara) to `read_file` with `always: true`.
|
|
992
996
|
Decisions are recorded in `model.approvals.decisions`, and `model.approvals.end(id, reason)` ends a request.
|
|
993
997
|
|
|
994
|
-
`createDummyModel({ vault: 'empty' | 'secrets' | 'policy' | 'locked' | 'request' | 'backup' | 'waiting' | 'purged' | 'endpoints-fail' | 'off' })` (`?vault=` on `/ui/`, off by default; `notice` is kept as another name for `policy`) adds an invented vault: no secrets yet; five secrets (an API key, a jerk lease from pier-nuc revealed to `pier publish`, a Tide Mail sign-in, one without a rule and a removed one) with an activity log that pages back once; the same with a refused rule and a policy change in the log; locked before the first unlock, with a waiting request from docs-pass; unlocked with a `fnox set` request for `TIDE_STATION_TOKEN`; the secrets with a due backup reminder; or a vault that hasn't answered yet (`model.vault.arrive()` brings it); `endpoints-fail` is the secrets with an `endpoints` that refuses. The dummy vault records each `endpoints` change in `model.vault.endpointChanges`.
|
|
998
|
+
`createDummyModel({ vault: 'empty' | 'secrets' | 'policy' | 'locked' | 'request' | 'backup' | 'waiting' | 'purged' | 'endpoints-fail' | 'off' })` (`?vault=` on `/ui/`, off by default; `notice` is kept as another name for `policy`) adds an invented vault: no secrets yet; five secrets (an API key, a jerk lease from pier-nuc revealed to `pier publish`, a Tide Mail sign-in, one without a rule and a removed one) with an activity log that pages back once; the same with a refused rule and a policy change in the log; locked before the first unlock, with a waiting request from docs-pass; unlocked with a `fnox set` request for `TIDE_STATION_TOKEN`; the secrets with a due backup reminder; or a vault that hasn't answered yet (`model.vault.arrive()` brings it); `endpoints-fail` is the secrets with an `endpoints` that refuses (an MCP URL with “The vault refused {url} for this token. Check the server's address, then try again.”, a model URL with “The vault refused that endpoint. Check the address and try again.”). The dummy vault records each `endpoints` change in `model.vault.endpointChanges`.
|
|
995
999
|
`purged` is `secrets` plus two `endpoints` audit entries: one with `purged` and `adopted`, and one `pinned` (`DOCS_MCP_TOKEN` on docs.example) that Unpin turns into an `unpinned` entry; `vaultPurged` and `vaultPinned` are exported.
|
|
996
1000
|
It implements every op with the vault's checks, and keeps only names: `model.vault.puts` records `{ name, request? }`, `cancelled` the cancelled requests, `imports` each import's kind, size and pick, and `exported` the exports.
|
|
997
1001
|
Import offers five invented candidates (one replaces a secret, one is a model key, one is refused with a reason that the done stage repeats; KeePass adds a `_2` duplicate), six answers ENOTSUP, page ops are recorded with `uid: null, tag: null`, and value ops while locked answer EACCES.
|
|
@@ -1039,6 +1043,7 @@ Each reply to it is paced to take about 20 seconds, so its streaming states can
|
|
|
1039
1043
|
A prompt that says search or grep runs a search.
|
|
1040
1044
|
Its tool turn has two clusters and a standalone run with URL, file and plain commands in every state, and two dummy browser tabs carry invented `data:` favicons.
|
|
1041
1045
|
The dummy's fixtures are invented: a small forecast API called harbor, with cones, scoops, a conversation per agent, pending changes (one of them binary, without a diff), browser tabs and accounts.
|
|
1046
|
+
The harbor cone ends with three `scoop-notify` licks: amber-heron's, finished with a `summary`, a markdown body and a `report` at `/shared/scoop-notifications/amber-heron.md`; grey-tern's, failed, with no summary; and salt-gull's, only the legacy bracketed line.
|
|
1042
1047
|
Its only sprinkles are SLICC's own: `welcome` (the onboarding wizard, as a dip in the sliccy cone) and `suggestions`, copied from `slicc` (`packages/vfs-root/shared/sprinkles/`, commit `aa29784`) with their comments stripped, as is the sprinkle theme in `src/app/sprinkle-theme.css`.
|
|
1043
1048
|
`suggestions` reads its stream from the invented `/home/.gelatiere/suggestions.json` through the bridge; there is no `/home/.welcomed`, so `welcome` starts its wizard.
|
|
1044
1049
|
Its replies are scripted by keyword (debug or investigate, tests, fix or add, open or docs, files, anything else), and their tool calls act on the other ports: an edit writes a file and shows up as a change, a browse opens a tab.
|
|
@@ -970,6 +970,7 @@ var suggestions = `${JSON.stringify(
|
|
|
970
970
|
2
|
|
971
971
|
)}
|
|
972
972
|
`;
|
|
973
|
+
var upstreamReport = "# Upstream limits\n\n## Summary\n\nThe forecast upstream allows **60 requests a minute** per key and answers `429` with a `Retry-After` header past that. A one-minute cache keeps harbor well under it.\n\n## Findings\n\n- Bursts after midnight UTC reach about 40 requests a minute.\n- `Retry-After` is in seconds, never a date.\n- Timeouts come from the upstream, not the network: 3 in the last 1,000 calls.\n";
|
|
973
974
|
var files = {
|
|
974
975
|
"/workspace/harbor/README.md": readme,
|
|
975
976
|
"/workspace/harbor/package.json": pkg,
|
|
@@ -986,6 +987,7 @@ var files = {
|
|
|
986
987
|
"/workspace/skills/release-notes/scripts/collect.jsh": collect,
|
|
987
988
|
"/shared/MEMORY.md": memory,
|
|
988
989
|
"/shared/notes/2026-10-04-standup.md": standup,
|
|
990
|
+
"/shared/scoop-notifications/amber-heron.md": upstreamReport,
|
|
989
991
|
"/home/user/.bashrc": bashrc,
|
|
990
992
|
"/home/.gelatiere/suggestions.json": suggestions,
|
|
991
993
|
"/tmp/forecast-sample.json": sample
|
|
@@ -1101,6 +1103,7 @@ var accounts = [
|
|
|
1101
1103
|
id: "mcp:docs",
|
|
1102
1104
|
provider: "docs",
|
|
1103
1105
|
identity: "docs.example",
|
|
1106
|
+
url: "https://docs.example/mcp",
|
|
1104
1107
|
status: "connected",
|
|
1105
1108
|
auth: "api-key",
|
|
1106
1109
|
group: "mcp",
|
|
@@ -1110,6 +1113,7 @@ var accounts = [
|
|
|
1110
1113
|
id: "mcp:tickets",
|
|
1111
1114
|
provider: "tickets",
|
|
1112
1115
|
identity: "tickets.example",
|
|
1116
|
+
url: "https://tickets.example/mcp",
|
|
1113
1117
|
status: "expired",
|
|
1114
1118
|
auth: "api-key",
|
|
1115
1119
|
group: "mcp",
|
|
@@ -1352,6 +1356,35 @@ var conversations = {
|
|
|
1352
1356
|
title: "quiet-otter",
|
|
1353
1357
|
text: "Started: add retries with backoff around the upstream fetch",
|
|
1354
1358
|
createdAt: at2(9)
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
id: "m-h5",
|
|
1362
|
+
role: "lick",
|
|
1363
|
+
channel: "scoop-notify",
|
|
1364
|
+
title: "amber-heron (scout)",
|
|
1365
|
+
text: "[scoop amber-heron (scout) completed]",
|
|
1366
|
+
summary: "The upstream allows 60 requests a minute, so a one-minute cache keeps harbor under it.",
|
|
1367
|
+
report: "/shared/scoop-notifications/amber-heron.md",
|
|
1368
|
+
body: `[scoop amber-heron (scout) completed]
|
|
1369
|
+
${upstreamReport}`,
|
|
1370
|
+
createdAt: at2(11)
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
id: "m-h6",
|
|
1374
|
+
role: "lick",
|
|
1375
|
+
channel: "scoop-notify",
|
|
1376
|
+
title: "grey-tern (worker)",
|
|
1377
|
+
text: "[scoop grey-tern (worker) failed]",
|
|
1378
|
+
body: "[scoop grey-tern (worker) failed]\nThe deploy preview timed out after **120 s**. Nothing was pushed.",
|
|
1379
|
+
createdAt: at2(13)
|
|
1380
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
id: "m-h7",
|
|
1383
|
+
role: "lick",
|
|
1384
|
+
channel: "scoop-notify",
|
|
1385
|
+
title: "salt-gull (scout)",
|
|
1386
|
+
text: "[scoop salt-gull (scout) completed]",
|
|
1387
|
+
createdAt: at2(15)
|
|
1355
1388
|
}
|
|
1356
1389
|
],
|
|
1357
1390
|
"scoop-otter": [
|
|
@@ -1448,7 +1481,7 @@ var conversations = {
|
|
|
1448
1481
|
},
|
|
1449
1482
|
{
|
|
1450
1483
|
type: "text",
|
|
1451
|
-
text: "## harbor 1.9.0\n\n
|
|
1484
|
+
text: "## harbor 1.9.0\n\n#### Features\n- Added a `/health` endpoint.\n\n#### Fixes\n- Requests without a city now get a 400 instead of a 500.\n\n#### Chores\n- Updated wrangler to 4.40.0."
|
|
1452
1485
|
}
|
|
1453
1486
|
]
|
|
1454
1487
|
},
|
|
@@ -1908,4 +1941,4 @@ export {
|
|
|
1908
1941
|
dummySettingsKey,
|
|
1909
1942
|
DummySettings
|
|
1910
1943
|
};
|
|
1911
|
-
//# sourceMappingURL=chunk-
|
|
1944
|
+
//# sourceMappingURL=chunk-CVMI4FSY.js.map
|