@ai-ecoverse/slicc-spectrum 1.91.0 → 1.93.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
@@ -406,11 +406,16 @@ An account whose status is `signing-in` (or a non-API-key account while its `con
406
406
  Accounts with `group: 'mcp'` (MCP servers) get their own **MCP servers** heading (`.accounts[data-group=mcp]`).
407
407
  An `api-key` account asks for its `secret`: “{provider} API key” by default, “{provider} token” for `secret: 'token'`; when it is `expired` it reads **API key rejected** or **Token rejected**.
408
408
  An MCP server with `auth: 'oauth'` signs in in a pop-up: its button reads **Sign in**, and it reads **Needs sign-in** when `disconnected` and **Sign-in expired** when `expired`.
409
- An account with `fields` (`{ name, label, required? }`) shows a plain labelled input for each, and their values go to `connect(id, secret?, fields)`.
409
+ 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)`. A field with `options` (`{ value, label }`) is a dropdown (`select[data-account-field]`, with the inputs' `aria-required`, `aria-invalid` and `aria-describedby`) instead, so a provider can offer only the values it accepts, such as Bedrock's regions; it starts on the field's current `value`, otherwise on the first option, and an empty `options` list is a disabled dropdown, never a free-text input, and doesn't hold up Save even when `required`. The dummy's Amazon Bedrock account has one for its region.
410
410
  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
411
  Without `store: 'vault'` or `vault.put`, the key goes to `connect(id, secret)` as before.
412
412
  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
413
  With a `vault` port, **Secrets** (`<slicc-secrets>`) follows.
414
+ 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.
415
+ An account with `needs: 'cors-free-transport'` says “Needs the SLICC extension or CLI, because the browser blocks direct requests to it.” under its row (`.notes [data-notice]`), and an OAuth account that is signing in says “Continue in the sign-in window that opened.”, then, after `SliccChat.closedAfter`, “Closed the sign-in window? Cancel, then Log in again.” (`[data-signing]`).
416
+ **Add a provider** (`data-action="add-provider"`, a quiet button under the list) opens a modal picker (`dialog.picker[data-picker=pick]`): **Search providers** filters `providers()` in their order by every word of the name, id and description; each row (`.option[data-provider][data-auth][role=option]`) shows the name, the `description` and a hint (API key, Sign in, Endpoint and key, On this device), and “Needs the SLICC extension or CLI” for a CORS-free one; added providers read **Added** and can't be picked (`aria-disabled`); no match reads “No provider matches “{q}”.”. Down arrow moves from the search to the rows, Up, Down, Home and End walk them, typing goes back to the search, Enter or Space picks, Enter in the search picks the first match that isn't added, and Escape closes and gives focus back to **Add a provider**.
417
+ Picking one opens **Add {name}** (`dialog.picker[data-picker=connect]`, `.step[data-step=<auth>]`): `api-key` and `fields` get the key form (the vault form when there is one, `vault.put(vaultName ?? recordName, key, undefined, { kind: 'model', provider: id })`), with the provider's `fields` and **Get a key** (`data-action="get-key"`, `keyUrl` in a new tab); **Connect** stays disabled until the key and the required fields are filled; a `customBase` field whose value is an `https:` URL is bound to the key's record after its `put` and before `connect`, with `vault.endpoints({ add: { model: [{ name, url }] } })` (`name` is the record's, `vaultName ?? recordName`; without a vault record nothing is bound) (a bare name, such as an Azure resource, isn't), a refusal shows on that field and `connect` doesn't run, a changed URL (against the field's `value`, else the one this page bound) is one call that removes the old and adds the new, and a bare value only removes the old; a rejected `connect` reverses that call and shows on that field when it names it as a whole word (or https, URL, port or userinfo); **Remove** leaves the URLs to `vault.remove`, which drops them with the record; the saved `value`s prefill the fields and count as filled; `oauth` gets **Sign in** (`data-action="sign-in"`), which calls `connect(id)` within the click, then reads “Signing in…” with Cancel and the same window hints; `local` says “SLICC connects to {name} on this device.” and offers **Connect**. **Back** returns to the list. A rejected `connect` shows its message inline and keeps the form and its fields, clearing only the key; once the account is `connected`, the picker closes and focus lands on its row.
418
+ Without `providers()`, Accounts is as before.
414
419
  When the model for new cones was moved off a provider that isn't set up, a quiet note under its picker (`p[data-switched][role=status]`) says “Switched to {model} because the previous model's provider isn't set up.” until a model is picked.
415
420
 
416
421
  #### `<slicc-secrets>`
@@ -726,6 +731,7 @@ Later backed by: local storage.
726
731
  Theme, model, thinking level, composer and diff preferences, the models on offer, and accounts.
727
732
  A model can say it is its provider's `default`.
728
733
  Emit `settings` or `accounts` when the models on offer change.
734
+ The optional `providers()` returns the `ProviderOption`s that can be added (`{ id, name, description?, auth: 'api-key' | 'oauth' | 'fields' | 'local', fields?, keyUrl?, needs?, 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` and `vaultName`, the vault record for its key (else pi-ai's environment-variable name).
729
735
 
730
736
  #### `changes` (optional)
731
737
 
@@ -885,7 +891,7 @@ The vault's view for SLICC, typed after `@ai-ecoverse/slicc-vault` (spectrum re-
885
891
  `status()` returns the `VaultStatus` (`locked`, `lock`, `scrub`, `waiting` requests, and optional `exportedAt` and `backupReminder`) or `null` while loading, and `list()` the `VaultList` (`secrets` as `SecretView`s, `refused` rules, `exits`, and the first `audit` page with `auditMore`) or `null`.
886
892
  Emit `status` and `list` when they change (bios re-lists after the vault's `changed` events), and `availability` when the optional `availability()` (`connecting`, `waiting`, `locked` or `ready`) changes.
887
893
  `audit(before?, limit?)` resolves with an older `AuditPage` (`before` is a `seq`).
888
- The optional page ops, each hiding its control when missing: `put(name, value, request?)` (a value request's `put` must use its `name`), `cancel(request)`, `remove(name, kind?)`, `restore(name, kind?)`, `import(kind, bytes, { password?, pick? })` (a `pick` stage, then `done`), `export(passphrase)` (the bundle's bytes), `lock()` and `dismissBackupReminder()`.
894
+ The optional page ops, each hiding its control when missing: `put(name, value, request?)` (a value request's `put` must use its `name`), `cancel(request)`, `remove(name, kind?)`, `restore(name, kind?)`, `import(kind, bytes, { password?, pick? })` (a `pick` stage, then `done`), `export(passphrase)` (the bundle's bytes), `lock()`, `dismissBackupReminder()` and `endpoints({ add?, remove? })`, which registers or takes back the URLs a page-sealed credential may be filled on (each an `EndpointSet`, `{ model?: [{ name, url }], mcp?: [{ server, url }] }`, a model URL bound to one record by name), checks the whole change first (`EINVAL` changes nothing) and rejects with a sentence. Settings registers them itself, never through the agent.
889
895
  They reject with a `VaultFailure`, an `Error` with an `ErrorCode` `code` (`isVaultFailure` from `/ui`).
890
896
  Values go to the vault only through `put`, and never through another port.
891
897
  `button()` returns a new `VaultButton`, `{ element, post(message), listen(fn) }` returning an unsubscribe: bios wraps the vault's `button.html` frame and its postMessage protocol, and spectrum only sends `ButtonMessage`s and receives `ButtonEvent`s.
@@ -974,6 +980,7 @@ Update this one to send prompts.", and shows a warning notice without a close bu
974
980
 
975
981
  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.”
976
982
  `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`).
983
+ `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, CORS-free), 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.
977
984
 
978
985
  `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.
979
986
  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.
@@ -982,7 +989,7 @@ Like slicc-agent's snapshot, the dummy's vitals are the budget, Spend, Active ag
982
989
  (`guest-follower`: the same without `decide`), or (`guest-always`) one guest tool call from Seat 2 (Mara) to `read_file` with `always: true`.
983
990
  Decisions are recorded in `model.approvals.decisions`, and `model.approvals.end(id, reason)` ends a request.
984
991
 
985
- `createDummyModel({ vault: 'empty' | 'secrets' | 'policy' | 'locked' | 'request' | 'backup' | 'waiting' | 'purged' | '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).
992
+ `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`.
986
993
  `purged` is `secrets` plus an `endpoints` audit entry for two invented model URLs.
987
994
  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.
988
995
  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.