@ai-ecoverse/slicc-agent 6.11.0 → 6.11.2

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
@@ -138,11 +138,13 @@ const owner = await startAgent({
138
138
 
139
139
  `runAgentWorker(self, options)` is the same with its parts replaceable (model, providers, kernel attach, the account store, storage).
140
140
 
141
- **Providers.** `sliccProviders()` registers Bedrock, Adobe and every pi-ai provider that takes a plain API key: Anthropic, OpenAI, Mistral, Groq, DeepSeek, Together, Hugging Face, Xiaomi and its three token plans, Cerebras, OpenRouter, Fireworks, Baseten, Moonshot AI and its China platform, xAI, Meta, Vercel AI Gateway, MiniMax and MiniMax CN, Z.AI and Z.AI Coding CN, NVIDIA, Kimi For Coding, Ant Ling, OpenCode Zen and Go, and the three Qwen token plans (`KEY_PROVIDERS` in `src/providers.ts`, each with its id, pi-ai factory, pi-ai env name, a description, a key URL and whether it answers browsers). Three more take a key and non-secret fields (`auth: 'fields'`), each checked in `connect` and mapped to pi-ai's env: Azure (`endpoint`, required: a resource name becomes `AZURE_OPENAI_RESOURCE_NAME`, an `https:` URL without a port or user name `AZURE_OPENAI_BASE_URL`; `deployments`, optional `model=deployment` pairs, `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`), Cloudflare Workers AI (`account`, 32 hex characters, `CLOUDFLARE_ACCOUNT_ID`) and Cloudflare AI Gateway (`account` and `gateway`, `CLOUDFLARE_GATEWAY_ID`). Bedrock's `region` is on the same mechanism. A bad value rejects `connect` with a sentence, a required field left out keeps its stored value or rejects with “{label} can’t be empty.”, and a stored value that no longer passes is left out of the env. A fields provider whose required fields are missing or no longer pass shows as not connected, gives pi-ai no credential and offers no models, even with its key already in the vault (spectrum puts the key before `connect` checks the fields). Bedrock's region is any AWS region name (`eusc-de-east-1` and `us-gov-west-1` included), and stored values are trimmed. OpenCode Zen and OpenCode Go need seven's vault 1.12.2 or later, whose built-in table has their `/inference` endpoints; their descriptions say so. Azure's `endpoint` field says `customBase: true`: when its value is a full URL, spectrum registers it with the vault (the page's `endpoints`) before `connect`, since the agent never registers model endpoints for pi-ai providers; a resource name needs nothing, as `*.openai.azure.com` is built into the vault. The worker routes requests to an Azure account's base URL as Azure's. Google and Google Vertex wait for slicc-bios to stop removing `@google/genai`; the sign-ins come later. A provider's preferred model is pi-coding-agent 1.3.0's `defaultModelPerProvider` (Bedrock keeps `us.anthropic.claude-sonnet-5-5`), and the first model when that one is missing. Providers that don't answer a browser's preflight with the headers pi-ai sends (`x-api-key`, `anthropic-version`, the SDK's `x-stainless-*`, or `Authorization` under a bare `*`) say `needs: 'cors-free-transport'`; the direct ones are Anthropic, OpenAI, Mistral, Groq, DeepSeek, Together, Hugging Face, Baseten, both Moonshot AI platforms, Azure and the four Xiaomi providers. The worker's transport fetch drops the Stainless SDKs' `x-stainless-*` telemetry headers from every request it sends, which is what Baseten's and Moonshot AI's preflights refuse.
141
+ **Providers.** `sliccProviders()` registers Bedrock, Adobe and every pi-ai provider that takes a plain API key: Anthropic, OpenAI, Mistral, Groq, DeepSeek, Together, Hugging Face, Xiaomi and its three token plans, Cerebras, OpenRouter, Fireworks, Baseten, Moonshot AI and its China platform, xAI, Meta, Vercel AI Gateway, MiniMax and MiniMax CN, Z.AI and Z.AI Coding CN, NVIDIA, Kimi For Coding, Ant Ling, OpenCode Zen and Go, and the three Qwen token plans (`KEY_PROVIDERS` in `src/providers.ts`, each with its id, pi-ai factory, pi-ai env name, a description, a key URL and whether it answers browsers). Three more take a key and non-secret fields (`auth: 'fields'`), each checked in `connect` and mapped to pi-ai's env: Azure (`endpoint`, required: a resource name becomes `AZURE_OPENAI_RESOURCE_NAME`, an `https:` URL without a port or user name `AZURE_OPENAI_BASE_URL`; `deployments`, optional `model=deployment` pairs, `AZURE_OPENAI_DEPLOYMENT_NAME_MAP`), Cloudflare Workers AI (`account`, 32 hex characters, `CLOUDFLARE_ACCOUNT_ID`) and Cloudflare AI Gateway (`account` and `gateway`, `CLOUDFLARE_GATEWAY_ID`). Bedrock's `region` is on the same mechanism. A bad value rejects `connect` with a sentence, a required field left out keeps its stored value or rejects with “{label} can’t be empty.”, and a stored value that no longer passes is left out of the env. A fields provider whose required fields are missing or no longer pass shows as not connected, gives pi-ai no credential and offers no models, even with its key already in the vault (spectrum puts the key before `connect` checks the fields). Bedrock's region is any AWS region name (`eusc-de-east-1` and `us-gov-west-1` included), and stored values are trimmed. OpenCode Zen and OpenCode Go need seven's vault 1.12.2 or later, whose built-in table has their `/inference` endpoints; their descriptions say so. Azure's `endpoint` field says `customBase: true`: when its value is a full URL, spectrum registers it with the vault (the page's `endpoints`) before `connect`, since the agent never registers model endpoints for pi-ai providers; a resource name needs nothing, as `*.openai.azure.com` is built into the vault. The worker routes requests to an Azure account's base URL as Azure's. Google and Google Vertex wait for slicc-bios to stop removing `@google/genai`; the sign-ins come later. A provider's preferred model is pi-coding-agent 1.3.0's `defaultModelPerProvider` (Bedrock keeps `us.anthropic.claude-sonnet-5-5`), and the first model when that one is missing. Providers that don't answer a browser from `https://seven.sliccy.ai` with the headers pi-ai and the AWS SDK send say `needs: 'cors-free-transport'`; the direct ones are Bedrock, Adobe, Anthropic, OpenAI, Azure, Mistral, Groq, DeepSeek, Together, Hugging Face, Baseten, both Moonshot AI platforms, xAI, Meta and the four Xiaomi providers. Chrome accepts `Authorization` under a bare `Access-Control-Allow-Headers: *` (as xAI, Meta and Adobe's proxy answer), checked in Chrome 156. The worker's transport fetch drops the Stainless SDKs' `x-stainless-*` telemetry headers from every request it sends, which is what Baseten's and Moonshot AI's preflights refuse.
142
142
 
143
- The first provider is AWS Bedrock with a Bedrock API key as the bearer token; the default model is `us.anthropic.claude-sonnet-5-5`. Requests go to `us-west-2` unless the account's `region` field names another one (`AWS_REGION`). The account publishes that field as `fields: [{ name: 'region', label: 'Region', help, options, value }]`, and `connect` takes it. Bedrock sends no CORS headers, so it needs a CORS-free transport, and its account says so (`needs: 'cors-free-transport'`).
143
+ **Reachability, nightly.** `npm run reach` (`scripts/reach.mjs`, through `verdict.mjs`) checks every listing from `https://seven.sliccy.ai`. It captures the request pi-ai builds for each of the provider's APIs (with a dummy key and dummy fields, and the fetch faked; Bedrock's from the AWS SDK's own request, caught at its Node HTTP handler, which carries the same headers as in a browser: `amz-sdk-invocation-id`, `amz-sdk-request`, `authorization`, `content-type`, `x-amz-user-agent`). Then it sends that request's preflight and, when the preflight passes, the request itself with dummy credentials, whose answer must carry `Access-Control-Allow-Origin` too. Besides each chat API it probes what the provider's models need beyond a turn: the requests its model discovery sends under its base URL (Adobe's `/v1/config`), one classifier call per classifier API (Adobe's `/v1/ai/run`, OpenAI's `/v1/decisions`, OpenRouter's and Cloudflare's), and Adobe's `/v1/usage`. It prints `reach: <id> direct|cors-free|unknown|unprobed` per provider (`unknown` when the network gave no answer; `unprobed` when it captured no request at all, which counts as a disagreement, so a provider can't silently drop out of the check) and one `reach: PASS` line. `.github/workflows/reach.yml` runs it every night on `ubuntu-latest`, never on pull requests, and opens or updates the issue titled “Provider reachability disagrees with the listings” when a provider's verdict isn't its `needs`, and closes it with a comment once every provider's verdict matches its listing again (a run with an `unknown` provider leaves it open); a run without the PASS line fails.
144
144
 
