@ai-ecoverse/slicc-agent 5.0.1 → 5.0.3

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
@@ -626,11 +626,12 @@ The agent never holds a secret's value. Values live in the vault (`vault.sliccy.
626
626
  - **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.
627
627
  - **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.
628
628
  - **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.
629
+ - **For bios:** the agent's `notices` (keys moved, the mismatch banner) 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.
629
630
  - **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.
630
631
 
631
632
  ## The kernel
632
633
 
633
- `@ai-ecoverse/slicc-kernel` is a peer dependency (`>=1.81.0 <2`; until 3.31.0 the floor was 1.26.3, then 1.47.1): the agent only calls `attachKernel`, the client end of the page kernel's port, so it must match the kernel the page runs. Seven chooses that version: slicc-bios pins the certified kernel next to the agent in `src/packages/agent/package.json`, and pnpm fills the peer from that pin. The floor is the kernel slicc-bios ships (1.81.0 today), and slicc-bios's own CI tests the agent on it; when slicc-bios moves its pin, the floor follows. The kernel and the `wasm-*` commands are devDependencies pinned to certified versions (kernel 1.94.0 today), and CI runs the unit and integration tests on them in one leg. If the port protocol ever breaks within 1.x, a patch release narrows the upper bound. Renovate opens PRs for the kernel, `wasi-pnpm`, the `wasm-*` packages and `slicc-cdp` but never merges them, because they move only after the homescoop coordinator certifies them; such a PR is merged once the cert lane has certified its version.
634
+ `@ai-ecoverse/slicc-kernel` is a peer dependency (`>=1.90.0 <2`; until 3.31.0 the floor was 1.26.3, then 1.47.1, then 1.81.0): the agent only calls `attachKernel`, the client end of the page kernel's port, so it must match the kernel the page runs. Seven chooses that version: slicc-bios pins the certified kernel next to the agent in `src/packages/agent/package.json`, and pnpm fills the peer from that pin. The floor is the kernel slicc-bios ships (1.90.0 today), and slicc-bios's own CI tests the agent on it; when slicc-bios moves its pin, the floor follows. The kernel and the `wasm-*` commands are devDependencies pinned to certified versions (kernel 1.94.0 today), and CI runs the unit and integration tests on them in one leg. If the port protocol ever breaks within 1.x, a patch release narrows the upper bound. Renovate opens PRs for the kernel, `wasi-pnpm`, the `wasm-*` packages and `slicc-cdp` but never merges them, because they move only after the homescoop coordinator certifies them; such a PR is merged once the cert lane has certified its version.
634
635
 
635
636
  ## Patched dependencies
636
637
 
@@ -54,7 +54,7 @@ function createAgentModel(connection, options = {}) {
54
54
  sprinkles: new SprinkleAdapter(connection, report),
55
55
  memory: new MemoryAdapter(connection, report),
56
56
  changes: new ChangesAdapter(connection, report),
57
- notices: new NoticesAdapter(connection.settings, report, connection.mismatch),
57
+ notices: new NoticesAdapter(connection.settings, report, connection.mismatch, options.notices),
58
58
  monitor: new MonitorAdapter(
59
59
  connection,
60
60
  options.monitor?.extra ? { extra: options.monitor.extra } : {}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/index.ts"],
4
- "sourcesContent": ["import type { MonitorSection } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { ChangesAdapter } from './changes-port.ts';\nimport { MemoryAdapter } from './memory-port.ts';\nimport { MonitorAdapter } from './monitor-port.ts';\nimport { NoticesAdapter } from './notices-port.ts';\nimport { PageAdapter, type PageOptions } from './page-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { SprinkleAdapter } from './sprinkle-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { ChangesAdapter, type ChangesEvents } from './changes-port.ts';\nexport { MemoryAdapter } from './memory-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport {\n MonitorAdapter,\n type MonitorAdapterOptions,\n type MonitorInput,\n monitorSnapshot,\n} from './monitor-port.ts';\nexport {\n KEYS_MOVED,\n MISMATCH,\n mismatchBanner,\n movedList,\n movedNotice,\n NoticesAdapter,\n} from './notices-port.ts';\nexport {\n PageAdapter,\n type PageDocument,\n type PageOptions,\n type PageRun,\n type PageTarget,\n} from './page-port.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { SprinkleAdapter } from './sprinkle-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: {\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null;\n login?: Login;\n monitor?: { extra?: () => readonly MonitorSection[] };\n page?: PageOptions;\n } = {}\n): {\n agent: AgentAdapter;\n settings: SettingsAdapter;\n tray: TrayAdapter;\n sprinkles: SprinkleAdapter;\n memory: MemoryAdapter;\n changes: ChangesAdapter;\n monitor: MonitorAdapter;\n notices: NoticesAdapter;\n page: PageAdapter | null;\n} {\n const agent = new AgentAdapter(connection);\n const report = (text: string) => agent.notify(text);\n const run = options.page?.run;\n return {\n agent,\n page: options.page && run ? new PageAdapter(connection, agent, { ...options.page, run }) : null,\n settings: new SettingsAdapter(\n connection.settings,\n agent,\n options.storage ?? null,\n options.login ?? null\n ),\n tray: new TrayAdapter(connection.settings),\n sprinkles: new SprinkleAdapter(connection, report),\n memory: new MemoryAdapter(connection, report),\n changes: new ChangesAdapter(connection, report),\n notices: new NoticesAdapter(connection.settings, report, connection.mismatch),\n monitor: new MonitorAdapter(\n connection,\n options.monitor?.extra ? { extra: options.monitor.extra } : {}\n ),\n };\n}\n"],
5
- "mappings": "AAEA,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;AAC/B,SAAS,mBAAqC;AAC9C,SAAqB,uBAAuB;AAC5C,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAE5B,SAAS,gBAAAA,eAAc,YAAY;AACnC,SAAS,kBAAAC,uBAA0C;AACnD,SAAS,iBAAAC,sBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE,kBAAAC;AAAA,EAGA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAAC;AAAA,OACK;AACP;AAAA,EACE,eAAAC;AAAA,OAKK;AACP,SAAqB,cAAc,mBAAAC,wBAAuB;AAC1D,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,eAAAC,oBAAmB;AAErB,SAAS,iBACd,YACA,UAKI,CAAC,GAWL;AACA,QAAM,QAAQ,IAAI,aAAa,UAAU;AACzC,QAAM,SAAS,CAAC,SAAiB,MAAM,OAAO,IAAI;AAClD,QAAM,MAAM,QAAQ,MAAM;AAC1B,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ,QAAQ,MAAM,IAAI,YAAY,YAAY,OAAO,EAAE,GAAG,QAAQ,MAAM,IAAI,CAAC,IAAI;AAAA,IAC3F,UAAU,IAAI;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA,QAAQ,WAAW;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AAAA,IACA,MAAM,IAAI,YAAY,WAAW,QAAQ;AAAA,IACzC,WAAW,IAAI,gBAAgB,YAAY,MAAM;AAAA,IACjD,QAAQ,IAAI,cAAc,YAAY,MAAM;AAAA,IAC5C,SAAS,IAAI,eAAe,YAAY,MAAM;AAAA,IAC9C,SAAS,IAAI,eAAe,WAAW,UAAU,QAAQ,WAAW,QAAQ;AAAA,IAC5E,SAAS,IAAI;AAAA,MACX;AAAA,MACA,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { MonitorSection } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { ChangesAdapter } from './changes-port.ts';\nimport { MemoryAdapter } from './memory-port.ts';\nimport { MonitorAdapter } from './monitor-port.ts';\nimport { type HostNotices, NoticesAdapter } from './notices-port.ts';\nimport { PageAdapter, type PageOptions } from './page-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { SprinkleAdapter } from './sprinkle-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { ChangesAdapter, type ChangesEvents } from './changes-port.ts';\nexport { MemoryAdapter } from './memory-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport {\n MonitorAdapter,\n type MonitorAdapterOptions,\n type MonitorInput,\n monitorSnapshot,\n} from './monitor-port.ts';\nexport {\n type HostNotices,\n KEYS_MOVED,\n MISMATCH,\n mismatchBanner,\n movedList,\n movedNotice,\n NoticesAdapter,\n} from './notices-port.ts';\nexport {\n PageAdapter,\n type PageDocument,\n type PageOptions,\n type PageRun,\n type PageTarget,\n} from './page-port.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { SprinkleAdapter } from './sprinkle-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: {\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null;\n login?: Login;\n monitor?: { extra?: () => readonly MonitorSection[] };\n page?: PageOptions;\n notices?: HostNotices;\n } = {}\n): {\n agent: AgentAdapter;\n settings: SettingsAdapter;\n tray: TrayAdapter;\n sprinkles: SprinkleAdapter;\n memory: MemoryAdapter;\n changes: ChangesAdapter;\n monitor: MonitorAdapter;\n notices: NoticesAdapter;\n page: PageAdapter | null;\n} {\n const agent = new AgentAdapter(connection);\n const report = (text: string) => agent.notify(text);\n const run = options.page?.run;\n return {\n agent,\n page: options.page && run ? new PageAdapter(connection, agent, { ...options.page, run }) : null,\n settings: new SettingsAdapter(\n connection.settings,\n agent,\n options.storage ?? null,\n options.login ?? null\n ),\n tray: new TrayAdapter(connection.settings),\n sprinkles: new SprinkleAdapter(connection, report),\n memory: new MemoryAdapter(connection, report),\n changes: new ChangesAdapter(connection, report),\n notices: new NoticesAdapter(connection.settings, report, connection.mismatch, options.notices),\n monitor: new MonitorAdapter(\n connection,\n options.monitor?.extra ? { extra: options.monitor.extra } : {}\n ),\n };\n}\n"],
5
+ "mappings": "AAEA,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAA2B,sBAAsB;AACjD,SAAS,mBAAqC;AAC9C,SAAqB,uBAAuB;AAC5C,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAE5B,SAAS,gBAAAA,eAAc,YAAY;AACnC,SAAS,kBAAAC,uBAA0C;AACnD,SAAS,iBAAAC,sBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE,kBAAAC;AAAA,EAGA;AAAA,OACK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAAC;AAAA,OACK;AACP;AAAA,EACE,eAAAC;AAAA,OAKK;AACP,SAAqB,cAAc,mBAAAC,wBAAuB;AAC1D,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,eAAAC,oBAAmB;AAErB,SAAS,iBACd,YACA,UAMI,CAAC,GAWL;AACA,QAAM,QAAQ,IAAI,aAAa,UAAU;AACzC,QAAM,SAAS,CAAC,SAAiB,MAAM,OAAO,IAAI;AAClD,QAAM,MAAM,QAAQ,MAAM;AAC1B,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ,QAAQ,MAAM,IAAI,YAAY,YAAY,OAAO,EAAE,GAAG,QAAQ,MAAM,IAAI,CAAC,IAAI;AAAA,IAC3F,UAAU,IAAI;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA,QAAQ,WAAW;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AAAA,IACA,MAAM,IAAI,YAAY,WAAW,QAAQ;AAAA,IACzC,WAAW,IAAI,gBAAgB,YAAY,MAAM;AAAA,IACjD,QAAQ,IAAI,cAAc,YAAY,MAAM;AAAA,IAC5C,SAAS,IAAI,eAAe,YAAY,MAAM;AAAA,IAC9C,SAAS,IAAI,eAAe,WAAW,UAAU,QAAQ,WAAW,UAAU,QAAQ,OAAO;AAAA,IAC7F,SAAS,IAAI;AAAA,MACX;AAAA,MACA,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;",
6
6
  "names": ["AgentAdapter", "ChangesAdapter", "MemoryAdapter", "MonitorAdapter", "NoticesAdapter", "PageAdapter", "SettingsAdapter", "SprinkleAdapter", "TrayAdapter"]
7
7
  }