145
- **Adobe.** `adobeProvider()` reaches Adobe's LLM proxy (`ADOBE_PROXY`) with the user's IMS token as a bearer. Its models come from the proxy's `/v1/config` once a token is stored: Anthropic-style models go through pi-ai's `anthropic-messages` API at the proxy root, `api: 'openai'` models through `openai-completions` under `/v1`, and hidden ones are skipped. Every request to the proxy (turns on either API, Clef, `/v1/config` and `/v1/usage`) carries an `X-Session-Id` that is fixed for the worker and an `X-Slicc-Version` (slicc-agent's version, `adobeProvider({ session, version })`); the proxy refuses turns without them. They're added where the request is built, over the caller's and the model's headers, because pi-ai 1.0.4 keeps a provider's `headers` but never sends them. The account is a sign-in (`auth: 'oauth'`): `AgentSettings.signIn('adobe')` returns the IMS `clientId`, `scopes` and `imsEnvironment` from `/v1/config`, fetched in the worker through the transport. The page runs the IMS popup and seals the token in the vault itself (bios: `seal { kind: 'model', provider: 'adobe', name: 'ADOBE_IMS_TOKEN', source: 'oauth:adobe', expiresAt }`), so `createAgentModel(connection, { login })` takes a `login(providerId, signIn, { signal })` that resolves `{ sealed: true }`; the adapter then calls `connect('adobe')` with no secret. A `login` that still returns a token is refused with "Adobe sign-in needs seven's vault: update seven", and the token never reaches the worker. IMS gives no refresh token, so an expired record shows the account as `expired`, and Sign in (or Reconnect) runs `login` again. The worker derives that from each vault record (`records()`, any account kind, MCP rows included): a record that isn't `ready` or `refreshing` (such as `inert`), or whose `expiresAt` (epoch milliseconds, the vault's `EpochMs`) has passed, reads `expired`, and a timer flips it when the clock reaches `expiresAt`, so an open page needn't wait for the vault to change. A model request that still gets a 401 after the vault's one refresh marks the record it was sent with `expired` too, until that record changes (a new sign-in gives it a new `expiresAt`) or the account is connected again (`connect`), not when an unrelated record changes, so the error card's **Log in** lands on an account that offers Sign in. While it runs, the account shows as `signing-in` (spectrum ≥ 1.34.0), and `SettingsPort.cancel(id)` aborts the signal; a cancelled sign-in never connects, even if the login resolves late. The adapter calls `login` before anything else in the click's task, so the page can open its popup while it still has the user's gesture, and `signIn()` then fetches the IMS options lazily. The weekly budget from `/v1/usage` is in the settings state as `budget` (refreshed on connect and every five minutes), and the `tray` adapter shows it in spectrum's TrayStatus. Until the proxy allows `*.sliccy.ai` (vibemigration#228), Adobe needs a CORS-free transport like Bedrock. An expired session's error card (a 401, or a refusal whose type or message says the token is invalid or expired) offers **Log in**, reads “Log in again to continue.” (the raw error goes to the console once, already scrubbed, never onto the card) and names its account (`account: 'adobe'`, spectrum ≥ 1.88.0), so Log in calls `settings.connect('adobe')` inside the click and spectrum's card follows the sign-in (its own copy while signing in, then “Signed in to Adobe again.”, from the account's `provider`), or shows the failure as a sentence (a `login` that rejects with “the sign-in timed out” reads “The sign-in timed out.”; a cancel stays an `AbortError`, which shows nothing). A rejected or missing key's card, and the “isn't connected” error, name their account too, so spectrum opens Settings at that row; any other refusal, such as a 403 `missing_required_header`, shows the proxy's message, with the HTTP status and error type below it (`403 missing_required_header`) and **Retry**.
145
+ The first provider is AWS Bedrock with a Bedrock API key as the bearer token; the default model is `us.anthropic.claude-sonnet-5-5`. Requests go to `us-west-2` unless the account's `region` field names another one (`AWS_REGION`). The account publishes that field as `fields: [{ name: 'region', label: 'Region', help, options, value }]`, and `connect` takes it. Bedrock's runtime endpoints answer browsers (`Access-Control-Allow-Origin: *`, allowing the AWS SDK's headers), so it needs no CORS-free transport.
146
+
147
+ **Adobe.** `adobeProvider()` reaches Adobe's LLM proxy (`ADOBE_PROXY`) with the user's IMS token as a bearer. Its models come from the proxy's `/v1/config` once a token is stored: Anthropic-style models go through pi-ai's `anthropic-messages` API at the proxy root, `api: 'openai'` models through `openai-completions` under `/v1`, and hidden ones are skipped. Every request to the proxy (turns on either API, Clef, `/v1/config` and `/v1/usage`) carries an `X-Session-Id` that is fixed for the worker and an `X-Slicc-Version` (slicc-agent's version, `adobeProvider({ session, version })`); the proxy refuses turns without them. They're added where the request is built, over the caller's and the model's headers, because pi-ai 1.0.4 keeps a provider's `headers` but never sends them. The account is a sign-in (`auth: 'oauth'`): `AgentSettings.signIn('adobe')` returns the IMS `clientId`, `scopes` and `imsEnvironment` from `/v1/config`, fetched in the worker through the transport. The page runs the IMS popup and seals the token in the vault itself (bios: `seal { kind: 'model', provider: 'adobe', name: 'ADOBE_IMS_TOKEN', source: 'oauth:adobe', expiresAt }`), so `createAgentModel(connection, { login })` takes a `login(providerId, signIn, { signal })` that resolves `{ sealed: true }`; the adapter then calls `connect('adobe')` with no secret. A `login` that still returns a token is refused with "Adobe sign-in needs seven's vault: update seven", and the token never reaches the worker. IMS gives no refresh token, so an expired record shows the account as `expired`, and Sign in (or Reconnect) runs `login` again. The worker derives that from each vault record (`records()`, any account kind, MCP rows included): a record that isn't `ready` or `refreshing` (such as `inert`), or whose `expiresAt` (epoch milliseconds, the vault's `EpochMs`) has passed, reads `expired`, and a timer flips it when the clock reaches `expiresAt`, so an open page needn't wait for the vault to change. A model request that still gets a 401 after the vault's one refresh marks the record it was sent with `expired` too, until that record changes (a new sign-in gives it a new `expiresAt`) or the account is connected again (`connect`), not when an unrelated record changes, so the error card's **Log in** lands on an account that offers Sign in. While it runs, the account shows as `signing-in` (spectrum ≥ 1.34.0), and `SettingsPort.cancel(id)` aborts the signal; a cancelled sign-in never connects, even if the login resolves late. The adapter calls `login` before anything else in the click's task, so the page can open its popup while it still has the user's gesture, and `signIn()` then fetches the IMS options lazily. The weekly budget from `/v1/usage` is in the settings state as `budget` (refreshed on connect and every five minutes), and the `tray` adapter shows it in spectrum's TrayStatus. The proxy answers browsers from `seven.sliccy.ai`, so Adobe needs no CORS-free transport. An expired session's error card (a 401, or a refusal whose type or message says the token is invalid or expired) offers **Log in**, reads “Log in again to continue.” (the raw error goes to the console once, already scrubbed, never onto the card) and names its account (`account: 'adobe'`, spectrum ≥ 1.88.0), so Log in calls `settings.connect('adobe')` inside the click and spectrum's card follows the sign-in (its own copy while signing in, then “Signed in to Adobe again.”, from the account's `provider`), or shows the failure as a sentence (a `login` that rejects with “the sign-in timed out” reads “The sign-in timed out.”; a cancel stays an `AbortError`, which shows nothing). A rejected or missing key's card, and the “isn't connected” error, name their account too, so spectrum opens Settings at that row; any other refusal, such as a 403 `missing_required_header`, shows the proxy's message, with the HTTP status and error type below it (`403 missing_required_header`) and **Retry**.
146
148
 
147
149
  **Classifiers.** The Adobe provider also registers Cloudflare's Clef classifiers as `adobe/clef` and `adobe/clef-flash` (`adobeClassifiers()`): `type: 'classifier'` models on pi-ai's `cloudflare-workers-ai-system-one` API with `baseUrl: <proxy>/v1/ai`, so `models.classify(model, { state, questions })` posts `{ model: '@cf/cloudflare/clef', input }` to the proxy's `/v1/ai/run` with the IMS token as the bearer. The provider maps its short ids to the Workers AI names, and pi parses the answers and prices the usage ($0.24 per million input tokens for Clef, $0.09 for Clef Flash, nothing for output), which counts against the same weekly budget. They are classifiers only: `models.getModels()`, the settings `models` and the picker list chat models, and `models.getAvailableOfType('classifier', 'adobe')` returns them only while an Adobe token is stored.
148
150
 
@@ -755,13 +757,21 @@ The agent never holds a secret's value. Values live in the vault (`vault.sliccy.
755
757
  - **Credentials by placeholder.** `records()` lists the `model` and `mcp` records (names, placeholders, states, never values). It is read at start, whenever `version` changes, and after `connect`, `disconnect` and `remove`. The read-only `CredentialStore` behind pi-ai returns a model record's placeholder as the API key (found by `kind: 'model'` and `provider`), with the account's non-secret fields as its env (Bedrock's `region` as `AWS_REGION`); writing to it throws.
756
758
  - **The transport.** The worker's `fetch` is `vaultFetch`: a request to a configured provider's base URL (Bedrock's standard `bedrock-runtime.*.amazonaws.com` and `-fips` endpoints included, so the AWS SDK's bearer requests are covered), or one carrying a model placeholder in its URL or headers, goes out as `model { provider?, request }`; MCP requests go out as `mcp { server, request }`, discovery and client registration included. The vault returns the request scrubbed over every value and with the credential filled in its headers or URL, and the existing transport sends it (`loopbackFetch` for a server on `http://<port>.kernel.localhost`); the response comes back directly. `EAGAIN` prepares the request once more; a refusal becomes a response (403 for `EACCES`, with the vault's sentence); a 401 calls `refresh { provider | server }` once and sends again. Anything else passes through, and without a vault every request does. Transport errors name only the method and origin, never a prepared URL or header.
757
759
  - **Endpoints.** `endpoints { model: [{ provider, url }], mcp: [{ server, url, names?, tokenUrl? }] }` is sent when the port opens and whenever the providers, the accounts or `mcp.json` change: every https base URL of every provider the vault doesn't already know (pi-ai's catalog, Bedrock's regions included, is built into the vault, so only Adobe's proxy and providers outside it are sent), and every server URL with the `${NAME}`s its headers use. Registrations only add.
758
- - **`slicc-secrets`**, a built-in extension modelled on [pi-fnox](https://github.com/Bucurenciu-Cristian/pi-fnox) (MIT, © 2026 Kicky), with its hooks mapped to pi-durable: `secret list --json` in the home env at start and whenever `version` changes (pi-fnox's `fnox export`; nothing goes into the worker's environment); an `afterTool` hook scrubbing every result's text, diagnostics and string `details` (pi-fnox's `tool_result`); a `secrets` system section with the names, placeholders, where each works and how to use them (`before_agent_start`); and `/secrets`, answered locally as a `slicc.secrets` entry (`/fnox-list`; reloading is automatic). When at least one secret is `shell = true`, every conversation command runs as `fnox exec --shell-grants -- <argv>` (pi-fnox's `spawnHook`). slicc-agent doesn't depend on `@kickythrust/pi-fnox`. Every conversation keeps the extension: new ones are pinned at creation, and conversations stored with an extension list that lacks it get it back at start.
760
+ - **`slicc-secrets`**, a built-in extension modelled on [pi-fnox](https://github.com/Bucurenciu-Cristian/pi-fnox) (MIT, © 2026 Kicky), with its hooks mapped to pi-durable. fnox ([`@ai-ecoverse/wasi-fnox`](https://www.npmjs.com/package/@ai-ecoverse/wasi-fnox) 1.39, built by homescoop and used unpatched here) is seven's only secrets CLI; the agent adapts to its own commands and needs `fnox` on `PATH` in the home env.
761
+ - **The listing**, read in the home env at start and whenever `version` changes (pi-fnox's `fnox export`; nothing goes into the worker's environment), joins three commands by name, none of which prints a value:
762
+ - `fnox env --json --describe` gives the fnox config's keys (`kind: "secret"`), which are the `$NAME`s bash sees. It runs only with `--describe`: without it fnox prints values, so every command goes through one guard that refuses `fnox env` without `--describe`. `--for exec|shell` isn't passed, since fnox 1.39 ignores it when describing (both print the same keys).
763
+ - the vault's own `list` op on `/dev/sealed` (`exec 3<>/dev/sealed`, write `{"v":1,"op":"list"}`, read the reply) gives each sealed secret's placeholder, its `[exec]` mode from `/etc/slicc/secrets-policy.toml` and the rules the vault accepted. fnox's describe output has no placeholders, and the agent port's `records` has no `secret` records.
764
+ - `fnox proxy rules --json` gives the config's `[[proxy.rules]]`, shown as proposed for a secret the vault has no accepted rule for. A config without `[proxy]` (exit 1, "No [proxy] configuration found") has no rules.
765
+ - **Shown as:** `- NAME = <placeholder>: <rules>; in every bash` for a sealed secret; a fnox key the vault doesn't hold reads `- NAME = $NAME: not sealed` (a plain fnox secret), and a sealed secret the config lacks says `not in the fnox config`.
766
+ - **Failures are loud.** When fnox is missing (exit 127), a command exits non-zero, `/dev/sealed` fails or a reply doesn't parse, the `secrets` section is "Secrets are unavailable: <reason>.", `/secrets` says the same, and the worker logs one `slicc-secrets: secrets are unavailable: <reason>` line until the reason changes. The reason is a fixed sentence: fnox's error `kind` (`config` reads "the fnox config has an error; check /etc/fnox/config.toml") or the vault's error `code`, never their message, since fnox quotes the offending config line and a plain secret's value can sit on it, which the vault's scrub doesn't know. The message goes only to that log line, redacted by the vault and cut to 200 characters. stderr is never read. Only a config with no keys and a vault with no sealed secrets shows "No secrets are configured".
767
+ - **Shell grants stay off while fnox would hand out unsealed values.** `fnox exec` also sets every config secret it may inject (`injectable.exec`, which is `env = true` and `env = "exec"`), and a plain or age secret's value isn't one the vault scrubs. So while any such key isn't in the vault's list, the wrapper isn't used, the section and `/secrets` say "Shell grants are off: fnox exec would also give every command the unsealed fnox secrets <names>; seal them or set env = false for them.", and one line is logged.
768
+ - **The hooks:** an `afterTool` hook scrubbing every result's text, diagnostics and string `details` (pi-fnox's `tool_result`); the `secrets` system section with the names, placeholders, where each works and how to use them (`before_agent_start`); and `/secrets`, answered locally as a `slicc.secrets` entry (`/fnox-list`; reloading is automatic). When the listing succeeded, no unsealed key would be injected and the vault grants at least one listed secret `{ shell = true }`, every conversation command runs as `fnox exec --shell-grants -- <argv>` (pi-fnox's `spawnHook`). slicc-agent doesn't depend on `@kickythrust/pi-fnox`. Every conversation keeps the extension: new ones are pinned at creation, and conversations stored with an extension list that lacks it get it back at start.
759
769
  - **Where text is scrubbed before anything keeps or cuts it:** command output, per stream, before the bash tool's tail window, the spill file and the live slot; `read`'s bytes (images aren't, and files over 8 MiB stream in 1 MiB chunks); MCP results before their middle cut and progress text; codemode output before its cut; user and remote sends after skill expansion; every lick (sprinkle sends and scoop results included) before triage and the outbox; and memory writes, before the pattern redaction. Remote-control summaries are built from stored entries only.
760
770
  - **The hold.** A vault that doesn't answer within 2 s reads as `waiting`. Nothing unscrubbed is appended: tool results, sends and licks wait in memory, model requests wait before they start, and command output held past 32 MiB kills the command with "output held: waiting for the vault". The state is the transcript's `vault` field (`{ port, needed, version, locked, held }`), and remote `watch` streams get a `notice` "waiting for the vault" and one when it answers again.
761
- - **In spectrum.** The agent port's `hold()` is `{ reason: 'locked', held }` while the vault needs a passkey, `{ reason: 'waiting', held }` while scrubbing is needed and the port isn't `ready`, and `null` otherwise, with a `hold` event on each change (and `null` when it clears); spectrum (≥ 1.62.0) shows it above the composer, and older spectrum ignores it. While a hold is up, the worker's active cone is busy (`working` when it isn't running), so the composer steers and queues; a send made during the hold waits in the tab, shown in that cone's queue, and goes to the worker once the hold clears; removing it before then means it's never sent. `/secrets` entries (`slicc.secrets`) show as a "Secrets listed" notice.
771
+ - **In spectrum.** The agent port's `hold()` is `{ reason: 'locked', held }` while the vault needs a passkey, `{ reason: 'waiting', held }` while scrubbing is needed and the port isn't `ready`, and `null` otherwise, with a `hold` event on each change (and `null` when it clears); spectrum (≥ 1.62.0) shows it above the composer, and older spectrum ignores it. While a hold is up, the worker's active cone is busy (`working` when it isn't running), so the composer steers and queues; a send made during the hold waits in the tab, shown in that cone's queue, and goes to the worker once the hold clears; removing it before then means it's never sent. `/secrets` entries (`slicc.secrets`) show as a "Secrets listed" notice, or "Secrets unavailable" with the reason.
762
772
  - **The 3.x store is wiped.** 3.x kept model and MCP keys in the IndexedDB database `slicc-agent-credentials`; 4.x doesn't migrate them. At start, before any model call, the worker reads only that database's record keys (never decrypting), deletes it, and keeps the accounts it found (`adobe` as Adobe, `amazon-bedrock` as Bedrock, `mcp:<server>|…` as the server, anything else by its providerId) in the session document `slicc.keys-moved`. A missing database, or no `indexedDB`, does nothing. The worker waits up to 3 s for the delete and claims the keys moved only once it succeeds. A delete blocked by an open 3.x tab shows no notice yet: if it succeeds later in the session (the tab closes) its accounts are added then, and otherwise the next start tries again. A delete that fails reports nothing. Once the vault's records are read, the accounts still without a record are the settings state's `moved`, and the spectrum adapter's `notices` port shows one notice, with model accounts first and MCP servers grouped after them: "Your keys moved to seven's vault. Add them again in Settings › Accounts: Adobe, Bedrock; MCP servers: github, jira." (or "…, and the MCP server github" for one). Dismissing it calls `AgentSettings.dismissMoved`, which marks the document, so it stays dismissed across reloads. An account without a record reads not connected. A model call that finds no credential ("Provider is not configured", "No API key") leads with "Adobe isn't connected." or "Bedrock isn't connected.", with "Connect it in Settings › Accounts." underneath instead of the raw error, and opens settings, and a send whose run finds no model because its account isn't connected (Adobe lists models only once signed in) shows the same error. "Your Adobe session has expired." is kept for a 401 from a token that exists. The settings state's `labels` names every model of a provider without a record from the agent's catalogue ("Claude Haiku 4.5 (US) on Bedrock"), and the adapter puts that name in the header of a reply whose model isn't in the picker, instead of the raw id.
763
773
  - **For bios:** the agent's `notices` (keys moved) would replace bios's own port in `{ ...bios, ...createAgentModel(connection) }`, so pass that port in instead, as `createAgentModel(connection, { notices: biosPort })`. The returned port lists bios's notices and then the agent's, sends `act` and `dismiss` to whichever port listed the id (never dismissing one with `dismissible: false`), and passes on both ports' `notices` events. Without the option, it lists only the agent's.
764
- - **Tests.** The worker never compiles values: `src/` takes only `import type { Hit }` from [`@ai-ecoverse/slicc-scrub`](https://www.npmjs.com/package/@ai-ecoverse/slicc-scrub), the vault's scrubber, and the package is a devDependency. The fake vault the tests use scrubs with that package, and its `vectors.json` (every form, line-wrapped base64 and hex included) runs through each scrub point and each cut. A suite-wide check asserts that no captured model request, entry, slot or memory file holds any form of a seeded value.
774
+ - **Tests.** The worker never compiles values: `src/` takes only `import type { Hit }` from [`@ai-ecoverse/slicc-scrub`](https://www.npmjs.com/package/@ai-ecoverse/slicc-scrub), the vault's scrubber, and the package is a devDependency. The fake vault the tests use scrubs with that package, and its `vectors.json` (every form, line-wrapped base64 and hex included) runs through each scrub point and each cut. A suite-wide check asserts that no captured model request, entry, slot or memory file holds any form of a seeded value. `test/integration/secrets-fnox.test.mjs` runs the listing on the page kernel with `@ai-ecoverse/wasi-fnox` (a devDependency) and the fake vault's `/dev/sealed` side (`fakeSealed`: `list`, `placeholder`, `materialize`, `materializeConsume`): a sealed secret in the section and `/secrets`, a shell grant reaching a command through `fnox exec --shell-grants`, shell grants kept off by an unsealed `env = true` secret, fnox missing, fnox exiting non-zero on a config error that quotes a plain value (which never reaches the prompt, `/secrets` or the transcript), and an empty config.
765
775
 
766
776
  ## The kernel
767
777
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/control.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n type UserInput,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Agents, ScoopRecord } from './agents.ts';\nimport { fromBase64, type ShownAttachment, splitUser, userParts } from './attachments.ts';\nimport type { ChangesRuntime } from './changes/index.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { FreezerRuntime } from './freezer/index.ts';\nimport type { Guests } from './guests/index.ts';\nimport type { LickSources, Licks } from './licks/index.ts';\nimport { LICK_STATE_KIND } from './licks/state.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport type { Monitor } from './monitor/index.ts';\nimport type { Scoops, ScoopsRuntime } from './scoops/index.ts';\nimport { SECRETS_COMMAND, secretsEntry } from './secrets/extension.ts';\nimport type { SecretName } from './secrets/names.ts';\nimport { scrubItems, scrubText } from './secrets/scrub.ts';\nimport { copyAgent, storedAgent } from './selection.ts';\nimport type {\n AgentControl,\n Created,\n ImageRef,\n LickResolved,\n OperationError,\n Rewound,\n SendRequest,\n SendResponse,\n StoredImage,\n} from './services.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { LabelsRuntime } from './titles/labels.ts';\nimport { saverFor, type Uploads } from './uploads.ts';\n\nexport interface HostLicks {\n licks: Licks;\n sources: LickSources;\n}\n\nexport interface HostScoops {\n scoops: Scoops;\n runtime: ScoopsRuntime;\n}\n\ntype WithAgents = { agents?: Agents };\n\nexport function readable(out: string): string {\n const text = out.replace(/^(?:sub)?agent: /, '').trim();\n const sentence = text.charAt(0).toUpperCase() + text.slice(1);\n return /[.!?]$/.test(sentence) ? sentence : `${sentence}.`;\n}\n\nfunction created(run: () => Promise<string>): Promise<Created> {\n return run().then(\n (id) => ({ id, error: null }),\n (error: Error) => ({ id: null, error: error.message })\n );\n}\n\nfunction monitorControl(\n monitor: Pick<Monitor, 'open' | 'close'> | undefined\n): Pick<AgentControl, 'monitorOpen' | 'monitorClose'> {\n return {\n monitorOpen: async (lease, resync, context) =>\n monitor ? monitor.open(lease, resync, context) : { lease: '', ttlMs: 0 },\n monitorClose: async (lease, context) => monitor?.close(lease, context),\n };\n}\n\nconst noApprovals: AgentControl['decide'] = async () => ({\n done: false,\n state: null,\n error: 'This agent has no approvals.',\n});\n\ntype FreezerControl = Pick<\n FreezerRuntime,\n 'freeze' | 'newChat' | 'thaw' | 'discard' | 'importProfile' | 'cancelImport'\n>;\n\nfunction freezerControl(\n freezer: FreezerControl | undefined\n): Pick<\n AgentControl,\n 'freeze' | 'newChat' | 'thaw' | 'discard' | 'importProfile' | 'importCancel'\n> {\n const needed = () => {\n if (!freezer) throw new Error('This agent has no freezer.');\n return freezer;\n };\n return {\n freeze: (agentId, context) => created(async () => needed().freeze(agentId, context)),\n newChat: (agentId, context) =>\n created(async () => (await needed().newChat(agentId, context)) ?? ''),\n thaw: (id, context) => created(async () => needed().thaw(id, context)),\n discard: (id, context) =>\n created(async () => {\n await needed().discard(id, context);\n return id;\n }),\n importProfile: (request, context) =>\n freezer\n ? freezer.importProfile(\n request.data === null\n ? { upload: request.upload ?? '' }\n : { bytes: fromBase64(request.data) },\n { again: request.again, preview: request.preview },\n context\n )\n : Promise.resolve({\n done: false,\n preview: null,\n cones: [],\n report: null,\n text: '',\n line: '',\n error: 'This agent has no freezer.',\n }),\n importCancel: async () => freezer?.cancelImport(),\n };\n}\n\nfunction guestControl(\n guests: Pick<Guests, 'open' | 'revoke' | 'forgetAlways'> | undefined\n): Pick<AgentControl, 'guestOpen' | 'guestRevoke' | 'guestAlwaysForget'> {\n return {\n guestOpen: async (draft, context) =>\n guests ? guests.open(draft, context) : { seat: null, error: NO_GUESTS },\n guestRevoke: async (id, context) => (guests ? guests.revoke(id, context) : { revoked: false }),\n guestAlwaysForget: async (seat, approval, context) =>\n guests ? guests.forgetAlways(seat, approval, context) : { removed: false },\n };\n}\n\nconst NO_GUESTS = 'This agent has no guest seats.';\n\nfunction changesControl(\n changes: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'> | undefined\n): Pick<AgentControl, 'changesOpen' | 'changeAccept' | 'changeRevert'> {\n const needed = () => {\n if (!changes) throw new Error('This agent tracks no changes.');\n return changes;\n };\n const done = (path: string, run: (runtime: NonNullable<typeof changes>) => Promise<void>) =>\n created(async () => {\n await run(needed());\n return path;\n });\n return {\n changesOpen: async (context) => changes?.open(context),\n changeAccept: (path, context) => done(path, (runtime) => runtime.accept(path, context)),\n changeRevert: (path, context) => done(path, (runtime) => runtime.revert(path, context)),\n };\n}\n\nfunction memoryControl(\n memory: Pick<MemoryRuntime, 'save' | 'remove'> | undefined\n): Pick<AgentControl, 'memorySave' | 'memoryRemove'> {\n return {\n async memorySave(draft, context) {\n if (!memory) throw new Error('This agent has no memory.');\n const { id, ...rest } = draft;\n return memory.save({ ...rest, ...(id ? { id } : {}) }, context);\n },\n async memoryRemove(id, context) {\n return { removed: memory ? await memory.remove(id, context) : false };\n },\n };\n}\n\nfunction sprinkleControl(\n sprinkles: Pick<SprinklesRuntime, 'send' | 'call'> | undefined\n): Pick<AgentControl, 'sprinkleSend' | 'sprinkleCall'> {\n return {\n sprinkleSend: async (id, payload, context) =>\n sprinkles ? sprinkles.send(id, payload, context) : { delivered: false },\n async sprinkleCall(id, method, args, context) {\n if (!sprinkles) throw new Error('This agent has no sprinkles.');\n return sprinkles.call(id, method, args, context);\n },\n };\n}\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n attachments: ShownAttachment[];\n at: EntryId | null;\n files: boolean;\n}\n\ntype Block = { type?: string; name?: string };\n\nconst WRITERS = ['write', 'edit', 'bash', 'codemode'];\n\nfunction writesIn(entries: readonly EntryRecord[]): boolean {\n return entries.some((entry) =>\n (entry.model ?? []).some(\n (message) =>\n Array.isArray(message.content) &&\n (message.content as Block[]).some(\n (block) => block.type === 'toolCall' && WRITERS.includes(String(block.name))\n )\n )\n );\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return {\n ...splitUser(user.model?.[0]?.content),\n at: entries[turn - 1]?.id ?? null,\n files: writesIn(entries.slice(turn)),\n };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\ntype RewindDeps = {\n harness: Harness;\n agents?: Agents;\n cone: Cones;\n scoops?: HostScoops;\n};\n\nasync function turnIn(\n harness: Harness,\n conversation: Conversation,\n messageId: string | null,\n context: Context\n): Promise<{ turn: Turn; view: ConversationView } | Rewound> {\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n return turn ? { turn, view } : { done: false, text: null, reason: 'no-turn' };\n}\n\nasync function forkAt(\n harness: Harness,\n conversation: Conversation,\n found: { turn: Turn },\n context: Context\n): Promise<Conversation> {\n const ownership = { kind: 'ownerless' } as const;\n if (found.turn.at !== null) return conversation.fork(found.turn.at, { ownership }, context);\n const init = copyAgent(await storedAgent(harness, conversation.id, context));\n return harness.createConversation({ ownership, init }, context);\n}\n\nasync function rewindAgent(\n deps: RewindDeps,\n agentId: string | null,\n messageId: string | null,\n context: Context\n): Promise<Rewound> {\n const { harness, agents, cone, scoops } = deps;\n const scoop = agentId && agents ? agents.state().scoops[agentId] : undefined;\n if (agentId && agents && !scoop && agentId !== agents.activeCone())\n await agents.selectCone(agentId, context);\n const conversation =\n scoop && agents\n ? ((await agents.conversation(agentId as string, context)) as Conversation)\n : cone.cone();\n const found = await turnIn(harness, conversation, messageId, context);\n if ('reason' in found) return found;\n const fork = await forkAt(harness, conversation, found, context);\n const changed =\n scoops && agents && !scoop\n ? await scoops.scoops.rewound(agents.activeCone(), fork, context)\n : { stopped: [], restored: [] };\n const files = found.turn.files;\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1, ...changed, files } } },\n context\n );\n if (scoop && agents)\n await agents.update((_tx, state) => {\n const record = state.scoops[agentId as string] as ScoopRecord;\n record.conversation = fork.id;\n delete record.title;\n delete record.titled;\n }, context);\n else await cone.switchCone(fork, context);\n const { attachments } = found.turn;\n return {\n done: true,\n text: found.turn.text,\n reason: null,\n ...(attachments.length ? { attachments } : {}),\n };\n}\n\ntype Picture = { type?: string; data?: string; mimeType?: string };\n\nasync function storedImage(\n harness: Harness,\n ref: ImageRef,\n context: Context\n): Promise<StoredImage> {\n const entry = await harness.commit((tx) => tx.entry(ref.entry as EntryId), context);\n const content = entry?.model?.[ref.message]?.content;\n const part = Array.isArray(content) ? (content[ref.part] as Picture | undefined) : undefined;\n return part?.type === 'image' && part.data && part.mimeType\n ? { data: part.data, mimeType: part.mimeType }\n : { data: null, mimeType: null };\n}\n\nasync function contentOf(\n text: string,\n request: SendRequest,\n conversation: Conversation,\n uploads: Uploads | undefined,\n context: Context\n): Promise<UserInput> {\n if (!request.attachments?.length) return scrubText(text);\n const saver = await saverFor(conversation, uploads, context);\n return scrubItems(await userParts(text, request.attachments, saver));\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries,\n licks?: HostLicks,\n scoops?: HostScoops,\n skills?: Pick<SkillsRuntime, 'expand' | 'commandsFor'>,\n sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>,\n memory?: Pick<MemoryRuntime, 'save' | 'remove'>,\n freezer?: FreezerControl,\n changes?: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'>,\n monitor?: Pick<Monitor, 'open' | 'close'>,\n secrets?: () => readonly SecretName[],\n uploads?: Uploads,\n labels?: Pick<LabelsRuntime, 'label'>,\n guests?: Pick<Guests, 'open' | 'revoke' | 'forgetAlways'>\n): Omit<AgentControl, 'approvalPolicySet'> {\n const cone = cones(target);\n const current = () => cone.cone();\n const agents = (target as WithAgents).agents;\n const conversationFor = async (agentId: string | null | undefined, context: Context) => {\n if (!agentId || !agents) return current();\n const found = await agents.conversation(agentId, context);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n };\n const withdraw = async (conversation: Conversation, id: string, context: Context) => {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' as const };\n const result = await harness.abortSubmission(parsed, context, conversation.id);\n if (result === 'aborted') return { outcome: 'withdrawn' as const };\n return {\n outcome: result === 'not_found' ? ('not_found' as const) : ('already_placed' as const),\n };\n };\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n const rewindIn = (agentId: string | null, messageId: string | null, context: Context) =>\n serial(() => rewindAgent({ harness, agents, cone, scoops }, agentId, messageId, context));\n const decide = (\n agentId: string | null,\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved> =>\n serial(async () => {\n if (!licks) return { done: false, text: null, error: 'this agent has no licks' };\n const action = state === 'confirmed' ? 'confirm' : 'dismiss';\n try {\n const conversation = await conversationFor(agentId, context);\n const text = await licks.licks.decide(conversation, lickId, action, undefined, context);\n await conversation.submit(\n {\n type: 'write',\n entry: {\n kind: LICK_STATE_KIND,\n data: { lick: lickId, state, by: 'user' },\n model: [\n {\n role: 'user',\n content: `The user ${state} lick ${lickId}: ${text}`,\n timestamp: Date.now(),\n },\n ],\n },\n },\n context\n );\n return { done: true, text, error: null };\n } catch (error) {\n return { done: false, text: null, error: (error as Error).message };\n }\n });\n return {\n send(request, context) {\n return serial(async () => {\n if (secrets && request.text.trim() === SECRETS_COMMAND)\n return accepted(async () =>\n (await conversationFor(request.agentId, context)).submit(\n { type: 'write', entry: secretsEntry(secrets()) },\n context\n )\n );\n const response = await accepted(async () => {\n const conversation = await conversationFor(request.agentId, context);\n const expanded = skills\n ? await skills.expand(request.text, context, conversation.id)\n : request.text;\n return conversation.submit(\n {\n type: 'input',\n content: await contentOf(expanded, request, conversation, uploads, context),\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n );\n });\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind: (messageId, context) => rewindIn(null, messageId, context),\n rewindAgent: (agentId, messageId, context) => rewindIn(agentId, messageId, context),\n\n async wait(id, context) {\n const parsed = submissionId(id);\n const submission =\n parsed === undefined ? undefined : await harness.submission(parsed, context);\n if (!submission) throw new Error(`unknown submission ${id}`);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) return { status: 'unanswered', text: null, reason: String(reason) };\n const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return { status: 'done', text: answerText(entry?.model?.[0]?.content), reason: null };\n },\n withdraw: (id, context) => withdraw(current(), id, context),\n ...sprinkleControl(sprinkles),\n ...memoryControl(memory),\n ...freezerControl(freezer),\n ...changesControl(changes),\n ...guestControl(guests),\n ...monitorControl(monitor),\n image: (ref, context) => storedImage(harness, ref, context),\n async unqueue(agentId, id, context) {\n return withdraw(await conversationFor(agentId, context), id, context);\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n compactAgent: (agentId, instructions, context) =>\n accepted(async () =>\n (await conversationFor(agentId, context)).compact(instructions ?? undefined, context)\n ),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n resolveLick: (lickId, state, context) => decide(null, lickId, state, context),\n resolveAgentLick: (agentId, lickId, state, context) => decide(agentId, lickId, state, context),\n async webhook(name, delivery, context) {\n const delivered = licks\n ? await licks.sources.webhook(\n name,\n {\n ...(delivery.id ? { id: delivery.id } : {}),\n headers: delivery.headers,\n body: delivery.body,\n },\n context\n )\n : false;\n return { delivered };\n },\n async stopAgent(agentId, context) {\n await (await conversationFor(agentId, context)).abort(context);\n },\n async selectCone(agentId, context) {\n await agents?.selectCone(agentId, context);\n },\n createCone(name, context) {\n return created(async () => {\n if (!agents) throw new Error('This agent has a single cone.');\n return agents.createCone(name, context);\n });\n },\n createScoop(parentId, name, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const roles = await scoops.runtime.roles(context);\n const answer = await scoops.scoops.spawn(\n { cone: parentId, name, prompts: [], fromAgent: false, limits: roles.limits },\n context\n );\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return `scoop:${answer.out.trim()}`;\n });\n },\n drop(agentId, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const answer = await scoops.scoops.stop(agentId, false, context);\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return agentId;\n });\n },\n configure: async (change, context) =>\n (await conversationFor(change.agentId, context)).configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n agentCommands: async (agentId) => skills?.commandsFor(agentId) ?? [],\n toolLabel: async (agentId, callIds, context) =>\n labels ? labels.label(agentId, callIds, context) : null,\n decide: noApprovals,\n };\n}\n"],
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n type UserInput,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Agents, ScoopRecord } from './agents.ts';\nimport { fromBase64, type ShownAttachment, splitUser, userParts } from './attachments.ts';\nimport type { ChangesRuntime } from './changes/index.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { FreezerRuntime } from './freezer/index.ts';\nimport type { Guests } from './guests/index.ts';\nimport type { LickSources, Licks } from './licks/index.ts';\nimport { LICK_STATE_KIND } from './licks/state.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport type { Monitor } from './monitor/index.ts';\nimport type { Scoops, ScoopsRuntime } from './scoops/index.ts';\nimport { SECRETS_COMMAND, secretsEntry } from './secrets/extension.ts';\nimport type { SecretListing } from './secrets/names.ts';\nimport { scrubItems, scrubText } from './secrets/scrub.ts';\nimport { copyAgent, storedAgent } from './selection.ts';\nimport type {\n AgentControl,\n Created,\n ImageRef,\n LickResolved,\n OperationError,\n Rewound,\n SendRequest,\n SendResponse,\n StoredImage,\n} from './services.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { LabelsRuntime } from './titles/labels.ts';\nimport { saverFor, type Uploads } from './uploads.ts';\n\nexport interface HostLicks {\n licks: Licks;\n sources: LickSources;\n}\n\nexport interface HostScoops {\n scoops: Scoops;\n runtime: ScoopsRuntime;\n}\n\ntype WithAgents = { agents?: Agents };\n\nexport function readable(out: string): string {\n const text = out.replace(/^(?:sub)?agent: /, '').trim();\n const sentence = text.charAt(0).toUpperCase() + text.slice(1);\n return /[.!?]$/.test(sentence) ? sentence : `${sentence}.`;\n}\n\nfunction created(run: () => Promise<string>): Promise<Created> {\n return run().then(\n (id) => ({ id, error: null }),\n (error: Error) => ({ id: null, error: error.message })\n );\n}\n\nfunction monitorControl(\n monitor: Pick<Monitor, 'open' | 'close'> | undefined\n): Pick<AgentControl, 'monitorOpen' | 'monitorClose'> {\n return {\n monitorOpen: async (lease, resync, context) =>\n monitor ? monitor.open(lease, resync, context) : { lease: '', ttlMs: 0 },\n monitorClose: async (lease, context) => monitor?.close(lease, context),\n };\n}\n\nconst noApprovals: AgentControl['decide'] = async () => ({\n done: false,\n state: null,\n error: 'This agent has no approvals.',\n});\n\ntype FreezerControl = Pick<\n FreezerRuntime,\n 'freeze' | 'newChat' | 'thaw' | 'discard' | 'importProfile' | 'cancelImport'\n>;\n\nfunction freezerControl(\n freezer: FreezerControl | undefined\n): Pick<\n AgentControl,\n 'freeze' | 'newChat' | 'thaw' | 'discard' | 'importProfile' | 'importCancel'\n> {\n const needed = () => {\n if (!freezer) throw new Error('This agent has no freezer.');\n return freezer;\n };\n return {\n freeze: (agentId, context) => created(async () => needed().freeze(agentId, context)),\n newChat: (agentId, context) =>\n created(async () => (await needed().newChat(agentId, context)) ?? ''),\n thaw: (id, context) => created(async () => needed().thaw(id, context)),\n discard: (id, context) =>\n created(async () => {\n await needed().discard(id, context);\n return id;\n }),\n importProfile: (request, context) =>\n freezer\n ? freezer.importProfile(\n request.data === null\n ? { upload: request.upload ?? '' }\n : { bytes: fromBase64(request.data) },\n { again: request.again, preview: request.preview },\n context\n )\n : Promise.resolve({\n done: false,\n preview: null,\n cones: [],\n report: null,\n text: '',\n line: '',\n error: 'This agent has no freezer.',\n }),\n importCancel: async () => freezer?.cancelImport(),\n };\n}\n\nfunction guestControl(\n guests: Pick<Guests, 'open' | 'revoke' | 'forgetAlways'> | undefined\n): Pick<AgentControl, 'guestOpen' | 'guestRevoke' | 'guestAlwaysForget'> {\n return {\n guestOpen: async (draft, context) =>\n guests ? guests.open(draft, context) : { seat: null, error: NO_GUESTS },\n guestRevoke: async (id, context) => (guests ? guests.revoke(id, context) : { revoked: false }),\n guestAlwaysForget: async (seat, approval, context) =>\n guests ? guests.forgetAlways(seat, approval, context) : { removed: false },\n };\n}\n\nconst NO_GUESTS = 'This agent has no guest seats.';\n\nfunction changesControl(\n changes: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'> | undefined\n): Pick<AgentControl, 'changesOpen' | 'changeAccept' | 'changeRevert'> {\n const needed = () => {\n if (!changes) throw new Error('This agent tracks no changes.');\n return changes;\n };\n const done = (path: string, run: (runtime: NonNullable<typeof changes>) => Promise<void>) =>\n created(async () => {\n await run(needed());\n return path;\n });\n return {\n changesOpen: async (context) => changes?.open(context),\n changeAccept: (path, context) => done(path, (runtime) => runtime.accept(path, context)),\n changeRevert: (path, context) => done(path, (runtime) => runtime.revert(path, context)),\n };\n}\n\nfunction memoryControl(\n memory: Pick<MemoryRuntime, 'save' | 'remove'> | undefined\n): Pick<AgentControl, 'memorySave' | 'memoryRemove'> {\n return {\n async memorySave(draft, context) {\n if (!memory) throw new Error('This agent has no memory.');\n const { id, ...rest } = draft;\n return memory.save({ ...rest, ...(id ? { id } : {}) }, context);\n },\n async memoryRemove(id, context) {\n return { removed: memory ? await memory.remove(id, context) : false };\n },\n };\n}\n\nfunction sprinkleControl(\n sprinkles: Pick<SprinklesRuntime, 'send' | 'call'> | undefined\n): Pick<AgentControl, 'sprinkleSend' | 'sprinkleCall'> {\n return {\n sprinkleSend: async (id, payload, context) =>\n sprinkles ? sprinkles.send(id, payload, context) : { delivered: false },\n async sprinkleCall(id, method, args, context) {\n if (!sprinkles) throw new Error('This agent has no sprinkles.');\n return sprinkles.call(id, method, args, context);\n },\n };\n}\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n attachments: ShownAttachment[];\n at: EntryId | null;\n files: boolean;\n}\n\ntype Block = { type?: string; name?: string };\n\nconst WRITERS = ['write', 'edit', 'bash', 'codemode'];\n\nfunction writesIn(entries: readonly EntryRecord[]): boolean {\n return entries.some((entry) =>\n (entry.model ?? []).some(\n (message) =>\n Array.isArray(message.content) &&\n (message.content as Block[]).some(\n (block) => block.type === 'toolCall' && WRITERS.includes(String(block.name))\n )\n )\n );\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return {\n ...splitUser(user.model?.[0]?.content),\n at: entries[turn - 1]?.id ?? null,\n files: writesIn(entries.slice(turn)),\n };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\ntype RewindDeps = {\n harness: Harness;\n agents?: Agents;\n cone: Cones;\n scoops?: HostScoops;\n};\n\nasync function turnIn(\n harness: Harness,\n conversation: Conversation,\n messageId: string | null,\n context: Context\n): Promise<{ turn: Turn; view: ConversationView } | Rewound> {\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n return turn ? { turn, view } : { done: false, text: null, reason: 'no-turn' };\n}\n\nasync function forkAt(\n harness: Harness,\n conversation: Conversation,\n found: { turn: Turn },\n context: Context\n): Promise<Conversation> {\n const ownership = { kind: 'ownerless' } as const;\n if (found.turn.at !== null) return conversation.fork(found.turn.at, { ownership }, context);\n const init = copyAgent(await storedAgent(harness, conversation.id, context));\n return harness.createConversation({ ownership, init }, context);\n}\n\nasync function rewindAgent(\n deps: RewindDeps,\n agentId: string | null,\n messageId: string | null,\n context: Context\n): Promise<Rewound> {\n const { harness, agents, cone, scoops } = deps;\n const scoop = agentId && agents ? agents.state().scoops[agentId] : undefined;\n if (agentId && agents && !scoop && agentId !== agents.activeCone())\n await agents.selectCone(agentId, context);\n const conversation =\n scoop && agents\n ? ((await agents.conversation(agentId as string, context)) as Conversation)\n : cone.cone();\n const found = await turnIn(harness, conversation, messageId, context);\n if ('reason' in found) return found;\n const fork = await forkAt(harness, conversation, found, context);\n const changed =\n scoops && agents && !scoop\n ? await scoops.scoops.rewound(agents.activeCone(), fork, context)\n : { stopped: [], restored: [] };\n const files = found.turn.files;\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1, ...changed, files } } },\n context\n );\n if (scoop && agents)\n await agents.update((_tx, state) => {\n const record = state.scoops[agentId as string] as ScoopRecord;\n record.conversation = fork.id;\n delete record.title;\n delete record.titled;\n }, context);\n else await cone.switchCone(fork, context);\n const { attachments } = found.turn;\n return {\n done: true,\n text: found.turn.text,\n reason: null,\n ...(attachments.length ? { attachments } : {}),\n };\n}\n\ntype Picture = { type?: string; data?: string; mimeType?: string };\n\nasync function storedImage(\n harness: Harness,\n ref: ImageRef,\n context: Context\n): Promise<StoredImage> {\n const entry = await harness.commit((tx) => tx.entry(ref.entry as EntryId), context);\n const content = entry?.model?.[ref.message]?.content;\n const part = Array.isArray(content) ? (content[ref.part] as Picture | undefined) : undefined;\n return part?.type === 'image' && part.data && part.mimeType\n ? { data: part.data, mimeType: part.mimeType }\n : { data: null, mimeType: null };\n}\n\nasync function contentOf(\n text: string,\n request: SendRequest,\n conversation: Conversation,\n uploads: Uploads | undefined,\n context: Context\n): Promise<UserInput> {\n if (!request.attachments?.length) return scrubText(text);\n const saver = await saverFor(conversation, uploads, context);\n return scrubItems(await userParts(text, request.attachments, saver));\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries,\n licks?: HostLicks,\n scoops?: HostScoops,\n skills?: Pick<SkillsRuntime, 'expand' | 'commandsFor'>,\n sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>,\n memory?: Pick<MemoryRuntime, 'save' | 'remove'>,\n freezer?: FreezerControl,\n changes?: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'>,\n monitor?: Pick<Monitor, 'open' | 'close'>,\n secrets?: () => SecretListing,\n uploads?: Uploads,\n labels?: Pick<LabelsRuntime, 'label'>,\n guests?: Pick<Guests, 'open' | 'revoke' | 'forgetAlways'>\n): Omit<AgentControl, 'approvalPolicySet'> {\n const cone = cones(target);\n const current = () => cone.cone();\n const agents = (target as WithAgents).agents;\n const conversationFor = async (agentId: string | null | undefined, context: Context) => {\n if (!agentId || !agents) return current();\n const found = await agents.conversation(agentId, context);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n };\n const withdraw = async (conversation: Conversation, id: string, context: Context) => {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' as const };\n const result = await harness.abortSubmission(parsed, context, conversation.id);\n if (result === 'aborted') return { outcome: 'withdrawn' as const };\n return {\n outcome: result === 'not_found' ? ('not_found' as const) : ('already_placed' as const),\n };\n };\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n const rewindIn = (agentId: string | null, messageId: string | null, context: Context) =>\n serial(() => rewindAgent({ harness, agents, cone, scoops }, agentId, messageId, context));\n const decide = (\n agentId: string | null,\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved> =>\n serial(async () => {\n if (!licks) return { done: false, text: null, error: 'this agent has no licks' };\n const action = state === 'confirmed' ? 'confirm' : 'dismiss';\n try {\n const conversation = await conversationFor(agentId, context);\n const text = await licks.licks.decide(conversation, lickId, action, undefined, context);\n await conversation.submit(\n {\n type: 'write',\n entry: {\n kind: LICK_STATE_KIND,\n data: { lick: lickId, state, by: 'user' },\n model: [\n {\n role: 'user',\n content: `The user ${state} lick ${lickId}: ${text}`,\n timestamp: Date.now(),\n },\n ],\n },\n },\n context\n );\n return { done: true, text, error: null };\n } catch (error) {\n return { done: false, text: null, error: (error as Error).message };\n }\n });\n return {\n send(request, context) {\n return serial(async () => {\n if (secrets && request.text.trim() === SECRETS_COMMAND)\n return accepted(async () =>\n (await conversationFor(request.agentId, context)).submit(\n { type: 'write', entry: secretsEntry(secrets()) },\n context\n )\n );\n const response = await accepted(async () => {\n const conversation = await conversationFor(request.agentId, context);\n const expanded = skills\n ? await skills.expand(request.text, context, conversation.id)\n : request.text;\n return conversation.submit(\n {\n type: 'input',\n content: await contentOf(expanded, request, conversation, uploads, context),\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n );\n });\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind: (messageId, context) => rewindIn(null, messageId, context),\n rewindAgent: (agentId, messageId, context) => rewindIn(agentId, messageId, context),\n\n async wait(id, context) {\n const parsed = submissionId(id);\n const submission =\n parsed === undefined ? undefined : await harness.submission(parsed, context);\n if (!submission) throw new Error(`unknown submission ${id}`);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) return { status: 'unanswered', text: null, reason: String(reason) };\n const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return { status: 'done', text: answerText(entry?.model?.[0]?.content), reason: null };\n },\n withdraw: (id, context) => withdraw(current(), id, context),\n ...sprinkleControl(sprinkles),\n ...memoryControl(memory),\n ...freezerControl(freezer),\n ...changesControl(changes),\n ...guestControl(guests),\n ...monitorControl(monitor),\n image: (ref, context) => storedImage(harness, ref, context),\n async unqueue(agentId, id, context) {\n return withdraw(await conversationFor(agentId, context), id, context);\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n compactAgent: (agentId, instructions, context) =>\n accepted(async () =>\n (await conversationFor(agentId, context)).compact(instructions ?? undefined, context)\n ),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n resolveLick: (lickId, state, context) => decide(null, lickId, state, context),\n resolveAgentLick: (agentId, lickId, state, context) => decide(agentId, lickId, state, context),\n async webhook(name, delivery, context) {\n const delivered = licks\n ? await licks.sources.webhook(\n name,\n {\n ...(delivery.id ? { id: delivery.id } : {}),\n headers: delivery.headers,\n body: delivery.body,\n },\n context\n )\n : false;\n return { delivered };\n },\n async stopAgent(agentId, context) {\n await (await conversationFor(agentId, context)).abort(context);\n },\n async selectCone(agentId, context) {\n await agents?.selectCone(agentId, context);\n },\n createCone(name, context) {\n return created(async () => {\n if (!agents) throw new Error('This agent has a single cone.');\n return agents.createCone(name, context);\n });\n },\n createScoop(parentId, name, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const roles = await scoops.runtime.roles(context);\n const answer = await scoops.scoops.spawn(\n { cone: parentId, name, prompts: [], fromAgent: false, limits: roles.limits },\n context\n );\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return `scoop:${answer.out.trim()}`;\n });\n },\n drop(agentId, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const answer = await scoops.scoops.stop(agentId, false, context);\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return agentId;\n });\n },\n configure: async (change, context) =>\n (await conversationFor(change.agentId, context)).configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n agentCommands: async (agentId) => skills?.commandsFor(agentId) ?? [],\n toolLabel: async (agentId, callIds, context) =>\n labels ? labels.label(agentId, callIds, context) : null,\n decide: noApprovals,\n };\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/host.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/host.ts"],
4
- "sourcesContent": ["import {\n type Context,\n createHost,\n forbidden,\n type Host,\n type Readable,\n type ReplicatedState,\n replicatedState,\n type Self,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agent } from './agent.ts';\nimport {\n type ApprovalJudge,\n type Approvals,\n attachApprovals,\n type Decider,\n} from './approvals/index.ts';\nimport { type ApprovalPolicies, alwaysFor, attachApprovalPolicy } from './approvals/policy.ts';\nimport { type AgentSudo, createSudo, ownedSudo } from './approvals/sudo.ts';\nimport type { ChangesView } from './changes/git.ts';\nimport type { ChangesRuntime } from './changes/index.ts';\nimport { createAgentControl, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport type { FreezerRuntime, FrozenCone } from './freezer/index.ts';\nimport { IDLE, type ImportProgress } from './freezer/v6.ts';\nimport { createGuests, type Guests } from './guests/index.ts';\nimport type { ScoopReviewHost } from './guests/scoop.ts';\nimport { admitGuest, guestOpener } from './guests/serve.ts';\nimport type { UserClients } from './kernel/users.ts';\nimport type { McpRuntime } from './mcp/index.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport { createMonitor, type MonitorOptions } from './monitor/index.ts';\nimport { viewRemote } from './readonly.ts';\nimport { type AgentRemote, createRemote } from './remote/host.ts';\nimport { createPages, ownedPages, type Pages } from './remote/pages.ts';\nimport type { SecretName } from './secrets/names.ts';\nimport type { VaultState } from './secrets/vault.ts';\nimport {\n AgentControl,\n type AgentGuest,\n AgentGuestService,\n AgentPagesService,\n AgentRemoteService,\n AgentSettings,\n AgentSudoService,\n AgentTranscript,\n CONTROL_METHODS,\n CONTROL_READS,\n type Command,\n GUEST_METHODS,\n OWNER_DEVICE_METHODS,\n PAGE_METHODS,\n type PrivateRevision,\n REMOTE_METHODS,\n REMOTE_READS,\n SETTINGS_METHODS,\n SUDO_METHODS,\n} from './services.ts';\nimport {\n AGENT_HOST,\n type AgentPrincipal,\n type GuestPrincipal,\n principalKey,\n readable,\n readablesOf,\n SELF,\n type Seat,\n served,\n} from './serving.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { slimmer, type Thumbnail, thumbnail } from './thumbs.ts';\nimport { type LabelsRuntime, visibleLabels, watchLabel } from './titles/labels.ts';\nimport type { Uploads } from './uploads.ts';\nimport { agentViews } from './views.ts';\nimport { type PortEndpoint, PortLink } from './wire.ts';\n\nexport interface ConnectOptions {\n readOnly?: boolean;\n pages?: boolean;\n guest?: { id: string; label: string };\n device?: string;\n}\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n readonly users?: UserClients;\n readonly pages: Pages;\n readonly guests: Guests;\n readonly approvals: Approvals;\n connect(endpoint: PortEndpoint, options?: ConnectOptions): void;\n close(): Promise<void>;\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n remove: async () => {},\n serverUrl: async () => null,\n signIn: async () => null,\n dismissMoved: async () => {},\n};\n\nasync function follow(\n agent: Agent,\n context: Context,\n slim: (view: ConversationView) => ConversationView\n) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(slim(attached.value));\n const mirror = (value: ConversationView, from: Context) => state.replace(from, slim(value));\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, slim(next.value));\n off = next.subscribe(mirror);\n });\n return {\n state,\n refresh: () => state.replace(context, slim(attached.value)),\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport interface HostSecrets {\n vault: ReplicatedState<VaultState>;\n list(): readonly SecretName[];\n close?(): void;\n}\n\nconst idleVault = replicatedState<VaultState>({\n port: 'none',\n needed: false,\n version: 0,\n locked: 'no',\n held: 0,\n});\n\nfunction gatedControls(\n control: AgentControl,\n shown: (entry: number) => boolean,\n views: () => readonly ConversationView[],\n labels: ReplicatedState<Record<string, string>>\n) {\n const visible = (entry: number) =>\n views().some((view) => view.entries.some((item) => item.id === entry));\n const gated =\n (allowed: (entry: number) => boolean): AgentControl['image'] =>\n async (ref, using) =>\n allowed(ref.entry) ? control.image(ref, using) : { data: null, mimeType: null };\n const trusted: AgentControl = { ...control, image: gated(shown) };\n const viewed: AgentControl = { ...control, image: gated(visible), toolLabel: watchLabel(labels) };\n return { trusted, viewed };\n}\n\nasync function approving(\n agent: Agent,\n policy: ApprovalPolicies,\n options: { judge?: ApprovalJudge; intent?: ScoopReviewHost['intent']; approvalMs?: number },\n context: Context\n): Promise<Approvals> {\n const approvals = await attachApprovals(\n {\n harness: agent.harness,\n guestAlways: alwaysFor(policy),\n sudoApprover: () => policy.current().sudo.approver,\n scoops: agent.scoopReviews,\n ...(options.judge ? { judge: options.judge } : {}),\n ...(options.approvalMs ? { ttlMs: options.approvalMs } : {}),\n },\n context\n );\n agent.scoopReviews.bind({\n approvals,\n agents: agent.agents,\n harness: agent.harness,\n ...(options.intent ? { intent: options.intent } : {}),\n });\n return approvals;\n}\n\nasync function guestsOf(\n agent: Agent,\n approvals: Approvals,\n policy: ApprovalPolicies,\n context: Context\n): Promise<Guests> {\n const guests = await createGuests({\n harness: agent.harness,\n agents: agent.agents,\n approvals,\n policy,\n reviews: agent.reviews,\n context,\n });\n agent.gate.bind({ approvals, policy: () => guests.policy() });\n return guests;\n}\n\nconst noRevision = () => replicatedState<PrivateRevision>({ revision: 0 });\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n skills?: SkillsRuntime;\n sprinkles?: SprinklesRuntime;\n memory?: MemoryRuntime;\n freezer?: FreezerRuntime;\n changes?: ChangesRuntime;\n mcp?: Pick<McpRuntime, 'close'>;\n users?: UserClients;\n monitor?: Pick<MonitorOptions, 'kernel' | 'groups' | 'users'>;\n pages?: Pages;\n secrets?: HostSecrets;\n uploads?: Uploads;\n thumbnail?: Thumbnail;\n labels?: LabelsRuntime;\n approvals?: Approvals;\n judge?: ApprovalJudge;\n intent?: ScoopReviewHost['intent'];\n approvalMs?: number;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n let refresh = () => {};\n const slims = slimmer(options.thumbnail ?? thumbnail, () => refresh());\n const transcript = await follow(agent, context, slims.slim);\n const { state } = transcript;\n const mounted = await agentViews(agent, context, slims.slim);\n refresh = () => {\n transcript.refresh();\n mounted.refresh();\n };\n const touch = () => options.freezer?.touch();\n const untouch = [state.subscribe(touch), mounted.views.subscribe(touch)];\n const pages = options.pages ?? createPages();\n const deliveries = trackDeliveries(agent.harness, state, context);\n const policy = await attachApprovalPolicy(agent.harness, context);\n const approvals = options.approvals ?? (await approving(agent, policy, options, context));\n const monitor = createMonitor({\n ...options.monitor,\n harness: agent.harness,\n agents: agent.agents,\n views: mounted.views,\n context,\n });\n const guests = await guestsOf(agent, approvals, policy, context);\n const control: AgentControl = {\n ...createAgentControl(\n agent.harness,\n agent,\n deliveries,\n options.licks,\n options.scoops,\n options.skills,\n options.sprinkles,\n options.memory,\n options.freezer,\n options.changes,\n monitor,\n options.secrets?.list,\n options.uploads,\n options.labels,\n guests\n ),\n approvalPolicySet: (change, using) => policy.set(change, using),\n };\n const remote = createRemote({\n harness: agent.harness,\n send: (request, using) => control.send(request, using),\n vault: options.secrets?.vault ?? idleVault,\n async conversation(agentId, using) {\n if (!agentId) return agent.cone();\n const found = await agent.agents.conversation(agentId, using);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n },\n });\n const shownViews = () => [state.value, ...Object.values(mounted.views.value)];\n const kept = options.labels?.labels ?? replicatedState<Record<string, string>>({});\n const labels = visibleLabels(kept, [state, mounted.views], shownViews);\n const transcriptService = {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n commands: options.skills?.commands ?? replicatedState<Command[]>([]),\n privateRevision: options.skills?.privateRevision ?? noRevision(),\n sprinkles: options.sprinkles?.sprinkles ?? replicatedState<Sprinkle[]>([]),\n memories: options.memory?.memories ?? replicatedState<MemoryEntry[]>([]),\n memoryScopes: options.memory?.scopes ?? replicatedState<MemoryScope[]>([]),\n frozen: options.freezer?.frozen ?? replicatedState<FrozenCone[]>([]),\n importing: options.freezer?.importing ?? replicatedState<ImportProgress>(IDLE),\n changes:\n options.changes?.view ?? replicatedState<ChangesView>({ unavailable: null, changes: [] }),\n monitor: monitor.state,\n vault: options.secrets?.vault ?? idleVault,\n labels: labels.state,\n guests: guests.seats,\n approvals: approvals.state,\n approvalPolicy: policy.state,\n };\n const sudo = createSudo({\n approvals,\n agents: () => agent.agents.state(),\n ...(options.licks ? { licks: options.licks.licks } : {}),\n });\n const settings = options.settings ?? noSettings;\n const gates = gatedControls(control, slims.shown, shownViews, transcriptService.labels);\n const { trusted, viewed } = gates;\n const host = serveAgent({\n trusted,\n viewed,\n transcript: transcriptService,\n viewMonitor: monitor.viewState,\n sudo,\n settings,\n remote,\n pages,\n guest: guestOpener(guests, agent.agents, mounted.views, context),\n decide: (by) => (id, decision, reason, using) =>\n approvals.decide(id, decision, reason, by, using),\n });\n const offGuests = guests.onEnd((id) => {\n void host.invalidatePrincipal(\n (principal) => principal.seat === 'guest' && principal.guest.id === id\n );\n });\n let links = 0;\n return {\n serverId,\n agent,\n pages,\n guests,\n approvals,\n ...(options.users ? { users: options.users } : {}),\n connect(endpoint, connectOptions) {\n if (connectOptions?.guest) {\n links += 1;\n admitGuest(guests, endpoint, connectOptions.guest, serverId, links, host);\n return;\n }\n const seat: Exclude<Seat, 'guest'> = connectOptions?.readOnly\n ? 'view'\n : connectOptions?.pages === false\n ? 'control'\n : 'owner';\n links += 1;\n const link = new PortLink(endpoint);\n link.greet(serverId);\n const device = connectOptions?.device ? { device: connectOptions.device } : {};\n host.accept(link, {\n principal: { seat, link: links, ...device } satisfies AgentPrincipal,\n });\n },\n async close() {\n offGuests();\n await host.close();\n guests.dispose();\n await pages.dispose();\n await monitor.dispose();\n await remote.dispose();\n deliveries.dispose();\n sudo.close();\n approvals.close();\n transcript.dispose();\n mounted.dispose();\n labels.dispose();\n slims.dispose();\n for (const off of untouch) off();\n options.freezer?.close();\n options.secrets?.close?.();\n await options.changes?.close(context);\n await options.memory?.close(context);\n await options.sprinkles?.close(context);\n await options.skills?.close(context);\n await options.mcp?.close(context);\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n await options.users?.close();\n },\n };\n}\n\ninterface Served {\n trusted: AgentControl;\n viewed: AgentControl;\n transcript: Record<keyof AgentTranscript, ReplicatedState<unknown>>;\n viewMonitor: ReplicatedState<unknown>;\n sudo?: AgentSudo;\n settings: AgentSettings;\n remote: AgentRemote;\n pages: Pages;\n guest?: (principal: GuestPrincipal) => { value: AgentGuest; dispose(): void } | undefined;\n decide: (by: Decider) => AgentControl['decide'];\n}\n\nexport const CLOSED_TO_GUESTS = 'a guest seat has only its transcript and propose';\n\nconst opened = new WeakSet<object>();\n\nexport const openToGuests = <F extends object>(factory: F): F => {\n opened.add(factory);\n return factory;\n};\n\nexport function closedToGuests(host: Host<AgentPrincipal>): Host<AgentPrincipal> {\n const serve = ((type: never, factory: (...args: unknown[]) => unknown, options?: never) =>\n host.serve(\n type,\n (opened.has(factory)\n ? factory\n : (input: unknown, principal: AgentPrincipal, self: unknown) => {\n if (principal.seat === 'guest') throw forbidden(CLOSED_TO_GUESTS);\n return factory(input, principal, self);\n }) as never,\n options\n )) as Host<AgentPrincipal>['serve'];\n return new Proxy(host, {\n get(target, key) {\n if (key === 'serve') return serve;\n const value = Reflect.get(target, key, target) as unknown;\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\nexport function lazily<T>(state: () => ReplicatedState<T>): Readable<T> {\n return {\n get value() {\n return readable(state()).value;\n },\n subscribe: (listener) => readable(state()).subscribe(listener),\n events: (listener) => readable(state()).events(listener),\n };\n}\n\nconst seated = (serving: Served, principal: AgentPrincipal): AgentControl => ({\n ...(principal.seat === 'view' ? serving.viewed : serving.trusted),\n decide: serving.decide({\n by: principal.seat === 'owner' ? 'owner' : 'follower',\n ...('device' in principal && principal.device ? { device: principal.device } : {}),\n }),\n});\n\nconst VIEW_METHODS = [...CONTROL_READS, ...OWNER_DEVICE_METHODS];\n\nexport function serveAgent(serving: Served): Host<AgentPrincipal> {\n const host = closedToGuests(\n createHost<AgentPrincipal>({ name: AGENT_HOST, self: SELF, principalKey })\n );\n const viewing = { ...serving.transcript, monitor: serving.viewMonitor };\n host.serve(AgentControl, (_input, principal) => {\n const control = seated(serving, principal);\n return principal.seat === 'view'\n ? { variant: 'view', ...served(() => control, VIEW_METHODS) }\n : { variant: 'control', ...served(() => control, CONTROL_METHODS) };\n });\n host.serve(AgentTranscript, (_input, principal) => ({\n variant: 'transcript',\n ...readablesOf(principal.seat === 'view' ? viewing : serving.transcript),\n }));\n host.serve(AgentSettings, (_input, principal) =>\n principal.seat === 'view'\n ? { variant: 'view', state: readable(serving.settings.state) }\n : {\n variant: 'settings',\n state: readable(serving.settings.state),\n ...served(() => serving.settings, SETTINGS_METHODS),\n }\n );\n host.serve(AgentRemoteService, (_input, principal) => {\n if (principal.seat !== 'view')\n return { variant: 'remote', ...served(() => serving.remote, REMOTE_METHODS) };\n const scoped = viewRemote(serving.remote);\n return { variant: 'watch', ...served(() => scoped, REMOTE_READS) };\n });\n host.serve(AgentPagesService, (_input, principal, self) => {\n if (principal.seat !== 'owner') return { variant: 'none' };\n const mine = self.resource('mine', {\n version: 1,\n open: () => ownedPages(serving.pages),\n close: (owned) => owned.release(),\n });\n return { variant: 'pages', ...served(() => mine.value, PAGE_METHODS) };\n });\n host.serve(\n AgentGuestService,\n openToGuests((_input: unknown, principal: AgentPrincipal, self: Self<AgentPrincipal>) => {\n if (principal.seat !== 'guest') return { variant: 'none' };\n const opening = serving.guest?.(principal.guest);\n if (!opening) throw forbidden(CLOSED_TO_GUESTS);\n const seat = self.resource('seat', {\n version: 1,\n open: () => opening,\n close: (made) => made.dispose(),\n });\n const later = (name: 'transcript' | 'seat' | 'proposals') =>\n lazily(() => seat.value.value[name] as ReplicatedState<unknown>);\n return {\n variant: 'guest',\n transcript: later('transcript'),\n seat: later('seat'),\n proposals: later('proposals'),\n ...served(() => seat.value.value, GUEST_METHODS),\n };\n })\n );\n host.serve(AgentSudoService, (_input, principal, self) => {\n const sudo = serving.sudo;\n if (principal.seat !== 'owner' || !sudo) return { variant: 'none' };\n const mine = self.resource('mine', {\n version: 1,\n open: () => ownedSudo(sudo),\n close: (owned) => owned.release(),\n });\n return {\n variant: 'sudo',\n approvals: readable(sudo.approvals),\n ...served(() => mine.value, SUDO_METHODS),\n };\n });\n return host;\n}\n"],
4
+ "sourcesContent": ["import {\n type Context,\n createHost,\n forbidden,\n type Host,\n type Readable,\n type ReplicatedState,\n replicatedState,\n type Self,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agent } from './agent.ts';\nimport {\n type ApprovalJudge,\n type Approvals,\n attachApprovals,\n type Decider,\n} from './approvals/index.ts';\nimport { type ApprovalPolicies, alwaysFor, attachApprovalPolicy } from './approvals/policy.ts';\nimport { type AgentSudo, createSudo, ownedSudo } from './approvals/sudo.ts';\nimport type { ChangesView } from './changes/git.ts';\nimport type { ChangesRuntime } from './changes/index.ts';\nimport { createAgentControl, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport type { FreezerRuntime, FrozenCone } from './freezer/index.ts';\nimport { IDLE, type ImportProgress } from './freezer/v6.ts';\nimport { createGuests, type Guests } from './guests/index.ts';\nimport type { ScoopReviewHost } from './guests/scoop.ts';\nimport { admitGuest, guestOpener } from './guests/serve.ts';\nimport type { UserClients } from './kernel/users.ts';\nimport type { McpRuntime } from './mcp/index.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport { createMonitor, type MonitorOptions } from './monitor/index.ts';\nimport { viewRemote } from './readonly.ts';\nimport { type AgentRemote, createRemote } from './remote/host.ts';\nimport { createPages, ownedPages, type Pages } from './remote/pages.ts';\nimport type { SecretListing } from './secrets/names.ts';\nimport type { VaultState } from './secrets/vault.ts';\nimport {\n AgentControl,\n type AgentGuest,\n AgentGuestService,\n AgentPagesService,\n AgentRemoteService,\n AgentSettings,\n AgentSudoService,\n AgentTranscript,\n CONTROL_METHODS,\n CONTROL_READS,\n type Command,\n GUEST_METHODS,\n OWNER_DEVICE_METHODS,\n PAGE_METHODS,\n type PrivateRevision,\n REMOTE_METHODS,\n REMOTE_READS,\n SETTINGS_METHODS,\n SUDO_METHODS,\n} from './services.ts';\nimport {\n AGENT_HOST,\n type AgentPrincipal,\n type GuestPrincipal,\n principalKey,\n readable,\n readablesOf,\n SELF,\n type Seat,\n served,\n} from './serving.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { slimmer, type Thumbnail, thumbnail } from './thumbs.ts';\nimport { type LabelsRuntime, visibleLabels, watchLabel } from './titles/labels.ts';\nimport type { Uploads } from './uploads.ts';\nimport { agentViews } from './views.ts';\nimport { type PortEndpoint, PortLink } from './wire.ts';\n\nexport interface ConnectOptions {\n readOnly?: boolean;\n pages?: boolean;\n guest?: { id: string; label: string };\n device?: string;\n}\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n readonly users?: UserClients;\n readonly pages: Pages;\n readonly guests: Guests;\n readonly approvals: Approvals;\n connect(endpoint: PortEndpoint, options?: ConnectOptions): void;\n close(): Promise<void>;\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n remove: async () => {},\n serverUrl: async () => null,\n signIn: async () => null,\n dismissMoved: async () => {},\n};\n\nasync function follow(\n agent: Agent,\n context: Context,\n slim: (view: ConversationView) => ConversationView\n) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(slim(attached.value));\n const mirror = (value: ConversationView, from: Context) => state.replace(from, slim(value));\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, slim(next.value));\n off = next.subscribe(mirror);\n });\n return {\n state,\n refresh: () => state.replace(context, slim(attached.value)),\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport interface HostSecrets {\n vault: ReplicatedState<VaultState>;\n list(): SecretListing;\n close?(): void;\n}\n\nconst idleVault = replicatedState<VaultState>({\n port: 'none',\n needed: false,\n version: 0,\n locked: 'no',\n held: 0,\n});\n\nfunction gatedControls(\n control: AgentControl,\n shown: (entry: number) => boolean,\n views: () => readonly ConversationView[],\n labels: ReplicatedState<Record<string, string>>\n) {\n const visible = (entry: number) =>\n views().some((view) => view.entries.some((item) => item.id === entry));\n const gated =\n (allowed: (entry: number) => boolean): AgentControl['image'] =>\n async (ref, using) =>\n allowed(ref.entry) ? control.image(ref, using) : { data: null, mimeType: null };\n const trusted: AgentControl = { ...control, image: gated(shown) };\n const viewed: AgentControl = { ...control, image: gated(visible), toolLabel: watchLabel(labels) };\n return { trusted, viewed };\n}\n\nasync function approving(\n agent: Agent,\n policy: ApprovalPolicies,\n options: { judge?: ApprovalJudge; intent?: ScoopReviewHost['intent']; approvalMs?: number },\n context: Context\n): Promise<Approvals> {\n const approvals = await attachApprovals(\n {\n harness: agent.harness,\n guestAlways: alwaysFor(policy),\n sudoApprover: () => policy.current().sudo.approver,\n scoops: agent.scoopReviews,\n ...(options.judge ? { judge: options.judge } : {}),\n ...(options.approvalMs ? { ttlMs: options.approvalMs } : {}),\n },\n context\n );\n agent.scoopReviews.bind({\n approvals,\n agents: agent.agents,\n harness: agent.harness,\n ...(options.intent ? { intent: options.intent } : {}),\n });\n return approvals;\n}\n\nasync function guestsOf(\n agent: Agent,\n approvals: Approvals,\n policy: ApprovalPolicies,\n context: Context\n): Promise<Guests> {\n const guests = await createGuests({\n harness: agent.harness,\n agents: agent.agents,\n approvals,\n policy,\n reviews: agent.reviews,\n context,\n });\n agent.gate.bind({ approvals, policy: () => guests.policy() });\n return guests;\n}\n\nconst noRevision = () => replicatedState<PrivateRevision>({ revision: 0 });\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n skills?: SkillsRuntime;\n sprinkles?: SprinklesRuntime;\n memory?: MemoryRuntime;\n freezer?: FreezerRuntime;\n changes?: ChangesRuntime;\n mcp?: Pick<McpRuntime, 'close'>;\n users?: UserClients;\n monitor?: Pick<MonitorOptions, 'kernel' | 'groups' | 'users'>;\n pages?: Pages;\n secrets?: HostSecrets;\n uploads?: Uploads;\n thumbnail?: Thumbnail;\n labels?: LabelsRuntime;\n approvals?: Approvals;\n judge?: ApprovalJudge;\n intent?: ScoopReviewHost['intent'];\n approvalMs?: number;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n let refresh = () => {};\n const slims = slimmer(options.thumbnail ?? thumbnail, () => refresh());\n const transcript = await follow(agent, context, slims.slim);\n const { state } = transcript;\n const mounted = await agentViews(agent, context, slims.slim);\n refresh = () => {\n transcript.refresh();\n mounted.refresh();\n };\n const touch = () => options.freezer?.touch();\n const untouch = [state.subscribe(touch), mounted.views.subscribe(touch)];\n const pages = options.pages ?? createPages();\n const deliveries = trackDeliveries(agent.harness, state, context);\n const policy = await attachApprovalPolicy(agent.harness, context);\n const approvals = options.approvals ?? (await approving(agent, policy, options, context));\n const monitor = createMonitor({\n ...options.monitor,\n harness: agent.harness,\n agents: agent.agents,\n views: mounted.views,\n context,\n });\n const guests = await guestsOf(agent, approvals, policy, context);\n const control: AgentControl = {\n ...createAgentControl(\n agent.harness,\n agent,\n deliveries,\n options.licks,\n options.scoops,\n options.skills,\n options.sprinkles,\n options.memory,\n options.freezer,\n options.changes,\n monitor,\n options.secrets?.list,\n options.uploads,\n options.labels,\n guests\n ),\n approvalPolicySet: (change, using) => policy.set(change, using),\n };\n const remote = createRemote({\n harness: agent.harness,\n send: (request, using) => control.send(request, using),\n vault: options.secrets?.vault ?? idleVault,\n async conversation(agentId, using) {\n if (!agentId) return agent.cone();\n const found = await agent.agents.conversation(agentId, using);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n },\n });\n const shownViews = () => [state.value, ...Object.values(mounted.views.value)];\n const kept = options.labels?.labels ?? replicatedState<Record<string, string>>({});\n const labels = visibleLabels(kept, [state, mounted.views], shownViews);\n const transcriptService = {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n commands: options.skills?.commands ?? replicatedState<Command[]>([]),\n privateRevision: options.skills?.privateRevision ?? noRevision(),\n sprinkles: options.sprinkles?.sprinkles ?? replicatedState<Sprinkle[]>([]),\n memories: options.memory?.memories ?? replicatedState<MemoryEntry[]>([]),\n memoryScopes: options.memory?.scopes ?? replicatedState<MemoryScope[]>([]),\n frozen: options.freezer?.frozen ?? replicatedState<FrozenCone[]>([]),\n importing: options.freezer?.importing ?? replicatedState<ImportProgress>(IDLE),\n changes:\n options.changes?.view ?? replicatedState<ChangesView>({ unavailable: null, changes: [] }),\n monitor: monitor.state,\n vault: options.secrets?.vault ?? idleVault,\n labels: labels.state,\n guests: guests.seats,\n approvals: approvals.state,\n approvalPolicy: policy.state,\n };\n const sudo = createSudo({\n approvals,\n agents: () => agent.agents.state(),\n ...(options.licks ? { licks: options.licks.licks } : {}),\n });\n const settings = options.settings ?? noSettings;\n const gates = gatedControls(control, slims.shown, shownViews, transcriptService.labels);\n const { trusted, viewed } = gates;\n const host = serveAgent({\n trusted,\n viewed,\n transcript: transcriptService,\n viewMonitor: monitor.viewState,\n sudo,\n settings,\n remote,\n pages,\n guest: guestOpener(guests, agent.agents, mounted.views, context),\n decide: (by) => (id, decision, reason, using) =>\n approvals.decide(id, decision, reason, by, using),\n });\n const offGuests = guests.onEnd((id) => {\n void host.invalidatePrincipal(\n (principal) => principal.seat === 'guest' && principal.guest.id === id\n );\n });\n let links = 0;\n return {\n serverId,\n agent,\n pages,\n guests,\n approvals,\n ...(options.users ? { users: options.users } : {}),\n connect(endpoint, connectOptions) {\n if (connectOptions?.guest) {\n links += 1;\n admitGuest(guests, endpoint, connectOptions.guest, serverId, links, host);\n return;\n }\n const seat: Exclude<Seat, 'guest'> = connectOptions?.readOnly\n ? 'view'\n : connectOptions?.pages === false\n ? 'control'\n : 'owner';\n links += 1;\n const link = new PortLink(endpoint);\n link.greet(serverId);\n const device = connectOptions?.device ? { device: connectOptions.device } : {};\n host.accept(link, {\n principal: { seat, link: links, ...device } satisfies AgentPrincipal,\n });\n },\n async close() {\n offGuests();\n await host.close();\n guests.dispose();\n await pages.dispose();\n await monitor.dispose();\n await remote.dispose();\n deliveries.dispose();\n sudo.close();\n approvals.close();\n transcript.dispose();\n mounted.dispose();\n labels.dispose();\n slims.dispose();\n for (const off of untouch) off();\n options.freezer?.close();\n options.secrets?.close?.();\n await options.changes?.close(context);\n await options.memory?.close(context);\n await options.sprinkles?.close(context);\n await options.skills?.close(context);\n await options.mcp?.close(context);\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n await options.users?.close();\n },\n };\n}\n\ninterface Served {\n trusted: AgentControl;\n viewed: AgentControl;\n transcript: Record<keyof AgentTranscript, ReplicatedState<unknown>>;\n viewMonitor: ReplicatedState<unknown>;\n sudo?: AgentSudo;\n settings: AgentSettings;\n remote: AgentRemote;\n pages: Pages;\n guest?: (principal: GuestPrincipal) => { value: AgentGuest; dispose(): void } | undefined;\n decide: (by: Decider) => AgentControl['decide'];\n}\n\nexport const CLOSED_TO_GUESTS = 'a guest seat has only its transcript and propose';\n\nconst opened = new WeakSet<object>();\n\nexport const openToGuests = <F extends object>(factory: F): F => {\n opened.add(factory);\n return factory;\n};\n\nexport function closedToGuests(host: Host<AgentPrincipal>): Host<AgentPrincipal> {\n const serve = ((type: never, factory: (...args: unknown[]) => unknown, options?: never) =>\n host.serve(\n type,\n (opened.has(factory)\n ? factory\n : (input: unknown, principal: AgentPrincipal, self: unknown) => {\n if (principal.seat === 'guest') throw forbidden(CLOSED_TO_GUESTS);\n return factory(input, principal, self);\n }) as never,\n options\n )) as Host<AgentPrincipal>['serve'];\n return new Proxy(host, {\n get(target, key) {\n if (key === 'serve') return serve;\n const value = Reflect.get(target, key, target) as unknown;\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n}\n\nexport function lazily<T>(state: () => ReplicatedState<T>): Readable<T> {\n return {\n get value() {\n return readable(state()).value;\n },\n subscribe: (listener) => readable(state()).subscribe(listener),\n events: (listener) => readable(state()).events(listener),\n };\n}\n\nconst seated = (serving: Served, principal: AgentPrincipal): AgentControl => ({\n ...(principal.seat === 'view' ? serving.viewed : serving.trusted),\n decide: serving.decide({\n by: principal.seat === 'owner' ? 'owner' : 'follower',\n ...('device' in principal && principal.device ? { device: principal.device } : {}),\n }),\n});\n\nconst VIEW_METHODS = [...CONTROL_READS, ...OWNER_DEVICE_METHODS];\n\nexport function serveAgent(serving: Served): Host<AgentPrincipal> {\n const host = closedToGuests(\n createHost<AgentPrincipal>({ name: AGENT_HOST, self: SELF, principalKey })\n );\n const viewing = { ...serving.transcript, monitor: serving.viewMonitor };\n host.serve(AgentControl, (_input, principal) => {\n const control = seated(serving, principal);\n return principal.seat === 'view'\n ? { variant: 'view', ...served(() => control, VIEW_METHODS) }\n : { variant: 'control', ...served(() => control, CONTROL_METHODS) };\n });\n host.serve(AgentTranscript, (_input, principal) => ({\n variant: 'transcript',\n ...readablesOf(principal.seat === 'view' ? viewing : serving.transcript),\n }));\n host.serve(AgentSettings, (_input, principal) =>\n principal.seat === 'view'\n ? { variant: 'view', state: readable(serving.settings.state) }\n : {\n variant: 'settings',\n state: readable(serving.settings.state),\n ...served(() => serving.settings, SETTINGS_METHODS),\n }\n );\n host.serve(AgentRemoteService, (_input, principal) => {\n if (principal.seat !== 'view')\n return { variant: 'remote', ...served(() => serving.remote, REMOTE_METHODS) };\n const scoped = viewRemote(serving.remote);\n return { variant: 'watch', ...served(() => scoped, REMOTE_READS) };\n });\n host.serve(AgentPagesService, (_input, principal, self) => {\n if (principal.seat !== 'owner') return { variant: 'none' };\n const mine = self.resource('mine', {\n version: 1,\n open: () => ownedPages(serving.pages),\n close: (owned) => owned.release(),\n });\n return { variant: 'pages', ...served(() => mine.value, PAGE_METHODS) };\n });\n host.serve(\n AgentGuestService,\n openToGuests((_input: unknown, principal: AgentPrincipal, self: Self<AgentPrincipal>) => {\n if (principal.seat !== 'guest') return { variant: 'none' };\n const opening = serving.guest?.(principal.guest);\n if (!opening) throw forbidden(CLOSED_TO_GUESTS);\n const seat = self.resource('seat', {\n version: 1,\n open: () => opening,\n close: (made) => made.dispose(),\n });\n const later = (name: 'transcript' | 'seat' | 'proposals') =>\n lazily(() => seat.value.value[name] as ReplicatedState<unknown>);\n return {\n variant: 'guest',\n transcript: later('transcript'),\n seat: later('seat'),\n proposals: later('proposals'),\n ...served(() => seat.value.value, GUEST_METHODS),\n };\n })\n );\n host.serve(AgentSudoService, (_input, principal, self) => {\n const sudo = serving.sudo;\n if (principal.seat !== 'owner' || !sudo) return { variant: 'none' };\n const mine = self.resource('mine', {\n version: 1,\n open: () => ownedSudo(sudo),\n close: (owned) => owned.release(),\n });\n return {\n variant: 'sudo',\n approvals: readable(sudo.approvals),\n ...served(() => mine.value, SUDO_METHODS),\n };\n });\n return host;\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/providers.js CHANGED
@@ -302,7 +302,7 @@ const KEY_PROVIDERS = [
302
302
  env: "XAI_API_KEY",
303
303
  description: "Grok models from xAI.",
304
304
  keyUrl: "https://console.x.ai",
305
- cors: false
305
+ cors: true
306
306
  },
307
307
  {
308
308
  id: "meta",
@@ -310,7 +310,7 @@ const KEY_PROVIDERS = [
310
310
  env: "META_API_KEY",
311
311
  description: "Meta\u2019s Muse models.",
312
312
  keyUrl: "https://developer.meta.ai",
313
- cors: false
313
+ cors: true
314
314
  },
315
315
  {
316
316
  id: "vercel-ai-gateway",
@@ -435,7 +435,7 @@ const LISTINGS = new Map([
435
435
  description: "Claude and other models on AWS, with a Bedrock API key.",
436
436
  keyUrl: "https://console.aws.amazon.com/bedrock/home#/api-keys",
437
437
  auth: "fields",
438
- corsFree: true,
438
+ corsFree: false,
439
439
  vaultName: "AWS_BEARER_TOKEN_BEDROCK",
440
440
  fields: [REGION_FIELD]
441
441
  }
@@ -445,7 +445,7 @@ const LISTINGS = new Map([
445
445
  {
446
446
  description: "Models through Adobe\u2019s LLM proxy, with your Adobe ID.",
447
447
  auth: "oauth",
448
- corsFree: true,
448
+ corsFree: false,
449
449
  vaultName: "ADOBE_IMS_TOKEN"
450
450
  }
451
451
  ],
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/providers.ts"],
4
- "sourcesContent": ["import type { Provider } from '@earendil-works/pi-ai';\nimport { antLingProvider } from '@earendil-works/pi-ai/providers/ant-ling';\nimport { anthropicProvider } from '@earendil-works/pi-ai/providers/anthropic';\nimport { azureProvider } from '@earendil-works/pi-ai/providers/azure';\nimport { basetenProvider } from '@earendil-works/pi-ai/providers/baseten';\nimport { cerebrasProvider } from '@earendil-works/pi-ai/providers/cerebras';\nimport { cloudflareAIGatewayProvider } from '@earendil-works/pi-ai/providers/cloudflare-ai-gateway';\nimport { cloudflareWorkersAIProvider } from '@earendil-works/pi-ai/providers/cloudflare-workers-ai';\nimport { deepseekProvider } from '@earendil-works/pi-ai/providers/deepseek';\nimport { fireworksProvider } from '@earendil-works/pi-ai/providers/fireworks';\nimport { groqProvider } from '@earendil-works/pi-ai/providers/groq';\nimport { huggingfaceProvider } from '@earendil-works/pi-ai/providers/huggingface';\nimport { kimiCodingProvider } from '@earendil-works/pi-ai/providers/kimi-coding';\nimport { metaProvider } from '@earendil-works/pi-ai/providers/meta';\nimport { minimaxProvider } from '@earendil-works/pi-ai/providers/minimax';\nimport { minimaxCnProvider } from '@earendil-works/pi-ai/providers/minimax-cn';\nimport { mistralProvider } from '@earendil-works/pi-ai/providers/mistral';\nimport { moonshotaiProvider } from '@earendil-works/pi-ai/providers/moonshotai';\nimport { moonshotaiCnProvider } from '@earendil-works/pi-ai/providers/moonshotai-cn';\nimport { nvidiaProvider } from '@earendil-works/pi-ai/providers/nvidia';\nimport { openaiProvider } from '@earendil-works/pi-ai/providers/openai';\nimport { opencodeProvider } from '@earendil-works/pi-ai/providers/opencode';\nimport { opencodeGoProvider } from '@earendil-works/pi-ai/providers/opencode-go';\nimport { openrouterProvider } from '@earendil-works/pi-ai/providers/openrouter';\nimport { qwenTokenPlanProvider } from '@earendil-works/pi-ai/providers/qwen-token-plan';\nimport { qwenTokenPlanCnProvider } from '@earendil-works/pi-ai/providers/qwen-token-plan-cn';\nimport { qwenTokenPlanIndividualProvider } from '@earendil-works/pi-ai/providers/qwen-token-plan-individual';\nimport { togetherProvider } from '@earendil-works/pi-ai/providers/together';\nimport { vercelAIGatewayProvider } from '@earendil-works/pi-ai/providers/vercel-ai-gateway';\nimport { xaiProvider } from '@earendil-works/pi-ai/providers/xai';\nimport { xiaomiProvider } from '@earendil-works/pi-ai/providers/xiaomi';\nimport { xiaomiTokenPlanAmsProvider } from '@earendil-works/pi-ai/providers/xiaomi-token-plan-ams';\nimport { xiaomiTokenPlanCnProvider } from '@earendil-works/pi-ai/providers/xiaomi-token-plan-cn';\nimport { xiaomiTokenPlanSgpProvider } from '@earendil-works/pi-ai/providers/xiaomi-token-plan-sgp';\nimport { zaiProvider } from '@earendil-works/pi-ai/providers/zai';\nimport { zaiCodingCnProvider } from '@earendil-works/pi-ai/providers/zai-coding-cn';\n\nexport interface FieldOption {\n value: string;\n label: string;\n}\n\nexport interface FieldInput {\n name: string;\n label: string;\n required?: boolean;\n placeholder?: string;\n help?: string;\n customBase?: true;\n options?: readonly FieldOption[];\n value?: string;\n}\n\nexport interface FieldSpec extends FieldInput {\n env(value: string): Record<string, string> | string;\n}\n\nexport interface KeyProvider {\n id: string;\n make: () => Provider;\n env: string;\n description: string;\n keyUrl: string;\n cors: boolean;\n fields?: readonly FieldSpec[];\n}\n\nconst RESOURCE = /^[a-z0-9][a-z0-9-]{0,62}$/i;\nconst DEPLOYMENTS = /^[^=,\\s]+\\s*=\\s*[^=,\\s]+(\\s*,\\s*[^=,\\s]+\\s*=\\s*[^=,\\s]+)*$/;\n\nexport function httpsBase(value: string): URL | undefined {\n const url = URL.parse(value);\n if (url?.protocol !== 'https:' || url.username || url.password) return undefined;\n return url.port === '' || url.port === '443' ? url : undefined;\n}\n\nfunction azureEndpoint(value: string): Record<string, string> | string {\n if (RESOURCE.test(value)) return { AZURE_OPENAI_RESOURCE_NAME: value };\n const url = httpsBase(value);\n if (!url)\n return 'Enter your Azure resource name, or its endpoint as an https:// address without a port or a user name.';\n return { AZURE_OPENAI_BASE_URL: url.href.replace(/\\/+$/, '') };\n}\n\nexport const AZURE = 'azure';\n\nexport function azureBase(env: Readonly<Record<string, string>>): string | undefined {\n if (env.AZURE_OPENAI_BASE_URL) return env.AZURE_OPENAI_BASE_URL;\n const name = env.AZURE_OPENAI_RESOURCE_NAME;\n return name ? `https://${name}.openai.azure.com/openai/v1` : undefined;\n}\n\nconst REGION = /^[a-z]{2,}(-[a-z]+)+-\\d{1,2}$/;\nconst ACCOUNT = /^[0-9a-f]{32}$/i;\nconst GATEWAY = /^[A-Za-z0-9_-]{1,64}$/;\n\nconst checked =\n (pattern: RegExp, env: string, problem: string) =>\n (value: string): Record<string, string> | string =>\n pattern.test(value) ? { [env]: value } : problem;\n\nconst BEDROCK_REGIONS: readonly (readonly [string, string])[] = [\n ['ap-northeast-1', 'Asia Pacific (Tokyo)'],\n ['ap-northeast-2', 'Asia Pacific (Seoul)'],\n ['ap-northeast-3', 'Asia Pacific (Osaka)'],\n ['ap-south-1', 'Asia Pacific (Mumbai)'],\n ['ap-south-2', 'Asia Pacific (Hyderabad)'],\n ['ap-southeast-1', 'Asia Pacific (Singapore)'],\n ['ap-southeast-2', 'Asia Pacific (Sydney)'],\n ['ca-central-1', 'Canada (Central)'],\n ['eu-central-1', 'Europe (Frankfurt)'],\n ['eu-central-2', 'Europe (Zurich)'],\n ['eu-north-1', 'Europe (Stockholm)'],\n ['eu-south-1', 'Europe (Milan)'],\n ['eu-south-2', 'Europe (Spain)'],\n ['eu-west-1', 'Europe (Ireland)'],\n ['eu-west-2', 'Europe (London)'],\n ['eu-west-3', 'Europe (Paris)'],\n ['sa-east-1', 'South America (S\u00E3o Paulo)'],\n ['us-east-1', 'US East (N. Virginia)'],\n ['us-gov-east-1', 'AWS GovCloud (US-East)'],\n ['us-gov-west-1', 'AWS GovCloud (US-West)'],\n ['us-west-2', 'US West (Oregon)'],\n];\n\nexport const DEFAULT_REGION = 'us-west-2';\n\nexport const REGION_FIELD: FieldSpec = {\n name: 'region',\n label: 'Region',\n help: 'The AWS region your Bedrock models run in.',\n options: BEDROCK_REGIONS.map(([value, label]) => ({ value, label: `${label}, ${value}` })),\n value: DEFAULT_REGION,\n env: checked(REGION, 'AWS_REGION', 'Enter an AWS region such as us-west-2.'),\n};\n\nconst ACCOUNT_FIELD: FieldSpec = {\n name: 'account',\n label: 'Account ID',\n required: true,\n placeholder: '0123456789abcdef0123456789abcdef',\n help: 'The 32-character ID on your Cloudflare dashboard\u2019s account overview.',\n env: checked(\n ACCOUNT,\n 'CLOUDFLARE_ACCOUNT_ID',\n 'A Cloudflare account ID is 32 characters of 0\u20139 and a\u2013f.'\n ),\n};\n\nexport const KEY_PROVIDERS: readonly KeyProvider[] = [\n {\n id: 'azure',\n make: azureProvider,\n env: 'AZURE_OPENAI_API_KEY',\n description: 'OpenAI models deployed in your own Azure OpenAI or AI Foundry resource.',\n keyUrl: 'https://portal.azure.com',\n cors: true,\n fields: [\n {\n name: 'endpoint',\n label: 'Endpoint',\n required: true,\n placeholder: 'my-resource or https://my-resource.openai.azure.com',\n help: 'Your resource\u2019s name, or its endpoint address from Keys and Endpoint.',\n customBase: true,\n env: azureEndpoint,\n },\n {\n name: 'deployments',\n label: 'Deployments',\n placeholder: 'gpt-5.4=my-gpt',\n help: 'Only for deployments named differently from their model, as model=deployment, separated by commas.',\n env: (value) =>\n DEPLOYMENTS.test(value)\n ? { AZURE_OPENAI_DEPLOYMENT_NAME_MAP: value }\n : 'Write each deployment as model=deployment, separated by commas.',\n },\n ],\n },\n {\n id: 'cloudflare-workers-ai',\n make: cloudflareWorkersAIProvider,\n env: 'CLOUDFLARE_API_KEY',\n description: 'Open models on Cloudflare Workers AI, in your Cloudflare account.',\n keyUrl: 'https://dash.cloudflare.com/profile/api-tokens',\n cors: false,\n fields: [ACCOUNT_FIELD],\n },\n {\n id: 'cloudflare-ai-gateway',\n make: cloudflareAIGatewayProvider,\n env: 'CLOUDFLARE_API_KEY',\n description: 'Models from many labs through your Cloudflare AI Gateway.',\n keyUrl: 'https://dash.cloudflare.com/profile/api-tokens',\n cors: false,\n fields: [\n ACCOUNT_FIELD,\n {\n name: 'gateway',\n label: 'Gateway ID',\n required: true,\n placeholder: 'my-gateway',\n help: 'The name of your gateway under AI \u203A AI Gateway.',\n env: checked(\n GATEWAY,\n 'CLOUDFLARE_GATEWAY_ID',\n 'A gateway ID is letters, digits, - and _, up to 64 characters.'\n ),\n },\n ],\n },\n {\n id: 'anthropic',\n make: anthropicProvider,\n env: 'ANTHROPIC_API_KEY',\n description: 'Claude models, with an API key from the Claude Console.',\n keyUrl: 'https://console.anthropic.com/settings/keys',\n cors: true,\n },\n {\n id: 'openai',\n make: openaiProvider,\n env: 'OPENAI_API_KEY',\n description: 'GPT models, with an API key from the OpenAI platform.',\n keyUrl: 'https://platform.openai.com/api-keys',\n cors: true,\n },\n {\n id: 'mistral',\n make: mistralProvider,\n env: 'MISTRAL_API_KEY',\n description: 'Mistral and Devstral models from La Plateforme.',\n keyUrl: 'https://console.mistral.ai/api-keys',\n cors: true,\n },\n {\n id: 'groq',\n make: groqProvider,\n env: 'GROQ_API_KEY',\n description: 'Open models on Groq\u2019s fast inference.',\n keyUrl: 'https://console.groq.com/keys',\n cors: true,\n },\n {\n id: 'deepseek',\n make: deepseekProvider,\n env: 'DEEPSEEK_API_KEY',\n description: 'DeepSeek\u2019s own models.',\n keyUrl: 'https://platform.deepseek.com/api_keys',\n cors: true,\n },\n {\n id: 'together',\n make: togetherProvider,\n env: 'TOGETHER_API_KEY',\n description: 'Open models hosted by Together AI.',\n keyUrl: 'https://api.together.ai/settings/api-keys',\n cors: true,\n },\n {\n id: 'huggingface',\n make: huggingfaceProvider,\n env: 'HF_TOKEN',\n description: 'Open models through Hugging Face\u2019s inference router.',\n keyUrl: 'https://huggingface.co/settings/tokens',\n cors: true,\n },\n {\n id: 'xiaomi',\n make: xiaomiProvider,\n env: 'XIAOMI_API_KEY',\n description: 'Xiaomi\u2019s MiMo models.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'xiaomi-token-plan-ams',\n make: xiaomiTokenPlanAmsProvider,\n env: 'XIAOMI_TOKEN_PLAN_AMS_API_KEY',\n description: 'MiMo models on a Xiaomi token plan, served from Amsterdam.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'xiaomi-token-plan-cn',\n make: xiaomiTokenPlanCnProvider,\n env: 'XIAOMI_TOKEN_PLAN_CN_API_KEY',\n description: 'MiMo models on a Xiaomi token plan, served from China.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'xiaomi-token-plan-sgp',\n make: xiaomiTokenPlanSgpProvider,\n env: 'XIAOMI_TOKEN_PLAN_SGP_API_KEY',\n description: 'MiMo models on a Xiaomi token plan, served from Singapore.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'cerebras',\n make: cerebrasProvider,\n env: 'CEREBRAS_API_KEY',\n description: 'Open models on Cerebras inference.',\n keyUrl: 'https://cloud.cerebras.ai',\n cors: false,\n },\n {\n id: 'openrouter',\n make: openrouterProvider,\n env: 'OPENROUTER_API_KEY',\n description: 'Hundreds of models from many labs through one key.',\n keyUrl: 'https://openrouter.ai/settings/keys',\n cors: false,\n },\n {\n id: 'fireworks',\n make: fireworksProvider,\n env: 'FIREWORKS_API_KEY',\n description: 'Open models hosted by Fireworks AI.',\n keyUrl: 'https://app.fireworks.ai/settings/users/api-keys',\n cors: false,\n },\n {\n id: 'baseten',\n make: basetenProvider,\n env: 'BASETEN_API_KEY',\n description: 'Open models hosted by Baseten.',\n keyUrl: 'https://app.baseten.co/settings/api_keys',\n cors: true,\n },\n {\n id: 'moonshotai',\n make: moonshotaiProvider,\n env: 'MOONSHOT_API_KEY',\n description: 'Kimi models from Moonshot AI.',\n keyUrl: 'https://platform.moonshot.ai/console/api-keys',\n cors: true,\n },\n {\n id: 'moonshotai-cn',\n make: moonshotaiCnProvider,\n env: 'MOONSHOT_API_KEY',\n description: 'Kimi models from Moonshot AI\u2019s China platform.',\n keyUrl: 'https://platform.moonshot.cn/console/api-keys',\n cors: true,\n },\n {\n id: 'xai',\n make: xaiProvider,\n env: 'XAI_API_KEY',\n description: 'Grok models from xAI.',\n keyUrl: 'https://console.x.ai',\n cors: false,\n },\n {\n id: 'meta',\n make: metaProvider,\n env: 'META_API_KEY',\n description: 'Meta\u2019s Muse models.',\n keyUrl: 'https://developer.meta.ai',\n cors: false,\n },\n {\n id: 'vercel-ai-gateway',\n make: vercelAIGatewayProvider,\n env: 'AI_GATEWAY_API_KEY',\n description: 'Models from many labs through Vercel\u2019s AI Gateway.',\n keyUrl: 'https://vercel.com/docs/ai-gateway/authentication',\n cors: false,\n },\n {\n id: 'minimax',\n make: minimaxProvider,\n env: 'MINIMAX_API_KEY',\n description: 'MiniMax\u2019s models.',\n keyUrl: 'https://platform.minimax.io/user-center/basic-information/interface-key',\n cors: false,\n },\n {\n id: 'minimax-cn',\n make: minimaxCnProvider,\n env: 'MINIMAX_CN_API_KEY',\n description: 'MiniMax\u2019s models from its China platform.',\n keyUrl: 'https://platform.minimaxi.com/user-center/basic-information/interface-key',\n cors: false,\n },\n {\n id: 'zai',\n make: zaiProvider,\n env: 'ZAI_API_KEY',\n description: 'GLM models on Z.AI\u2019s coding plan.',\n keyUrl: 'https://z.ai/manage-apikey/apikey-list',\n cors: false,\n },\n {\n id: 'zai-coding-cn',\n make: zaiCodingCnProvider,\n env: 'ZAI_CODING_CN_API_KEY',\n description: 'GLM models on Zhipu\u2019s coding plan in China.',\n keyUrl: 'https://open.bigmodel.cn/usercenter/apikeys',\n cors: false,\n },\n {\n id: 'nvidia',\n make: nvidiaProvider,\n env: 'NVIDIA_API_KEY',\n description: 'Open models on NVIDIA\u2019s API catalog.',\n keyUrl: 'https://build.nvidia.com/settings/api-keys',\n cors: false,\n },\n {\n id: 'kimi-coding',\n make: kimiCodingProvider,\n env: 'KIMI_API_KEY',\n description: 'Kimi models on the Kimi Code subscription.',\n keyUrl: 'https://www.kimi.com/code/console',\n cors: false,\n },\n {\n id: 'ant-ling',\n make: antLingProvider,\n env: 'ANT_LING_API_KEY',\n description: 'Ling and Ring models from Ant Group.',\n keyUrl: 'https://www.ant-ling.com',\n cors: false,\n },\n {\n id: 'opencode',\n make: opencodeProvider,\n env: 'OPENCODE_API_KEY',\n description:\n 'Models curated for coding agents by OpenCode Zen. Needs seven\u2019s vault 1.12.2 or later.',\n keyUrl: 'https://opencode.ai/auth',\n cors: false,\n },\n {\n id: 'opencode-go',\n make: opencodeGoProvider,\n env: 'OPENCODE_API_KEY',\n description:\n 'Open models on the OpenCode Go subscription. Needs seven\u2019s vault 1.12.2 or later.',\n keyUrl: 'https://opencode.ai/auth',\n cors: false,\n },\n {\n id: 'qwen-token-plan',\n make: qwenTokenPlanProvider,\n env: 'QWEN_TOKEN_PLAN_API_KEY',\n description: 'Qwen models on an Alibaba Cloud token plan.',\n keyUrl: 'https://modelstudio.console.alibabacloud.com',\n cors: false,\n },\n {\n id: 'qwen-token-plan-cn',\n make: qwenTokenPlanCnProvider,\n env: 'QWEN_TOKEN_PLAN_CN_API_KEY',\n description: 'Qwen models on an Alibaba Cloud token plan in China.',\n keyUrl: 'https://bailian.console.aliyun.com',\n cors: false,\n },\n {\n id: 'qwen-token-plan-individual',\n make: qwenTokenPlanIndividualProvider,\n env: 'QWEN_TOKEN_PLAN_API_KEY',\n description: 'Qwen models on an individual Alibaba Cloud token plan.',\n keyUrl: 'https://modelstudio.console.alibabacloud.com',\n cors: false,\n },\n];\n\nexport type ProviderAuth = 'api-key' | 'oauth' | 'fields' | 'local';\n\nexport interface Listing {\n description: string;\n keyUrl?: string;\n auth: ProviderAuth;\n corsFree: boolean;\n vaultName: string;\n fields?: readonly FieldSpec[];\n}\n\nconst upper = (text: string) => text.toUpperCase().replace(/[^A-Z0-9]/g, '_');\n\nfunction vaultNames(entries: readonly KeyProvider[]): Map<string, string> {\n const uses = Map.groupBy(entries, (entry) => entry.env);\n return new Map(\n entries.map((entry) => [\n entry.id,\n (uses.get(entry.env) as KeyProvider[]).length > 1 ? `${upper(entry.id)}_API_KEY` : entry.env,\n ])\n );\n}\n\nconst named = vaultNames(KEY_PROVIDERS);\n\nexport const LISTINGS: ReadonlyMap<string, Listing> = new Map<string, Listing>([\n [\n 'amazon-bedrock',\n {\n description: 'Claude and other models on AWS, with a Bedrock API key.',\n keyUrl: 'https://console.aws.amazon.com/bedrock/home#/api-keys',\n auth: 'fields',\n corsFree: true,\n vaultName: 'AWS_BEARER_TOKEN_BEDROCK',\n fields: [REGION_FIELD],\n },\n ],\n [\n 'adobe',\n {\n description: 'Models through Adobe\u2019s LLM proxy, with your Adobe ID.',\n auth: 'oauth',\n corsFree: true,\n vaultName: 'ADOBE_IMS_TOKEN',\n },\n ],\n ...KEY_PROVIDERS.map((entry): [string, Listing] => [\n entry.id,\n {\n description: entry.description,\n keyUrl: entry.keyUrl,\n auth: entry.fields ? 'fields' : 'api-key',\n corsFree: !entry.cors,\n vaultName: named.get(entry.id) as string,\n ...(entry.fields ? { fields: entry.fields } : {}),\n },\n ]),\n]);\n\nexport const ADDED_FIRST: readonly string[] = ['amazon-bedrock', 'adobe'];\n\nexport const VAULT_KNOWN: ReadonlySet<string> = new Set(\n [...LISTINGS.keys()].filter((id) => id !== 'adobe')\n);\n"],
4
+ "sourcesContent": ["import type { Provider } from '@earendil-works/pi-ai';\nimport { antLingProvider } from '@earendil-works/pi-ai/providers/ant-ling';\nimport { anthropicProvider } from '@earendil-works/pi-ai/providers/anthropic';\nimport { azureProvider } from '@earendil-works/pi-ai/providers/azure';\nimport { basetenProvider } from '@earendil-works/pi-ai/providers/baseten';\nimport { cerebrasProvider } from '@earendil-works/pi-ai/providers/cerebras';\nimport { cloudflareAIGatewayProvider } from '@earendil-works/pi-ai/providers/cloudflare-ai-gateway';\nimport { cloudflareWorkersAIProvider } from '@earendil-works/pi-ai/providers/cloudflare-workers-ai';\nimport { deepseekProvider } from '@earendil-works/pi-ai/providers/deepseek';\nimport { fireworksProvider } from '@earendil-works/pi-ai/providers/fireworks';\nimport { groqProvider } from '@earendil-works/pi-ai/providers/groq';\nimport { huggingfaceProvider } from '@earendil-works/pi-ai/providers/huggingface';\nimport { kimiCodingProvider } from '@earendil-works/pi-ai/providers/kimi-coding';\nimport { metaProvider } from '@earendil-works/pi-ai/providers/meta';\nimport { minimaxProvider } from '@earendil-works/pi-ai/providers/minimax';\nimport { minimaxCnProvider } from '@earendil-works/pi-ai/providers/minimax-cn';\nimport { mistralProvider } from '@earendil-works/pi-ai/providers/mistral';\nimport { moonshotaiProvider } from '@earendil-works/pi-ai/providers/moonshotai';\nimport { moonshotaiCnProvider } from '@earendil-works/pi-ai/providers/moonshotai-cn';\nimport { nvidiaProvider } from '@earendil-works/pi-ai/providers/nvidia';\nimport { openaiProvider } from '@earendil-works/pi-ai/providers/openai';\nimport { opencodeProvider } from '@earendil-works/pi-ai/providers/opencode';\nimport { opencodeGoProvider } from '@earendil-works/pi-ai/providers/opencode-go';\nimport { openrouterProvider } from '@earendil-works/pi-ai/providers/openrouter';\nimport { qwenTokenPlanProvider } from '@earendil-works/pi-ai/providers/qwen-token-plan';\nimport { qwenTokenPlanCnProvider } from '@earendil-works/pi-ai/providers/qwen-token-plan-cn';\nimport { qwenTokenPlanIndividualProvider } from '@earendil-works/pi-ai/providers/qwen-token-plan-individual';\nimport { togetherProvider } from '@earendil-works/pi-ai/providers/together';\nimport { vercelAIGatewayProvider } from '@earendil-works/pi-ai/providers/vercel-ai-gateway';\nimport { xaiProvider } from '@earendil-works/pi-ai/providers/xai';\nimport { xiaomiProvider } from '@earendil-works/pi-ai/providers/xiaomi';\nimport { xiaomiTokenPlanAmsProvider } from '@earendil-works/pi-ai/providers/xiaomi-token-plan-ams';\nimport { xiaomiTokenPlanCnProvider } from '@earendil-works/pi-ai/providers/xiaomi-token-plan-cn';\nimport { xiaomiTokenPlanSgpProvider } from '@earendil-works/pi-ai/providers/xiaomi-token-plan-sgp';\nimport { zaiProvider } from '@earendil-works/pi-ai/providers/zai';\nimport { zaiCodingCnProvider } from '@earendil-works/pi-ai/providers/zai-coding-cn';\n\nexport interface FieldOption {\n value: string;\n label: string;\n}\n\nexport interface FieldInput {\n name: string;\n label: string;\n required?: boolean;\n placeholder?: string;\n help?: string;\n customBase?: true;\n options?: readonly FieldOption[];\n value?: string;\n}\n\nexport interface FieldSpec extends FieldInput {\n env(value: string): Record<string, string> | string;\n}\n\nexport interface KeyProvider {\n id: string;\n make: () => Provider;\n env: string;\n description: string;\n keyUrl: string;\n cors: boolean;\n fields?: readonly FieldSpec[];\n}\n\nconst RESOURCE = /^[a-z0-9][a-z0-9-]{0,62}$/i;\nconst DEPLOYMENTS = /^[^=,\\s]+\\s*=\\s*[^=,\\s]+(\\s*,\\s*[^=,\\s]+\\s*=\\s*[^=,\\s]+)*$/;\n\nexport function httpsBase(value: string): URL | undefined {\n const url = URL.parse(value);\n if (url?.protocol !== 'https:' || url.username || url.password) return undefined;\n return url.port === '' || url.port === '443' ? url : undefined;\n}\n\nfunction azureEndpoint(value: string): Record<string, string> | string {\n if (RESOURCE.test(value)) return { AZURE_OPENAI_RESOURCE_NAME: value };\n const url = httpsBase(value);\n if (!url)\n return 'Enter your Azure resource name, or its endpoint as an https:// address without a port or a user name.';\n return { AZURE_OPENAI_BASE_URL: url.href.replace(/\\/+$/, '') };\n}\n\nexport const AZURE = 'azure';\n\nexport function azureBase(env: Readonly<Record<string, string>>): string | undefined {\n if (env.AZURE_OPENAI_BASE_URL) return env.AZURE_OPENAI_BASE_URL;\n const name = env.AZURE_OPENAI_RESOURCE_NAME;\n return name ? `https://${name}.openai.azure.com/openai/v1` : undefined;\n}\n\nconst REGION = /^[a-z]{2,}(-[a-z]+)+-\\d{1,2}$/;\nconst ACCOUNT = /^[0-9a-f]{32}$/i;\nconst GATEWAY = /^[A-Za-z0-9_-]{1,64}$/;\n\nconst checked =\n (pattern: RegExp, env: string, problem: string) =>\n (value: string): Record<string, string> | string =>\n pattern.test(value) ? { [env]: value } : problem;\n\nconst BEDROCK_REGIONS: readonly (readonly [string, string])[] = [\n ['ap-northeast-1', 'Asia Pacific (Tokyo)'],\n ['ap-northeast-2', 'Asia Pacific (Seoul)'],\n ['ap-northeast-3', 'Asia Pacific (Osaka)'],\n ['ap-south-1', 'Asia Pacific (Mumbai)'],\n ['ap-south-2', 'Asia Pacific (Hyderabad)'],\n ['ap-southeast-1', 'Asia Pacific (Singapore)'],\n ['ap-southeast-2', 'Asia Pacific (Sydney)'],\n ['ca-central-1', 'Canada (Central)'],\n ['eu-central-1', 'Europe (Frankfurt)'],\n ['eu-central-2', 'Europe (Zurich)'],\n ['eu-north-1', 'Europe (Stockholm)'],\n ['eu-south-1', 'Europe (Milan)'],\n ['eu-south-2', 'Europe (Spain)'],\n ['eu-west-1', 'Europe (Ireland)'],\n ['eu-west-2', 'Europe (London)'],\n ['eu-west-3', 'Europe (Paris)'],\n ['sa-east-1', 'South America (S\u00E3o Paulo)'],\n ['us-east-1', 'US East (N. Virginia)'],\n ['us-gov-east-1', 'AWS GovCloud (US-East)'],\n ['us-gov-west-1', 'AWS GovCloud (US-West)'],\n ['us-west-2', 'US West (Oregon)'],\n];\n\nexport const DEFAULT_REGION = 'us-west-2';\n\nexport const REGION_FIELD: FieldSpec = {\n name: 'region',\n label: 'Region',\n help: 'The AWS region your Bedrock models run in.',\n options: BEDROCK_REGIONS.map(([value, label]) => ({ value, label: `${label}, ${value}` })),\n value: DEFAULT_REGION,\n env: checked(REGION, 'AWS_REGION', 'Enter an AWS region such as us-west-2.'),\n};\n\nconst ACCOUNT_FIELD: FieldSpec = {\n name: 'account',\n label: 'Account ID',\n required: true,\n placeholder: '0123456789abcdef0123456789abcdef',\n help: 'The 32-character ID on your Cloudflare dashboard\u2019s account overview.',\n env: checked(\n ACCOUNT,\n 'CLOUDFLARE_ACCOUNT_ID',\n 'A Cloudflare account ID is 32 characters of 0\u20139 and a\u2013f.'\n ),\n};\n\nexport const KEY_PROVIDERS: readonly KeyProvider[] = [\n {\n id: 'azure',\n make: azureProvider,\n env: 'AZURE_OPENAI_API_KEY',\n description: 'OpenAI models deployed in your own Azure OpenAI or AI Foundry resource.',\n keyUrl: 'https://portal.azure.com',\n cors: true,\n fields: [\n {\n name: 'endpoint',\n label: 'Endpoint',\n required: true,\n placeholder: 'my-resource or https://my-resource.openai.azure.com',\n help: 'Your resource\u2019s name, or its endpoint address from Keys and Endpoint.',\n customBase: true,\n env: azureEndpoint,\n },\n {\n name: 'deployments',\n label: 'Deployments',\n placeholder: 'gpt-5.4=my-gpt',\n help: 'Only for deployments named differently from their model, as model=deployment, separated by commas.',\n env: (value) =>\n DEPLOYMENTS.test(value)\n ? { AZURE_OPENAI_DEPLOYMENT_NAME_MAP: value }\n : 'Write each deployment as model=deployment, separated by commas.',\n },\n ],\n },\n {\n id: 'cloudflare-workers-ai',\n make: cloudflareWorkersAIProvider,\n env: 'CLOUDFLARE_API_KEY',\n description: 'Open models on Cloudflare Workers AI, in your Cloudflare account.',\n keyUrl: 'https://dash.cloudflare.com/profile/api-tokens',\n cors: false,\n fields: [ACCOUNT_FIELD],\n },\n {\n id: 'cloudflare-ai-gateway',\n make: cloudflareAIGatewayProvider,\n env: 'CLOUDFLARE_API_KEY',\n description: 'Models from many labs through your Cloudflare AI Gateway.',\n keyUrl: 'https://dash.cloudflare.com/profile/api-tokens',\n cors: false,\n fields: [\n ACCOUNT_FIELD,\n {\n name: 'gateway',\n label: 'Gateway ID',\n required: true,\n placeholder: 'my-gateway',\n help: 'The name of your gateway under AI \u203A AI Gateway.',\n env: checked(\n GATEWAY,\n 'CLOUDFLARE_GATEWAY_ID',\n 'A gateway ID is letters, digits, - and _, up to 64 characters.'\n ),\n },\n ],\n },\n {\n id: 'anthropic',\n make: anthropicProvider,\n env: 'ANTHROPIC_API_KEY',\n description: 'Claude models, with an API key from the Claude Console.',\n keyUrl: 'https://console.anthropic.com/settings/keys',\n cors: true,\n },\n {\n id: 'openai',\n make: openaiProvider,\n env: 'OPENAI_API_KEY',\n description: 'GPT models, with an API key from the OpenAI platform.',\n keyUrl: 'https://platform.openai.com/api-keys',\n cors: true,\n },\n {\n id: 'mistral',\n make: mistralProvider,\n env: 'MISTRAL_API_KEY',\n description: 'Mistral and Devstral models from La Plateforme.',\n keyUrl: 'https://console.mistral.ai/api-keys',\n cors: true,\n },\n {\n id: 'groq',\n make: groqProvider,\n env: 'GROQ_API_KEY',\n description: 'Open models on Groq\u2019s fast inference.',\n keyUrl: 'https://console.groq.com/keys',\n cors: true,\n },\n {\n id: 'deepseek',\n make: deepseekProvider,\n env: 'DEEPSEEK_API_KEY',\n description: 'DeepSeek\u2019s own models.',\n keyUrl: 'https://platform.deepseek.com/api_keys',\n cors: true,\n },\n {\n id: 'together',\n make: togetherProvider,\n env: 'TOGETHER_API_KEY',\n description: 'Open models hosted by Together AI.',\n keyUrl: 'https://api.together.ai/settings/api-keys',\n cors: true,\n },\n {\n id: 'huggingface',\n make: huggingfaceProvider,\n env: 'HF_TOKEN',\n description: 'Open models through Hugging Face\u2019s inference router.',\n keyUrl: 'https://huggingface.co/settings/tokens',\n cors: true,\n },\n {\n id: 'xiaomi',\n make: xiaomiProvider,\n env: 'XIAOMI_API_KEY',\n description: 'Xiaomi\u2019s MiMo models.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'xiaomi-token-plan-ams',\n make: xiaomiTokenPlanAmsProvider,\n env: 'XIAOMI_TOKEN_PLAN_AMS_API_KEY',\n description: 'MiMo models on a Xiaomi token plan, served from Amsterdam.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'xiaomi-token-plan-cn',\n make: xiaomiTokenPlanCnProvider,\n env: 'XIAOMI_TOKEN_PLAN_CN_API_KEY',\n description: 'MiMo models on a Xiaomi token plan, served from China.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'xiaomi-token-plan-sgp',\n make: xiaomiTokenPlanSgpProvider,\n env: 'XIAOMI_TOKEN_PLAN_SGP_API_KEY',\n description: 'MiMo models on a Xiaomi token plan, served from Singapore.',\n keyUrl: 'https://platform.xiaomimimo.com/#/console/api-keys',\n cors: true,\n },\n {\n id: 'cerebras',\n make: cerebrasProvider,\n env: 'CEREBRAS_API_KEY',\n description: 'Open models on Cerebras inference.',\n keyUrl: 'https://cloud.cerebras.ai',\n cors: false,\n },\n {\n id: 'openrouter',\n make: openrouterProvider,\n env: 'OPENROUTER_API_KEY',\n description: 'Hundreds of models from many labs through one key.',\n keyUrl: 'https://openrouter.ai/settings/keys',\n cors: false,\n },\n {\n id: 'fireworks',\n make: fireworksProvider,\n env: 'FIREWORKS_API_KEY',\n description: 'Open models hosted by Fireworks AI.',\n keyUrl: 'https://app.fireworks.ai/settings/users/api-keys',\n cors: false,\n },\n {\n id: 'baseten',\n make: basetenProvider,\n env: 'BASETEN_API_KEY',\n description: 'Open models hosted by Baseten.',\n keyUrl: 'https://app.baseten.co/settings/api_keys',\n cors: true,\n },\n {\n id: 'moonshotai',\n make: moonshotaiProvider,\n env: 'MOONSHOT_API_KEY',\n description: 'Kimi models from Moonshot AI.',\n keyUrl: 'https://platform.moonshot.ai/console/api-keys',\n cors: true,\n },\n {\n id: 'moonshotai-cn',\n make: moonshotaiCnProvider,\n env: 'MOONSHOT_API_KEY',\n description: 'Kimi models from Moonshot AI\u2019s China platform.',\n keyUrl: 'https://platform.moonshot.cn/console/api-keys',\n cors: true,\n },\n {\n id: 'xai',\n make: xaiProvider,\n env: 'XAI_API_KEY',\n description: 'Grok models from xAI.',\n keyUrl: 'https://console.x.ai',\n cors: true,\n },\n {\n id: 'meta',\n make: metaProvider,\n env: 'META_API_KEY',\n description: 'Meta\u2019s Muse models.',\n keyUrl: 'https://developer.meta.ai',\n cors: true,\n },\n {\n id: 'vercel-ai-gateway',\n make: vercelAIGatewayProvider,\n env: 'AI_GATEWAY_API_KEY',\n description: 'Models from many labs through Vercel\u2019s AI Gateway.',\n keyUrl: 'https://vercel.com/docs/ai-gateway/authentication',\n cors: false,\n },\n {\n id: 'minimax',\n make: minimaxProvider,\n env: 'MINIMAX_API_KEY',\n description: 'MiniMax\u2019s models.',\n keyUrl: 'https://platform.minimax.io/user-center/basic-information/interface-key',\n cors: false,\n },\n {\n id: 'minimax-cn',\n make: minimaxCnProvider,\n env: 'MINIMAX_CN_API_KEY',\n description: 'MiniMax\u2019s models from its China platform.',\n keyUrl: 'https://platform.minimaxi.com/user-center/basic-information/interface-key',\n cors: false,\n },\n {\n id: 'zai',\n make: zaiProvider,\n env: 'ZAI_API_KEY',\n description: 'GLM models on Z.AI\u2019s coding plan.',\n keyUrl: 'https://z.ai/manage-apikey/apikey-list',\n cors: false,\n },\n {\n id: 'zai-coding-cn',\n make: zaiCodingCnProvider,\n env: 'ZAI_CODING_CN_API_KEY',\n description: 'GLM models on Zhipu\u2019s coding plan in China.',\n keyUrl: 'https://open.bigmodel.cn/usercenter/apikeys',\n cors: false,\n },\n {\n id: 'nvidia',\n make: nvidiaProvider,\n env: 'NVIDIA_API_KEY',\n description: 'Open models on NVIDIA\u2019s API catalog.',\n keyUrl: 'https://build.nvidia.com/settings/api-keys',\n cors: false,\n },\n {\n id: 'kimi-coding',\n make: kimiCodingProvider,\n env: 'KIMI_API_KEY',\n description: 'Kimi models on the Kimi Code subscription.',\n keyUrl: 'https://www.kimi.com/code/console',\n cors: false,\n },\n {\n id: 'ant-ling',\n make: antLingProvider,\n env: 'ANT_LING_API_KEY',\n description: 'Ling and Ring models from Ant Group.',\n keyUrl: 'https://www.ant-ling.com',\n cors: false,\n },\n {\n id: 'opencode',\n make: opencodeProvider,\n env: 'OPENCODE_API_KEY',\n description:\n 'Models curated for coding agents by OpenCode Zen. Needs seven\u2019s vault 1.12.2 or later.',\n keyUrl: 'https://opencode.ai/auth',\n cors: false,\n },\n {\n id: 'opencode-go',\n make: opencodeGoProvider,\n env: 'OPENCODE_API_KEY',\n description:\n 'Open models on the OpenCode Go subscription. Needs seven\u2019s vault 1.12.2 or later.',\n keyUrl: 'https://opencode.ai/auth',\n cors: false,\n },\n {\n id: 'qwen-token-plan',\n make: qwenTokenPlanProvider,\n env: 'QWEN_TOKEN_PLAN_API_KEY',\n description: 'Qwen models on an Alibaba Cloud token plan.',\n keyUrl: 'https://modelstudio.console.alibabacloud.com',\n cors: false,\n },\n {\n id: 'qwen-token-plan-cn',\n make: qwenTokenPlanCnProvider,\n env: 'QWEN_TOKEN_PLAN_CN_API_KEY',\n description: 'Qwen models on an Alibaba Cloud token plan in China.',\n keyUrl: 'https://bailian.console.aliyun.com',\n cors: false,\n },\n {\n id: 'qwen-token-plan-individual',\n make: qwenTokenPlanIndividualProvider,\n env: 'QWEN_TOKEN_PLAN_API_KEY',\n description: 'Qwen models on an individual Alibaba Cloud token plan.',\n keyUrl: 'https://modelstudio.console.alibabacloud.com',\n cors: false,\n },\n];\n\nexport type ProviderAuth = 'api-key' | 'oauth' | 'fields' | 'local';\n\nexport interface Listing {\n description: string;\n keyUrl?: string;\n auth: ProviderAuth;\n corsFree: boolean;\n vaultName: string;\n fields?: readonly FieldSpec[];\n}\n\nconst upper = (text: string) => text.toUpperCase().replace(/[^A-Z0-9]/g, '_');\n\nfunction vaultNames(entries: readonly KeyProvider[]): Map<string, string> {\n const uses = Map.groupBy(entries, (entry) => entry.env);\n return new Map(\n entries.map((entry) => [\n entry.id,\n (uses.get(entry.env) as KeyProvider[]).length > 1 ? `${upper(entry.id)}_API_KEY` : entry.env,\n ])\n );\n}\n\nconst named = vaultNames(KEY_PROVIDERS);\n\nexport const LISTINGS: ReadonlyMap<string, Listing> = new Map<string, Listing>([\n [\n 'amazon-bedrock',\n {\n description: 'Claude and other models on AWS, with a Bedrock API key.',\n keyUrl: 'https://console.aws.amazon.com/bedrock/home#/api-keys',\n auth: 'fields',\n corsFree: false,\n vaultName: 'AWS_BEARER_TOKEN_BEDROCK',\n fields: [REGION_FIELD],\n },\n ],\n [\n 'adobe',\n {\n description: 'Models through Adobe\u2019s LLM proxy, with your Adobe ID.',\n auth: 'oauth',\n corsFree: false,\n vaultName: 'ADOBE_IMS_TOKEN',\n },\n ],\n ...KEY_PROVIDERS.map((entry): [string, Listing] => [\n entry.id,\n {\n description: entry.description,\n keyUrl: entry.keyUrl,\n auth: entry.fields ? 'fields' : 'api-key',\n corsFree: !entry.cors,\n vaultName: named.get(entry.id) as string,\n ...(entry.fields ? { fields: entry.fields } : {}),\n },\n ]),\n]);\n\nexport const ADDED_FIRST: readonly string[] = ['amazon-bedrock', 'adobe'];\n\nexport const VAULT_KNOWN: ReadonlySet<string> = new Set(\n [...LISTINGS.keys()].filter((id) => id !== 'adobe')\n);\n"],
5
5
  "mappings": "AACA,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,qBAAqB;AAC9B,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,mCAAmC;AAC5C,SAAS,mCAAmC;AAC5C,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,kCAAkC;AAC3C,SAAS,iCAAiC;AAC1C,SAAS,kCAAkC;AAC3C,SAAS,mBAAmB;AAC5B,SAAS,2BAA2B;AAgCpC,MAAM,WAAW;AACjB,MAAM,cAAc;AAEb,SAAS,UAAU,OAAgC;AACxD,QAAM,MAAM,IAAI,MAAM,KAAK;AAC3B,MAAI,KAAK,aAAa,YAAY,IAAI,YAAY,IAAI,SAAU,QAAO;AACvE,SAAO,IAAI,SAAS,MAAM,IAAI,SAAS,QAAQ,MAAM;AACvD;AAEA,SAAS,cAAc,OAAgD;AACrE,MAAI,SAAS,KAAK,KAAK,EAAG,QAAO,EAAE,4BAA4B,MAAM;AACrE,QAAM,MAAM,UAAU,KAAK;AAC3B,MAAI,CAAC;AACH,WAAO;AACT,SAAO,EAAE,uBAAuB,IAAI,KAAK,QAAQ,QAAQ,EAAE,EAAE;AAC/D;AAEO,MAAM,QAAQ;AAEd,SAAS,UAAU,KAA2D;AACnF,MAAI,IAAI,sBAAuB,QAAO,IAAI;AAC1C,QAAM,OAAO,IAAI;AACjB,SAAO,OAAO,WAAW,IAAI,gCAAgC;AAC/D;AAEA,MAAM,SAAS;AACf,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,MAAM,UACJ,CAAC,SAAiB,KAAa,YAC/B,CAAC,UACC,QAAQ,KAAK,KAAK,IAAI,EAAE,CAAC,GAAG,GAAG,MAAM,IAAI;AAE7C,MAAM,kBAA0D;AAAA,EAC9D,CAAC,kBAAkB,sBAAsB;AAAA,EACzC,CAAC,kBAAkB,sBAAsB;AAAA,EACzC,CAAC,kBAAkB,sBAAsB;AAAA,EACzC,CAAC,cAAc,uBAAuB;AAAA,EACtC,CAAC,cAAc,0BAA0B;AAAA,EACzC,CAAC,kBAAkB,0BAA0B;AAAA,EAC7C,CAAC,kBAAkB,uBAAuB;AAAA,EAC1C,CAAC,gBAAgB,kBAAkB;AAAA,EACnC,CAAC,gBAAgB,oBAAoB;AAAA,EACrC,CAAC,gBAAgB,iBAAiB;AAAA,EAClC,CAAC,cAAc,oBAAoB;AAAA,EACnC,CAAC,cAAc,gBAAgB;AAAA,EAC/B,CAAC,cAAc,gBAAgB;AAAA,EAC/B,CAAC,aAAa,kBAAkB;AAAA,EAChC,CAAC,aAAa,iBAAiB;AAAA,EAC/B,CAAC,aAAa,gBAAgB;AAAA,EAC9B,CAAC,aAAa,8BAA2B;AAAA,EACzC,CAAC,aAAa,uBAAuB;AAAA,EACrC,CAAC,iBAAiB,wBAAwB;AAAA,EAC1C,CAAC,iBAAiB,wBAAwB;AAAA,EAC1C,CAAC,aAAa,kBAAkB;AAClC;AAEO,MAAM,iBAAiB;AAEvB,MAAM,eAA0B;AAAA,EACrC,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS,gBAAgB,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,EAAE,OAAO,OAAO,GAAG,KAAK,KAAK,KAAK,GAAG,EAAE;AAAA,EACzF,OAAO;AAAA,EACP,KAAK,QAAQ,QAAQ,cAAc,wCAAwC;AAC7E;AAEA,MAAM,gBAA2B;AAAA,EAC/B,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,MAAM;AAAA,EACN,KAAK;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,MAAM,gBAAwC;AAAA,EACnD;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU;AAAA,QACV,aAAa;AAAA,QACb,MAAM;AAAA,QACN,YAAY;AAAA,QACZ,KAAK;AAAA,MACP;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM;AAAA,QACN,KAAK,CAAC,UACJ,YAAY,KAAK,KAAK,IAClB,EAAE,kCAAkC,MAAM,IAC1C;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ,CAAC,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU;AAAA,QACV,aAAa;AAAA,QACb,MAAM;AAAA,QACN,KAAK;AAAA,UACH;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aACE;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aACE;AAAA,IACF,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AACF;AAaA,MAAM,QAAQ,CAAC,SAAiB,KAAK,YAAY,EAAE,QAAQ,cAAc,GAAG;AAE5E,SAAS,WAAW,SAAsD;AACxE,QAAM,OAAO,IAAI,QAAQ,SAAS,CAAC,UAAU,MAAM,GAAG;AACtD,SAAO,IAAI;AAAA,IACT,QAAQ,IAAI,CAAC,UAAU;AAAA,MACrB,MAAM;AAAA,MACL,KAAK,IAAI,MAAM,GAAG,EAAoB,SAAS,IAAI,GAAG,MAAM,MAAM,EAAE,CAAC,aAAa,MAAM;AAAA,IAC3F,CAAC;AAAA,EACH;AACF;AAEA,MAAM,QAAQ,WAAW,aAAa;AAE/B,MAAM,WAAyC,IAAI,IAAqB;AAAA,EAC7E;AAAA,IACE;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,MACV,WAAW;AAAA,MACX,QAAQ,CAAC,YAAY;AAAA,IACvB;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,MACE,aAAa;AAAA,MACb,MAAM;AAAA,MACN,UAAU;AAAA,MACV,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,GAAG,cAAc,IAAI,CAAC,UAA6B;AAAA,IACjD,MAAM;AAAA,IACN;AAAA,MACE,aAAa,MAAM;AAAA,MACnB,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM,SAAS,WAAW;AAAA,MAChC,UAAU,CAAC,MAAM;AAAA,MACjB,WAAW,MAAM,IAAI,MAAM,EAAE;AAAA,MAC7B,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AACH,CAAC;AAEM,MAAM,cAAiC,CAAC,kBAAkB,OAAO;AAEjE,MAAM,cAAmC,IAAI;AAAA,EAClD,CAAC,GAAG,SAAS,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,OAAO,OAAO;AACpD;",
6
6
  "names": []
7
7
  }