@@ -37,21 +37,36 @@ class NoticesAdapter extends Emitter {
37
37
  #settings;
38
38
  #report;
39
39
  #mismatch;
40
+ #host;
40
41
  #hidden = false;
41
- constructor(settings, report, mismatch) {
42
+ constructor(settings, report, mismatch, host) {
42
43
  super();
43
44
  this.#settings = settings;
44
45
  this.#report = report;
45
46
  this.#mismatch = mismatch ? [mismatchBanner(mismatch)] : [];
47
+ this.#host = host;
46
48
  settings.state.subscribe(() => this.emit("notices", this.list()));
49
+ host?.on("notices", () => this.emit("notices", this.list()));
47
50
  }
48
51
  list() {
52
+ return [...this.#host?.list() ?? [], ...this.#agent()];
53
+ }
54
+ #agent() {
49
55
  const moved = this.#settings.state.value?.moved;
50
56
  return [...this.#mismatch, ...moved?.length && !this.#hidden ? [movedNotice(moved)] : []];
51
57
  }
52
- async act() {
58
+ #hosts(id) {
59
+ return this.#host?.list().find((notice) => notice.id === id);
60
+ }
61
+ async act(id, action) {
62
+ if (this.#hosts(id)) await this.#host?.act(id, action);
53
63
  }
54
64
  dismiss(id) {
65
+ const hosted = this.#hosts(id);
66
+ if (hosted) {
67
+ if (hosted.dismissible !== false) this.#host?.dismiss(id);
68
+ return;
69
+ }
55
70
  if (id !== KEYS_MOVED) return;
56
71
  this.#hidden = true;
57
72
  this.emit("notices", this.list());
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/notices-port.ts"],
4
- "sourcesContent": ["import type { Notice, NoticesPort } from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { MovedName } from '../secrets/legacy.ts';\nimport type { AgentSettings } from '../services.ts';\nimport { attempt, type Report } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\n\nexport const KEYS_MOVED = 'keys-moved';\n\nexport const MISMATCH = 'agent-mismatch';\n\nexport function mismatchBanner(text: string): Notice {\n return {\n id: MISMATCH,\n tone: 'warning',\n title: \"This tab can't follow that SLICC\",\n body: text,\n actions: [],\n dismissible: false,\n };\n}\n\nfunction servers(names: readonly string[], lead: boolean): string {\n if (names.length > 1) return `MCP servers: ${names.join(', ')}`;\n return `${lead ? 'the' : 'and the'} MCP server ${names[0]}`;\n}\n\nexport function movedList(accounts: readonly MovedName[]): string {\n const models = accounts.filter((name) => name.kind === 'model').map((name) => name.label);\n const mcp = accounts.filter((name) => name.kind === 'mcp').map((name) => name.label);\n if (!mcp.length) return models.join(', ');\n if (!models.length) return servers(mcp, true);\n return `${models.join(', ')}${mcp.length > 1 ? '; ' : ', '}${servers(mcp, false)}`;\n}\n\nexport function movedNotice(accounts: readonly MovedName[]): Notice {\n return {\n id: KEYS_MOVED,\n tone: 'info',\n title: \"Your keys moved to seven's vault.\",\n body: `Add them again in Settings \u203A Accounts: ${movedList(accounts)}.`,\n actions: [],\n };\n}\n\nexport class NoticesAdapter extends Emitter<{ notices: readonly Notice[] }> implements NoticesPort {\n readonly #settings: AgentSettings;\n readonly #report: Report;\n readonly #mismatch: readonly Notice[];\n #hidden = false;\n\n constructor(settings: AgentSettings, report: Report, mismatch?: string) {\n super();\n this.#settings = settings;\n this.#report = report;\n this.#mismatch = mismatch ? [mismatchBanner(mismatch)] : [];\n settings.state.subscribe(() => this.emit('notices', this.list()));\n }\n\n list(): readonly Notice[] {\n const moved = this.#settings.state.value?.moved;\n return [...this.#mismatch, ...(moved?.length && !this.#hidden ? [movedNotice(moved)] : [])];\n }\n\n async act(): Promise<void> {}\n\n dismiss(id: string): void {\n if (id !== KEYS_MOVED) return;\n this.#hidden = true;\n this.emit('notices', this.list());\n attempt(this.#settings.dismissMoved(BACKGROUND_CONTEXT), 'dismiss the notice', this.#report);\n }\n}\n"],
5
- "mappings": "AACA,SAAS,0BAA0B;AAGnC,SAAS,eAA4B;AACrC,SAAS,eAAe;AAEjB,MAAM,aAAa;AAEnB,MAAM,WAAW;AAEjB,SAAS,eAAe,MAAsB;AACnD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS,CAAC;AAAA,IACV,aAAa;AAAA,EACf;AACF;AAEA,SAAS,QAAQ,OAA0B,MAAuB;AAChE,MAAI,MAAM,SAAS,EAAG,QAAO,gBAAgB,MAAM,KAAK,IAAI,CAAC;AAC7D,SAAO,GAAG,OAAO,QAAQ,SAAS,eAAe,MAAM,CAAC,CAAC;AAC3D;AAEO,SAAS,UAAU,UAAwC;AAChE,QAAM,SAAS,SAAS,OAAO,CAAC,SAAS,KAAK,SAAS,OAAO,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;AACxF,QAAM,MAAM,SAAS,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;AACnF,MAAI,CAAC,IAAI,OAAQ,QAAO,OAAO,KAAK,IAAI;AACxC,MAAI,CAAC,OAAO,OAAQ,QAAO,QAAQ,KAAK,IAAI;AAC5C,SAAO,GAAG,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,SAAS,IAAI,OAAO,IAAI,GAAG,QAAQ,KAAK,KAAK,CAAC;AAClF;AAEO,SAAS,YAAY,UAAwC;AAClE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM,+CAA0C,UAAU,QAAQ,CAAC;AAAA,IACnE,SAAS,CAAC;AAAA,EACZ;AACF;AAEO,MAAM,uBAAuB,QAA+D;AAAA,EACxF;AAAA,EACA;AAAA,EACA;AAAA,EACT,UAAU;AAAA,EAEV,YAAY,UAAyB,QAAgB,UAAmB;AACtE,UAAM;AACN,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,YAAY,WAAW,CAAC,eAAe,QAAQ,CAAC,IAAI,CAAC;AAC1D,aAAS,MAAM,UAAU,MAAM,KAAK,KAAK,WAAW,KAAK,KAAK,CAAC,CAAC;AAAA,EAClE;AAAA,EAEA,OAA0B;AACxB,UAAM,QAAQ,KAAK,UAAU,MAAM,OAAO;AAC1C,WAAO,CAAC,GAAG,KAAK,WAAW,GAAI,OAAO,UAAU,CAAC,KAAK,UAAU,CAAC,YAAY,KAAK,CAAC,IAAI,CAAC,CAAE;AAAA,EAC5F;AAAA,EAEA,MAAM,MAAqB;AAAA,EAAC;AAAA,EAE5B,QAAQ,IAAkB;AACxB,QAAI,OAAO,WAAY;AACvB,SAAK,UAAU;AACf,SAAK,KAAK,WAAW,KAAK,KAAK,CAAC;AAChC,YAAQ,KAAK,UAAU,aAAa,kBAAkB,GAAG,sBAAsB,KAAK,OAAO;AAAA,EAC7F;AACF;",
4
+ "sourcesContent": ["import type { Notice, NoticesPort } from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { MovedName } from '../secrets/legacy.ts';\nimport type { AgentSettings } from '../services.ts';\nimport { attempt, type Report } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\n\nexport const KEYS_MOVED = 'keys-moved';\n\nexport const MISMATCH = 'agent-mismatch';\n\nexport function mismatchBanner(text: string): Notice {\n return {\n id: MISMATCH,\n tone: 'warning',\n title: \"This tab can't follow that SLICC\",\n body: text,\n actions: [],\n dismissible: false,\n };\n}\n\nfunction servers(names: readonly string[], lead: boolean): string {\n if (names.length > 1) return `MCP servers: ${names.join(', ')}`;\n return `${lead ? 'the' : 'and the'} MCP server ${names[0]}`;\n}\n\nexport function movedList(accounts: readonly MovedName[]): string {\n const models = accounts.filter((name) => name.kind === 'model').map((name) => name.label);\n const mcp = accounts.filter((name) => name.kind === 'mcp').map((name) => name.label);\n if (!mcp.length) return models.join(', ');\n if (!models.length) return servers(mcp, true);\n return `${models.join(', ')}${mcp.length > 1 ? '; ' : ', '}${servers(mcp, false)}`;\n}\n\nexport function movedNotice(accounts: readonly MovedName[]): Notice {\n return {\n id: KEYS_MOVED,\n tone: 'info',\n title: \"Your keys moved to seven's vault.\",\n body: `Add them again in Settings \u203A Accounts: ${movedList(accounts)}.`,\n actions: [],\n };\n}\n\nexport type HostNotices = Pick<NoticesPort, 'list' | 'act' | 'dismiss' | 'on'>;\n\nexport class NoticesAdapter extends Emitter<{ notices: readonly Notice[] }> implements NoticesPort {\n readonly #settings: AgentSettings;\n readonly #report: Report;\n readonly #mismatch: readonly Notice[];\n readonly #host: HostNotices | undefined;\n #hidden = false;\n\n constructor(settings: AgentSettings, report: Report, mismatch?: string, host?: HostNotices) {\n super();\n this.#settings = settings;\n this.#report = report;\n this.#mismatch = mismatch ? [mismatchBanner(mismatch)] : [];\n this.#host = host;\n settings.state.subscribe(() => this.emit('notices', this.list()));\n host?.on('notices', () => this.emit('notices', this.list()));\n }\n\n list(): readonly Notice[] {\n return [...(this.#host?.list() ?? []), ...this.#agent()];\n }\n\n #agent(): readonly Notice[] {\n const moved = this.#settings.state.value?.moved;\n return [...this.#mismatch, ...(moved?.length && !this.#hidden ? [movedNotice(moved)] : [])];\n }\n\n #hosts(id: string): Notice | undefined {\n return this.#host?.list().find((notice) => notice.id === id);\n }\n\n async act(id: string, action: string): Promise<void> {\n if (this.#hosts(id)) await this.#host?.act(id, action);\n }\n\n dismiss(id: string): void {\n const hosted = this.#hosts(id);\n if (hosted) {\n if (hosted.dismissible !== false) this.#host?.dismiss(id);\n return;\n }\n if (id !== KEYS_MOVED) return;\n this.#hidden = true;\n this.emit('notices', this.list());\n attempt(this.#settings.dismissMoved(BACKGROUND_CONTEXT), 'dismiss the notice', this.#report);\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,0BAA0B;AAGnC,SAAS,eAA4B;AACrC,SAAS,eAAe;AAEjB,MAAM,aAAa;AAEnB,MAAM,WAAW;AAEjB,SAAS,eAAe,MAAsB;AACnD,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS,CAAC;AAAA,IACV,aAAa;AAAA,EACf;AACF;AAEA,SAAS,QAAQ,OAA0B,MAAuB;AAChE,MAAI,MAAM,SAAS,EAAG,QAAO,gBAAgB,MAAM,KAAK,IAAI,CAAC;AAC7D,SAAO,GAAG,OAAO,QAAQ,SAAS,eAAe,MAAM,CAAC,CAAC;AAC3D;AAEO,SAAS,UAAU,UAAwC;AAChE,QAAM,SAAS,SAAS,OAAO,CAAC,SAAS,KAAK,SAAS,OAAO,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;AACxF,QAAM,MAAM,SAAS,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;AACnF,MAAI,CAAC,IAAI,OAAQ,QAAO,OAAO,KAAK,IAAI;AACxC,MAAI,CAAC,OAAO,OAAQ,QAAO,QAAQ,KAAK,IAAI;AAC5C,SAAO,GAAG,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,SAAS,IAAI,OAAO,IAAI,GAAG,QAAQ,KAAK,KAAK,CAAC;AAClF;AAEO,SAAS,YAAY,UAAwC;AAClE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,OAAO;AAAA,IACP,MAAM,+CAA0C,UAAU,QAAQ,CAAC;AAAA,IACnE,SAAS,CAAC;AAAA,EACZ;AACF;AAIO,MAAM,uBAAuB,QAA+D;AAAA,EACxF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,UAAU;AAAA,EAEV,YAAY,UAAyB,QAAgB,UAAmB,MAAoB;AAC1F,UAAM;AACN,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,YAAY,WAAW,CAAC,eAAe,QAAQ,CAAC,IAAI,CAAC;AAC1D,SAAK,QAAQ;AACb,aAAS,MAAM,UAAU,MAAM,KAAK,KAAK,WAAW,KAAK,KAAK,CAAC,CAAC;AAChE,UAAM,GAAG,WAAW,MAAM,KAAK,KAAK,WAAW,KAAK,KAAK,CAAC,CAAC;AAAA,EAC7D;AAAA,EAEA,OAA0B;AACxB,WAAO,CAAC,GAAI,KAAK,OAAO,KAAK,KAAK,CAAC,GAAI,GAAG,KAAK,OAAO,CAAC;AAAA,EACzD;AAAA,EAEA,SAA4B;AAC1B,UAAM,QAAQ,KAAK,UAAU,MAAM,OAAO;AAC1C,WAAO,CAAC,GAAG,KAAK,WAAW,GAAI,OAAO,UAAU,CAAC,KAAK,UAAU,CAAC,YAAY,KAAK,CAAC,IAAI,CAAC,CAAE;AAAA,EAC5F;AAAA,EAEA,OAAO,IAAgC;AACrC,WAAO,KAAK,OAAO,KAAK,EAAE,KAAK,CAAC,WAAW,OAAO,OAAO,EAAE;AAAA,EAC7D;AAAA,EAEA,MAAM,IAAI,IAAY,QAA+B;AACnD,QAAI,KAAK,OAAO,EAAE,EAAG,OAAM,KAAK,OAAO,IAAI,IAAI,MAAM;AAAA,EACvD;AAAA,EAEA,QAAQ,IAAkB;AACxB,UAAM,SAAS,KAAK,OAAO,EAAE;AAC7B,QAAI,QAAQ;AACV,UAAI,OAAO,gBAAgB,MAAO,MAAK,OAAO,QAAQ,EAAE;AACxD;AAAA,IACF;AACA,QAAI,OAAO,WAAY;AACvB,SAAK,UAAU;AACf,SAAK,KAAK,WAAW,KAAK,KAAK,CAAC;AAChC,YAAQ,KAAK,UAAU,aAAa,kBAAkB,GAAG,sBAAsB,KAAK,OAAO;AAAA,EAC7F;AACF;",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ import { AgentAdapter } from './agent-port.ts';
4
4
  import { ChangesAdapter } from './changes-port.ts';
5
5
  import { MemoryAdapter } from './memory-port.ts';
6
6
  import { MonitorAdapter } from './monitor-port.ts';
7
- import { NoticesAdapter } from './notices-port.ts';
7
+ import { type HostNotices, NoticesAdapter } from './notices-port.ts';
8
8
  import { PageAdapter, type PageOptions } from './page-port.ts';
9
9
  import { type Login, SettingsAdapter } from './settings-port.ts';
10
10
  import { SprinkleAdapter } from './sprinkle-port.ts';
@@ -14,7 +14,7 @@ export { ChangesAdapter, type ChangesEvents } from './changes-port.ts';
14
14
  export { MemoryAdapter } from './memory-port.ts';
15
15
  export { assistant, errorAction, errorPart, isBusy, queued, textOf, toMessages, } from './messages.ts';
16
16
  export { MonitorAdapter, type MonitorAdapterOptions, type MonitorInput, monitorSnapshot, } from './monitor-port.ts';
17
- export { KEYS_MOVED, MISMATCH, mismatchBanner, movedList, movedNotice, NoticesAdapter, } from './notices-port.ts';
17
+ export { type HostNotices, KEYS_MOVED, MISMATCH, mismatchBanner, movedList, movedNotice, NoticesAdapter, } from './notices-port.ts';
18
18
  export { PageAdapter, type PageDocument, type PageOptions, type PageRun, type PageTarget, } from './page-port.ts';
19
19
  export { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';
20
20
  export { SprinkleAdapter } from './sprinkle-port.ts';
@@ -26,6 +26,7 @@ export declare function createAgentModel(connection: AgentConnection, options?:
26
26
  extra?: () => readonly MonitorSection[];
27
27
  };
28
28
  page?: PageOptions;
29
+ notices?: HostNotices;
29
30
  }): {
30
31
  agent: AgentAdapter;
31
32
  settings: SettingsAdapter;
@@ -8,12 +8,13 @@ export declare const MISMATCH = "agent-mismatch";
8
8
  export declare function mismatchBanner(text: string): Notice;
9
9
  export declare function movedList(accounts: readonly MovedName[]): string;
10
10
  export declare function movedNotice(accounts: readonly MovedName[]): Notice;
11
+ export type HostNotices = Pick<NoticesPort, 'list' | 'act' | 'dismiss' | 'on'>;
11
12
  export declare class NoticesAdapter extends Emitter<{
12
13
  notices: readonly Notice[];
13
14
  }> implements NoticesPort {
14
15
  #private;
15
- constructor(settings: AgentSettings, report: Report, mismatch?: string);
16
+ constructor(settings: AgentSettings, report: Report, mismatch?: string, host?: HostNotices);
16
17
  list(): readonly Notice[];
17
- act(): Promise<void>;
18
+ act(id: string, action: string): Promise<void>;
18
19
  dismiss(id: string): void;
19
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -64,7 +64,7 @@
64
64
  "@ai-ecoverse/slicc-kernel": "1.94.0",
65
65
  "@ai-ecoverse/slicc-scrub": "1.0.0",
66
66
  "@ai-ecoverse/slicc-shared-web": "1.20.0",
67
- "@ai-ecoverse/slicc-spectrum": "1.76.0",
67
+ "@ai-ecoverse/slicc-spectrum": "1.77.1",
68
68
  "@ai-ecoverse/wasm-bash": "5.3.0-18",
69
69
  "@ai-ecoverse/wasm-coreutils": "9.12.0-6",
70
70
  "@ai-ecoverse/wasm-git": "2.55.0-15",
@@ -91,7 +91,7 @@
91
91
  "typebox": "1.3.36"
92
92
  },
93
93
  "peerDependencies": {
94
- "@ai-ecoverse/slicc-kernel": ">=1.81.0 <2",
94
+ "@ai-ecoverse/slicc-kernel": ">=1.90.0 <2",
95
95
  "@ai-ecoverse/slicc-spectrum": "^1.75.0"
96
96
  },
97
97
  "peerDependenciesMeta": {