@ai-ecoverse/slicc-agent 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -76,6 +76,8 @@ const owner = await startAgent({
76
76
 
77
77
  **Providers.** 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 stored credential names another region (`AWS_REGION`, set by `connect`'s `region`). The default applies when the credential is read, so keys stored earlier get it too. The settings UI has no region field yet. Bedrock sends no CORS headers, so it needs a CORS-free transport, and its account says so (`needs: 'cors-free-transport'`).
78
78
 
79
+ **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 carries an `X-Session-Id` that is fixed for the worker. 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 passes the token to `connect`, so `createAgentModel(connection, { login })` takes a `login(providerId, signIn)` that returns a token. 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 offers **Log in**.
80
+
79
81
  **Credentials.** They live in the IndexedDB database `slicc-agent-credentials`, sealed with AES-GCM under a non-extractable key that never leaves WebCrypto. They never enter the session database or the transcript, and followers never see them.
80
82
 
81
83
  **Settings.** The `slicc.agent.settings` service shows the models and accounts and takes `connect(providerId, apiKey)` and `disconnect(providerId)`.
@@ -93,7 +95,7 @@ app.model = { ...createKernelModel({ kernel, root }), ...createAgentModel(agent,
93
95
 
94
96
  `createAgentModel()` returns the `agent` and `settings` ports. Settings keeps the UI preferences in `storage`; model and thinking belong to the conversation, and accounts and models come from the worker.
95
97
 
96
- The adapter keeps the replicated transcript and maps it on every update. User, assistant and compaction entries become messages; tool results fill in the tool calls they answer; the generation in flight shows as a streaming message; retries and running compactions show as system messages. A failed generation ends in an error card whose action fits the error: credential errors (an invalid or missing key, 401/403, not authorized) open settings, a model the account can't use offers the model picker, and anything else offers Retry. `busy()` and `queue()` read durable's live and inbox documents. Steering is the default (#4): a send or a steer goes out with `whenBusy: 'steer'`, so while a run is going it joins after the current tool round, and a `queue` send is a follow-up. User messages carry `delivered` from the host's deliveries; entries the host didn't record (from before a reload) count as steered when they follow a tool result. There is one cone so far; scoops, the freezer, licks and questions come later. slicc-spectrum is an optional peer dependency (≥ 1.7.0), used for types only, so package managers don't install it next to the agent.
98
+ The adapter keeps the replicated transcript and maps it on every update. User, assistant and compaction entries become messages; tool results fill in the tool calls they answer; the generation in flight shows as a streaming message; retries and running compactions show as system messages. A failed generation ends in an error card whose action fits the error: credential errors (an invalid or missing key, 401/403, not authorized) open settings, a model the account can't use offers the model picker, and anything else offers Retry. Credential and model errors lead with a plain sentence ("Bedrock rejected the API key.", "This model isn't available with the current account.") and keep the provider's raw error as `detail`, which spectrum (≥ 1.11.0) shows underneath. `busy()` and `queue()` read durable's live and inbox documents. Steering is the default (#4): a send or a steer goes out with `whenBusy: 'steer'`, so while a run is going it joins after the current tool round, and a `queue` send is a follow-up. User messages carry `delivered` from the host's deliveries; entries the host didn't record (from before a reload) count as steered when they follow a tool result. There is one cone so far; scoops, the freezer, licks and questions come later. slicc-spectrum is an optional peer dependency (≥ 1.7.0), used for types only, so package managers don't install it next to the agent.
97
99
 
98
100
  ## Patched dependencies
99
101
 
package/dist/adobe.js ADDED
@@ -0,0 +1,83 @@
1
+ import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
2
+ import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
3
+ import { createProvider } from "@earendil-works/pi-ai/models";
4
+ const ADOBE = "adobe";
5
+ const ADOBE_PROXY = "https://adobe-llm-proxy.paolo-moz.workers.dev";
6
+ const free = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
7
+ function adobeModel(info, endpoint) {
8
+ const openai = info.api === "openai";
9
+ return {
10
+ id: info.id,
11
+ name: info.name,
12
+ provider: ADOBE,
13
+ api: openai ? "openai-completions" : "anthropic-messages",
14
+ baseUrl: openai ? `${endpoint}/v1` : endpoint,
15
+ reasoning: info.reasoning ?? !openai,
16
+ input: (info.input ?? ["text"]).filter(
17
+ (kind) => kind === "text" || kind === "image"
18
+ ),
19
+ cost: info.cost ?? free,
20
+ contextWindow: info.context_window ?? 2e5,
21
+ maxTokens: info.max_tokens ?? 8192,
22
+ ...openai ? { compat: { supportsStore: false, supportsDeveloperRole: false } } : {}
23
+ };
24
+ }
25
+ async function adobeConfig(endpoint, fetcher) {
26
+ const response = await fetcher(`${endpoint}/v1/config`, {
27
+ headers: { Accept: "application/json" }
28
+ });
29
+ if (!response.ok) throw new Error(`the Adobe proxy answered /v1/config with ${response.status}`);
30
+ return await response.json();
31
+ }
32
+ async function adobeUsage(token, {
33
+ endpoint = ADOBE_PROXY,
34
+ fetch: fetcher = globalThis.fetch
35
+ } = {}) {
36
+ const response = await fetcher(`${endpoint}/v1/usage`, {
37
+ headers: { Accept: "application/json", Authorization: `Bearer ${token}` }
38
+ });
39
+ if (!response.ok) return null;
40
+ const weekly = (await response.json()).usage?.weekly;
41
+ if (typeof weekly?.percent !== "number" || typeof weekly.resetsAt !== "string") return null;
42
+ return { percent: weekly.percent, window: "weekly", resets: weekly.resetsAt };
43
+ }
44
+ function adobeProvider({
45
+ endpoint = ADOBE_PROXY,
46
+ session = crypto.randomUUID(),
47
+ fetch: fetcher = (input, init) => globalThis.fetch(input, init)
48
+ } = {}) {
49
+ return createProvider({
50
+ id: ADOBE,
51
+ name: "Adobe",
52
+ baseUrl: endpoint,
53
+ headers: { "X-Session-Id": session },
54
+ auth: {
55
+ apiKey: {
56
+ name: "Adobe IMS",
57
+ resolve: async ({ credential, signal }) => {
58
+ signal.throwIfAborted();
59
+ if (!credential?.key) return void 0;
60
+ return {
61
+ auth: { headers: { Authorization: `Bearer ${credential.key}` } },
62
+ source: "Adobe IMS"
63
+ };
64
+ }
65
+ }
66
+ },
67
+ models: [],
68
+ fetchModels: async () => (await adobeConfig(endpoint, fetcher)).models.filter((info) => !info.hidden).map((info) => adobeModel(info, endpoint)),
69
+ api: {
70
+ "anthropic-messages": anthropicMessagesApi(),
71
+ "openai-completions": openAICompletionsApi()
72
+ }
73
+ });
74
+ }
75
+ export {
76
+ ADOBE,
77
+ ADOBE_PROXY,
78
+ adobeConfig,
79
+ adobeModel,
80
+ adobeProvider,
81
+ adobeUsage
82
+ };
83
+ //# sourceMappingURL=adobe.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/adobe.ts"],
4
+ "sourcesContent": ["import type { Provider } from '@earendil-works/pi-ai';\nimport { anthropicMessagesApi } from '@earendil-works/pi-ai/api/anthropic-messages.lazy';\nimport { openAICompletionsApi } from '@earendil-works/pi-ai/api/openai-completions.lazy';\nimport { createProvider } from '@earendil-works/pi-ai/models';\n\nexport const ADOBE = 'adobe';\nexport const ADOBE_PROXY = 'https://adobe-llm-proxy.paolo-moz.workers.dev';\n\nexport interface AdobeModelInfo {\n id: string;\n name: string;\n api?: 'anthropic' | 'openai';\n context_window?: number;\n max_tokens?: number;\n reasoning?: boolean;\n input?: string[];\n cost?: { input: number; output: number; cacheRead: number; cacheWrite: number };\n hidden?: boolean;\n}\n\nexport interface AdobeConfig {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n models: AdobeModelInfo[];\n}\n\nexport interface Budget {\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\ntype Fetch = typeof fetch;\n\nconst free = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };\n\nexport function adobeModel(info: AdobeModelInfo, endpoint: string) {\n const openai = info.api === 'openai';\n return {\n id: info.id,\n name: info.name,\n provider: ADOBE,\n api: openai ? ('openai-completions' as const) : ('anthropic-messages' as const),\n baseUrl: openai ? `${endpoint}/v1` : endpoint,\n reasoning: info.reasoning ?? !openai,\n input: (info.input ?? ['text']).filter(\n (kind): kind is 'text' | 'image' => kind === 'text' || kind === 'image'\n ),\n cost: info.cost ?? free,\n contextWindow: info.context_window ?? 200000,\n maxTokens: info.max_tokens ?? 8192,\n ...(openai ? { compat: { supportsStore: false, supportsDeveloperRole: false } } : {}),\n };\n}\n\nexport async function adobeConfig(endpoint: string, fetcher: Fetch): Promise<AdobeConfig> {\n const response = await fetcher(`${endpoint}/v1/config`, {\n headers: { Accept: 'application/json' },\n });\n if (!response.ok) throw new Error(`the Adobe proxy answered /v1/config with ${response.status}`);\n return (await response.json()) as AdobeConfig;\n}\n\nexport async function adobeUsage(\n token: string,\n {\n endpoint = ADOBE_PROXY,\n fetch: fetcher = globalThis.fetch,\n }: { endpoint?: string; fetch?: Fetch } = {}\n): Promise<Budget | null> {\n const response = await fetcher(`${endpoint}/v1/usage`, {\n headers: { Accept: 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!response.ok) return null;\n const weekly = (\n (await response.json()) as { usage?: { weekly?: { percent?: number; resetsAt?: string } } }\n ).usage?.weekly;\n if (typeof weekly?.percent !== 'number' || typeof weekly.resetsAt !== 'string') return null;\n return { percent: weekly.percent, window: 'weekly', resets: weekly.resetsAt };\n}\n\nexport function adobeProvider({\n endpoint = ADOBE_PROXY,\n session = crypto.randomUUID(),\n fetch: fetcher = (input, init) => globalThis.fetch(input, init),\n}: {\n endpoint?: string;\n session?: string;\n fetch?: Fetch;\n} = {}): Provider {\n return createProvider({\n id: ADOBE,\n name: 'Adobe',\n baseUrl: endpoint,\n headers: { 'X-Session-Id': session },\n auth: {\n apiKey: {\n name: 'Adobe IMS',\n resolve: async ({ credential, signal }) => {\n signal.throwIfAborted();\n if (!credential?.key) return undefined;\n return {\n auth: { headers: { Authorization: `Bearer ${credential.key}` } },\n source: 'Adobe IMS',\n };\n },\n },\n },\n models: [],\n fetchModels: async () =>\n (await adobeConfig(endpoint, fetcher)).models\n .filter((info) => !info.hidden)\n .map((info) => adobeModel(info, endpoint)),\n api: {\n 'anthropic-messages': anthropicMessagesApi(),\n 'openai-completions': openAICompletionsApi(),\n },\n }) as Provider;\n}\n"],
5
+ "mappings": "AACA,SAAS,4BAA4B;AACrC,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAExB,MAAM,QAAQ;AACd,MAAM,cAAc;AA6B3B,MAAM,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,EAAE;AAEzD,SAAS,WAAW,MAAsB,UAAkB;AACjE,QAAM,SAAS,KAAK,QAAQ;AAC5B,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,IACX,UAAU;AAAA,IACV,KAAK,SAAU,uBAAkC;AAAA,IACjD,SAAS,SAAS,GAAG,QAAQ,QAAQ;AAAA,IACrC,WAAW,KAAK,aAAa,CAAC;AAAA,IAC9B,QAAQ,KAAK,SAAS,CAAC,MAAM,GAAG;AAAA,MAC9B,CAAC,SAAmC,SAAS,UAAU,SAAS;AAAA,IAClE;AAAA,IACA,MAAM,KAAK,QAAQ;AAAA,IACnB,eAAe,KAAK,kBAAkB;AAAA,IACtC,WAAW,KAAK,cAAc;AAAA,IAC9B,GAAI,SAAS,EAAE,QAAQ,EAAE,eAAe,OAAO,uBAAuB,MAAM,EAAE,IAAI,CAAC;AAAA,EACrF;AACF;AAEA,eAAsB,YAAY,UAAkB,SAAsC;AACxF,QAAM,WAAW,MAAM,QAAQ,GAAG,QAAQ,cAAc;AAAA,IACtD,SAAS,EAAE,QAAQ,mBAAmB;AAAA,EACxC,CAAC;AACD,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,4CAA4C,SAAS,MAAM,EAAE;AAC/F,SAAQ,MAAM,SAAS,KAAK;AAC9B;AAEA,eAAsB,WACpB,OACA;AAAA,EACE,WAAW;AAAA,EACX,OAAO,UAAU,WAAW;AAC9B,IAA0C,CAAC,GACnB;AACxB,QAAM,WAAW,MAAM,QAAQ,GAAG,QAAQ,aAAa;AAAA,IACrD,SAAS,EAAE,QAAQ,oBAAoB,eAAe,UAAU,KAAK,GAAG;AAAA,EAC1E,CAAC;AACD,MAAI,CAAC,SAAS,GAAI,QAAO;AACzB,QAAM,UACH,MAAM,SAAS,KAAK,GACrB,OAAO;AACT,MAAI,OAAO,QAAQ,YAAY,YAAY,OAAO,OAAO,aAAa,SAAU,QAAO;AACvF,SAAO,EAAE,SAAS,OAAO,SAAS,QAAQ,UAAU,QAAQ,OAAO,SAAS;AAC9E;AAEO,SAAS,cAAc;AAAA,EAC5B,WAAW;AAAA,EACX,UAAU,OAAO,WAAW;AAAA,EAC5B,OAAO,UAAU,CAAC,OAAO,SAAS,WAAW,MAAM,OAAO,IAAI;AAChE,IAII,CAAC,GAAa;AAChB,SAAO,eAAe;AAAA,IACpB,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS,EAAE,gBAAgB,QAAQ;AAAA,IACnC,MAAM;AAAA,MACJ,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,SAAS,OAAO,EAAE,YAAY,OAAO,MAAM;AACzC,iBAAO,eAAe;AACtB,cAAI,CAAC,YAAY,IAAK,QAAO;AAC7B,iBAAO;AAAA,YACL,MAAM,EAAE,SAAS,EAAE,eAAe,UAAU,WAAW,GAAG,GAAG,EAAE;AAAA,YAC/D,QAAQ;AAAA,UACV;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,aAAa,aACV,MAAM,YAAY,UAAU,OAAO,GAAG,OACpC,OAAO,CAAC,SAAS,CAAC,KAAK,MAAM,EAC7B,IAAI,CAAC,SAAS,WAAW,MAAM,QAAQ,CAAC;AAAA,IAC7C,KAAK;AAAA,MACH,sBAAsB,qBAAqB;AAAA,MAC3C,sBAAsB,qBAAqB;AAAA,IAC7C;AAAA,EACF,CAAC;AACH;",
6
+ "names": []
7
+ }
package/dist/host.js CHANGED
@@ -57,7 +57,8 @@ const noSettings = {
57
57
  throw new Error("this agent has no accounts");
58
58
  },
59
59
  disconnect: async () => {
60
- }
60
+ },
61
+ signIn: async () => null
61
62
  };
62
63
  async function hostAgent(agent, options = {}) {
63
64
  const context = options.context ?? BACKGROUND_CONTEXT;
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 createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n} from './services.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\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};\n\nexport async function hostAgent(\n agent: Agent,\n options: { serverId?: string; context?: Context; settings?: AgentSettings } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const state = await agent.root.viewState(context);\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(AgentControl, createAgentControl(agent.harness, agent.root, deliveries));\n provider.provide(AgentTranscript, { state, deliveries: deliveries.state });\n provider.provide(AgentSettings, options.settings ?? noSettings);\n const host: ServerHost = {\n serverServices: serverServices(),\n async resolveSession(sessionId) {\n if (sessionId !== AGENT_SESSION)\n throw new SessionNotFoundError(`unknown session ${sessionId}`);\n return { id: sessionId };\n },\n openSession: async () => sessionServices(provider),\n };\n const listener = new PortListener(serverId);\n const server = await new Server(host, { serverId, listeners: [listener] }).start();\n return {\n serverId,\n agent,\n connect: (endpoint) => listener.connect(endpoint),\n async close() {\n await server.close();\n provider.dispose();\n deliveries.dispose();\n state.dispose();\n await agent.close();\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n} from './services.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\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 signIn: async () => null,\n};\n\nexport async function hostAgent(\n agent: Agent,\n options: { serverId?: string; context?: Context; settings?: AgentSettings } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const state = await agent.root.viewState(context);\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(AgentControl, createAgentControl(agent.harness, agent.root, deliveries));\n provider.provide(AgentTranscript, { state, deliveries: deliveries.state });\n provider.provide(AgentSettings, options.settings ?? noSettings);\n const host: ServerHost = {\n serverServices: serverServices(),\n async resolveSession(sessionId) {\n if (sessionId !== AGENT_SESSION)\n throw new SessionNotFoundError(`unknown session ${sessionId}`);\n return { id: sessionId };\n },\n openSession: async () => sessionServices(provider),\n };\n const listener = new PortListener(serverId);\n const server = await new Server(host, { serverId, listeners: [listener] }).start();\n return {\n serverId,\n agent,\n connect: (endpoint) => listener.connect(endpoint),\n async close() {\n await server.close();\n provider.dispose();\n deliveries.dispose();\n state.dispose();\n await agent.close();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.js CHANGED
@@ -1,3 +1,11 @@
1
+ import {
2
+ ADOBE,
3
+ ADOBE_PROXY,
4
+ adobeConfig,
5
+ adobeModel,
6
+ adobeProvider,
7
+ adobeUsage
8
+ } from "./adobe.js";
1
9
  import { answerText, openAgent } from "./agent.js";
2
10
  import { connectAgent } from "./client.js";
3
11
  import { createAgentControl } from "./control.js";
@@ -40,6 +48,8 @@ import {
40
48
  import { PortChannel, withUnrefTimers } from "./wire.js";
41
49
  import { kernelPort, serveConnections } from "./worker.js";
42
50
  export {
51
+ ADOBE,
52
+ ADOBE_PROXY,
43
53
  AGENT_DIRECTORY,
44
54
  AGENT_LOCK,
45
55
  AGENT_SESSION,
@@ -58,6 +68,10 @@ export {
58
68
  SliccKernelEnv,
59
69
  SliccPrompt,
60
70
  WasmSqliteDatabase,
71
+ adobeConfig,
72
+ adobeModel,
73
+ adobeProvider,
74
+ adobeUsage,
61
75
  answerText,
62
76
  applyPatches,
63
77
  connectAgent,
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport { type AgentConnection, connectAgent } from './client.ts';\nexport { createAgentControl } from './control.ts';\nexport { CREDENTIALS_DATABASE, EncryptedCredentialStore } from './credentials.ts';\nexport { type AgentHost, hostAgent } from './host.ts';\nexport type {\n KernelClient,\n KernelFs,\n KernelProcess,\n KernelStat,\n SpawnOptions,\n} from './kernel/client.ts';\nexport { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nexport { PortListener } from './listener.ts';\nexport {\n type AgentWorkerOptions,\n type AgentWorkerScope,\n type KernelAttach,\n runAgentWorker,\n} from './main.ts';\nexport { type Transport, type TransportResponse, transportFetch } from './net.ts';\nexport {\n AGENT_LOCK,\n type AgentOwner,\n type AgentWorker,\n type OwnerOptions,\n startAgent,\n} from './owner.ts';\nexport { applyPatches, patchServerAccept } from './patches.ts';\nexport { SliccPrompt } from './prompt.ts';\nexport {\n type AccountState,\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n type AgentSettingsChange,\n AgentTranscript,\n type ModelChoice,\n type OperationError,\n type PromptResult,\n type SendMode,\n type SendRequest,\n type SendResponse,\n type SettingsState,\n} from './services.ts';\nexport {\n BEDROCK,\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n DEFAULT_REGIONS,\n sliccProviders,\n withDefaultRegions,\n} from './settings.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\nexport { PortChannel, type PortEndpoint, withUnrefTimers } from './wire.ts';\nexport { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n"],
5
- "mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE,SAA+B,oBAAoB;AACnD,SAAS,0BAA0B;AACnC,SAAS,sBAAsB,gCAAgC;AAC/D,SAAyB,iBAAiB;AAQ1C,SAAS,MAA6B,mBAAmB,sBAAsB;AAC/E,SAAS,oBAAoB;AAC7B;AAAA,EAIE;AAAA,OACK;AACP,SAAiD,sBAAsB;AACvE;AAAA,EACE;AAAA,EAIA;AAAA,OACK;AACP,SAAS,cAAc,yBAAyB;AAChD,SAAS,mBAAmB;AAC5B;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAQK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAgC,uBAAuB;AAChE,SAAS,YAAY,wBAA0C;",
4
+ "sourcesContent": ["export {\n ADOBE,\n ADOBE_PROXY,\n type AdobeConfig,\n type AdobeModelInfo,\n adobeConfig,\n adobeModel,\n adobeProvider,\n adobeUsage,\n type Budget,\n} from './adobe.ts';\nexport { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport { type AgentConnection, connectAgent } from './client.ts';\nexport { createAgentControl } from './control.ts';\nexport { CREDENTIALS_DATABASE, EncryptedCredentialStore } from './credentials.ts';\nexport { type AgentHost, hostAgent } from './host.ts';\nexport type {\n KernelClient,\n KernelFs,\n KernelProcess,\n KernelStat,\n SpawnOptions,\n} from './kernel/client.ts';\nexport { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nexport { PortListener } from './listener.ts';\nexport {\n type AgentWorkerOptions,\n type AgentWorkerScope,\n type KernelAttach,\n runAgentWorker,\n} from './main.ts';\nexport { type Transport, type TransportResponse, transportFetch } from './net.ts';\nexport {\n AGENT_LOCK,\n type AgentOwner,\n type AgentWorker,\n type OwnerOptions,\n startAgent,\n} from './owner.ts';\nexport { applyPatches, patchServerAccept } from './patches.ts';\nexport { SliccPrompt } from './prompt.ts';\nexport {\n type AccountState,\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n type AgentSettingsChange,\n AgentTranscript,\n type BudgetState,\n type ModelChoice,\n type OperationError,\n type PromptResult,\n type SendMode,\n type SendRequest,\n type SendResponse,\n type SettingsState,\n type SignIn,\n} from './services.ts';\nexport {\n BEDROCK,\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n DEFAULT_REGIONS,\n sliccProviders,\n withDefaultRegions,\n} from './settings.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\nexport { PortChannel, type PortEndpoint, withUnrefTimers } from './wire.ts';\nexport { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAwC,YAAY,iBAAiB;AACrE,SAA+B,oBAAoB;AACnD,SAAS,0BAA0B;AACnC,SAAS,sBAAsB,gCAAgC;AAC/D,SAAyB,iBAAiB;AAQ1C,SAAS,MAA6B,mBAAmB,sBAAsB;AAC/E,SAAS,oBAAoB;AAC7B;AAAA,EAIE;AAAA,OACK;AACP,SAAiD,sBAAsB;AACvE;AAAA,EACE;AAAA,EAIA;AAAA,OACK;AACP,SAAS,cAAc,yBAAyB;AAChD,SAAS,mBAAmB;AAC5B;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAUK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAgC,uBAAuB;AAChE,SAAS,YAAY,wBAA0C;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/services.ts"],
4
- "sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n}\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected';\n auth: 'api-key';\n needs: 'cors-free-transport' | null;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
5
- "mappings": "AAAA,SAAuB,qBAA2C;AA0C3D,MAAM,eAAe,cAA4B,qBAAqB;AAStE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,gBAAgB;AAoCtB,MAAM,gBAAgB,cAA6B,sBAAsB;",
4
+ "sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n}\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
5
+ "mappings": "AAAA,SAAuB,qBAA2C;AA0C3D,MAAM,eAAe,cAA4B,qBAAqB;AAStE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,gBAAgB;AAmDtB,MAAM,gBAAgB,cAA6B,sBAAsB;",
6
6
  "names": []
7
7
  }
package/dist/settings.js CHANGED
@@ -1,12 +1,22 @@
1
1
  import { replicatedState } from "@earendil-works/chord";
2
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
3
  import { createModels } from "@earendil-works/pi-ai/models";
3
4
  import { amazonBedrockProvider } from "@earendil-works/pi-ai/providers/amazon-bedrock";
5
+ import {
6
+ ADOBE,
7
+ ADOBE_PROXY,
8
+ adobeConfig,
9
+ adobeProvider,
10
+ adobeUsage
11
+ } from "./adobe.js";
4
12
  const BEDROCK = "amazon-bedrock";
5
13
  const DEFAULT_MODEL = { provider: BEDROCK, modelId: "us.anthropic.claude-sonnet-5-5" };
6
14
  const DEFAULT_REGIONS = { [BEDROCK]: "us-west-2" };
7
- const corsFree = /* @__PURE__ */ new Set([BEDROCK]);
15
+ const corsFree = /* @__PURE__ */ new Set([BEDROCK, ADOBE]);
16
+ const signIn = /* @__PURE__ */ new Set([ADOBE]);
17
+ const BUDGET_EVERY = 5 * 60 * 1e3;
8
18
  function sliccProviders() {
9
- return [amazonBedrockProvider()];
19
+ return [amazonBedrockProvider(), adobeProvider()];
10
20
  }
11
21
  function withDefaultRegions(credentials) {
12
22
  return {
@@ -45,23 +55,44 @@ async function accounts(credentials, providers) {
45
55
  return providers.map((provider) => ({
46
56
  id: provider.id,
47
57
  provider: provider.name ?? provider.id,
48
- identity: stored.has(provider.id) ? "API key" : "",
58
+ identity: stored.has(provider.id) ? signIn.has(provider.id) ? "Adobe ID" : "API key" : "",
49
59
  status: stored.has(provider.id) ? "connected" : "disconnected",
50
- auth: "api-key",
60
+ auth: signIn.has(provider.id) ? "oauth" : "api-key",
51
61
  needs: corsFree.has(provider.id) ? "cors-free-transport" : null
52
62
  }));
53
63
  }
54
- async function createAgentSettings(models, credentials, providers = sliccProviders()) {
55
- const state = replicatedState({
64
+ async function budget(credentials, usage) {
65
+ const credential = await credentials.read(ADOBE);
66
+ const key = credential?.type === "api_key" ? credential.key : void 0;
67
+ const found = key ? await usage(key).catch(() => null) : null;
68
+ return found ? { provider: ADOBE, ...found } : null;
69
+ }
70
+ async function createAgentSettings(models, credentials, providers = sliccProviders(), options = {}) {
71
+ const endpoint = providers.find((provider) => provider.id === ADOBE)?.baseUrl ?? ADOBE_PROXY;
72
+ const usage = options.usage ?? ((token) => adobeUsage(token, { endpoint }));
73
+ const snapshot = async () => ({
56
74
  models: choices(models, providers),
57
- accounts: await accounts(credentials, providers)
75
+ accounts: await accounts(credentials, providers),
76
+ budget: await budget(credentials, usage)
58
77
  });
59
- const refresh = async (context) => {
60
- state.replace(context, {
61
- models: choices(models, providers),
62
- accounts: await accounts(credentials, providers)
63
- });
78
+ const state = replicatedState(await snapshot());
79
+ let latest = Promise.resolve();
80
+ const refresh = (context) => {
81
+ const next = latest.then(async () => state.replace(context, await snapshot()));
82
+ latest = next.catch(() => void 0);
83
+ return next;
64
84
  };
85
+ const signedIn = providers.filter((provider) => signIn.has(provider.id)).map((provider) => provider.id);
86
+ const discover = (ids) => models.refresh({ providers: ids }).then(
87
+ () => void 0,
88
+ () => void 0
89
+ );
90
+ if (signedIn.length) {
91
+ const cycle = () => void discover(signedIn).then(() => refresh(BACKGROUND_CONTEXT)).catch(() => void 0);
92
+ cycle();
93
+ const timer = setInterval(cycle, options.every ?? BUDGET_EVERY);
94
+ timer.unref?.();
95
+ }
65
96
  return {
66
97
  state,
67
98
  async connect(providerId, secret, region, context) {
@@ -70,8 +101,17 @@ async function createAgentSettings(models, credentials, providers = sliccProvide
70
101
  key: secret,
71
102
  ...region ? { env: { AWS_REGION: region } } : {}
72
103
  }));
104
+ if (signIn.has(providerId)) await discover([providerId]);
73
105
  await refresh(context);
74
106
  },
107
+ async signIn(providerId) {
108
+ if (providerId !== ADOBE) return null;
109
+ const { clientId, scopes, imsEnvironment } = await adobeConfig(
110
+ endpoint,
111
+ (input, init) => globalThis.fetch(input, init)
112
+ );
113
+ return { clientId, scopes, imsEnvironment: imsEnvironment || "prod" };
114
+ },
75
115
  async disconnect(providerId, context) {
76
116
  await credentials.delete(providerId);
77
117
  await refresh(context);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/settings.ts"],
4
- "sourcesContent": ["import { type Context, replicatedState } from '@earendil-works/chord';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport { createModels, type MutableModels } from '@earendil-works/pi-ai/models';\nimport { amazonBedrockProvider } from '@earendil-works/pi-ai/providers/amazon-bedrock';\nimport type { AccountState, AgentSettings, ModelChoice, SettingsState } from './services.ts';\n\nexport const BEDROCK = 'amazon-bedrock';\nexport const DEFAULT_MODEL = { provider: BEDROCK, modelId: 'us.anthropic.claude-sonnet-5-5' };\nexport const DEFAULT_REGIONS: Readonly<Record<string, string>> = { [BEDROCK]: 'us-west-2' };\n\nconst corsFree = new Set([BEDROCK]);\n\nexport function sliccProviders(): Provider[] {\n return [amazonBedrockProvider() as Provider];\n}\n\nexport function withDefaultRegions(credentials: CredentialStore): CredentialStore {\n return {\n async read(providerId, options) {\n const credential = await credentials.read(providerId, options);\n const region = DEFAULT_REGIONS[providerId];\n if (credential?.type !== 'api_key' || !region || credential.env?.AWS_REGION) {\n return credential;\n }\n return { ...credential, env: { ...credential.env, AWS_REGION: region } };\n },\n list: (options) => credentials.list(options),\n modify: (providerId, fn, options) => credentials.modify(providerId, fn, options),\n delete: (providerId, options) => credentials.delete(providerId, options),\n };\n}\n\nexport function createSliccModels(\n credentials: CredentialStore,\n providers = sliccProviders()\n): MutableModels {\n const models = createModels({ credentials: withDefaultRegions(credentials) });\n for (const provider of providers) models.setProvider(provider);\n return models;\n}\n\nfunction choices(models: MutableModels, providers: Provider[]): ModelChoice[] {\n return providers.flatMap((provider) =>\n models.getModels(provider.id).map((model) => ({\n id: `${provider.id}/${model.id}`,\n label: model.name,\n provider: provider.name ?? provider.id,\n kind: 'chat' as const,\n reasoning: Boolean(model.reasoning),\n contextWindow: model.contextWindow,\n }))\n );\n}\n\nasync function accounts(\n credentials: CredentialStore,\n providers: Provider[]\n): Promise<AccountState[]> {\n const stored = new Set((await credentials.list()).map((info) => info.providerId));\n return providers.map((provider) => ({\n id: provider.id,\n provider: provider.name ?? provider.id,\n identity: stored.has(provider.id) ? 'API key' : '',\n status: stored.has(provider.id) ? 'connected' : 'disconnected',\n auth: 'api-key',\n needs: corsFree.has(provider.id) ? 'cors-free-transport' : null,\n }));\n}\n\nexport async function createAgentSettings(\n models: MutableModels,\n credentials: CredentialStore,\n providers = sliccProviders()\n): Promise<AgentSettings> {\n const state = replicatedState<SettingsState>({\n models: choices(models, providers),\n accounts: await accounts(credentials, providers),\n });\n const refresh = async (context: Context) => {\n state.replace(context, {\n models: choices(models, providers),\n accounts: await accounts(credentials, providers),\n });\n };\n return {\n state,\n async connect(providerId, secret, region, context) {\n await credentials.modify(providerId, async () => ({\n type: 'api_key',\n key: secret,\n ...(region ? { env: { AWS_REGION: region } } : {}),\n }));\n await refresh(context);\n },\n async disconnect(providerId, context) {\n await credentials.delete(providerId);\n await refresh(context);\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { type Context, replicatedState } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport { createModels, type MutableModels } from '@earendil-works/pi-ai/models';\nimport { amazonBedrockProvider } from '@earendil-works/pi-ai/providers/amazon-bedrock';\nimport {\n ADOBE,\n ADOBE_PROXY,\n adobeConfig,\n adobeProvider,\n adobeUsage,\n type Budget,\n} from './adobe.ts';\nimport type { AccountState, AgentSettings, ModelChoice, SettingsState } from './services.ts';\n\nexport const BEDROCK = 'amazon-bedrock';\nexport const DEFAULT_MODEL = { provider: BEDROCK, modelId: 'us.anthropic.claude-sonnet-5-5' };\nexport const DEFAULT_REGIONS: Readonly<Record<string, string>> = { [BEDROCK]: 'us-west-2' };\n\nconst corsFree = new Set([BEDROCK, ADOBE]);\nconst signIn = new Set([ADOBE]);\nconst BUDGET_EVERY = 5 * 60 * 1000;\n\nexport function sliccProviders(): Provider[] {\n return [amazonBedrockProvider() as Provider, adobeProvider()];\n}\n\nexport function withDefaultRegions(credentials: CredentialStore): CredentialStore {\n return {\n async read(providerId, options) {\n const credential = await credentials.read(providerId, options);\n const region = DEFAULT_REGIONS[providerId];\n if (credential?.type !== 'api_key' || !region || credential.env?.AWS_REGION) {\n return credential;\n }\n return { ...credential, env: { ...credential.env, AWS_REGION: region } };\n },\n list: (options) => credentials.list(options),\n modify: (providerId, fn, options) => credentials.modify(providerId, fn, options),\n delete: (providerId, options) => credentials.delete(providerId, options),\n };\n}\n\nexport function createSliccModels(\n credentials: CredentialStore,\n providers = sliccProviders()\n): MutableModels {\n const models = createModels({ credentials: withDefaultRegions(credentials) });\n for (const provider of providers) models.setProvider(provider);\n return models;\n}\n\nfunction choices(models: MutableModels, providers: Provider[]): ModelChoice[] {\n return providers.flatMap((provider) =>\n models.getModels(provider.id).map((model) => ({\n id: `${provider.id}/${model.id}`,\n label: model.name,\n provider: provider.name ?? provider.id,\n kind: 'chat' as const,\n reasoning: Boolean(model.reasoning),\n contextWindow: model.contextWindow,\n }))\n );\n}\n\nasync function accounts(\n credentials: CredentialStore,\n providers: Provider[]\n): Promise<AccountState[]> {\n const stored = new Set((await credentials.list()).map((info) => info.providerId));\n return providers.map((provider) => ({\n id: provider.id,\n provider: provider.name ?? provider.id,\n identity: stored.has(provider.id) ? (signIn.has(provider.id) ? 'Adobe ID' : 'API key') : '',\n status: stored.has(provider.id) ? 'connected' : 'disconnected',\n auth: signIn.has(provider.id) ? 'oauth' : 'api-key',\n needs: corsFree.has(provider.id) ? 'cors-free-transport' : null,\n }));\n}\n\nexport type UsageReader = (token: string) => Promise<Budget | null>;\n\nasync function budget(credentials: CredentialStore, usage: UsageReader) {\n const credential = await credentials.read(ADOBE);\n const key = credential?.type === 'api_key' ? credential.key : undefined;\n const found = key ? await usage(key).catch(() => null) : null;\n return found ? { provider: ADOBE, ...found } : null;\n}\n\nexport async function createAgentSettings(\n models: MutableModels,\n credentials: CredentialStore,\n providers = sliccProviders(),\n options: { usage?: UsageReader; every?: number } = {}\n): Promise<AgentSettings> {\n const endpoint = providers.find((provider) => provider.id === ADOBE)?.baseUrl ?? ADOBE_PROXY;\n const usage = options.usage ?? ((token: string) => adobeUsage(token, { endpoint }));\n const snapshot = async (): Promise<SettingsState> => ({\n models: choices(models, providers),\n accounts: await accounts(credentials, providers),\n budget: await budget(credentials, usage),\n });\n const state = replicatedState<SettingsState>(await snapshot());\n let latest = Promise.resolve();\n const refresh = (context: Context) => {\n const next = latest.then(async () => state.replace(context, await snapshot()));\n latest = next.catch(() => undefined);\n return next;\n };\n const signedIn = providers\n .filter((provider) => signIn.has(provider.id))\n .map((provider) => provider.id);\n const discover = (ids: string[]) =>\n models.refresh({ providers: ids }).then(\n () => undefined,\n () => undefined\n );\n if (signedIn.length) {\n const cycle = () =>\n void discover(signedIn)\n .then(() => refresh(BACKGROUND_CONTEXT))\n .catch(() => undefined);\n cycle();\n const timer: unknown = setInterval(cycle, options.every ?? BUDGET_EVERY);\n (timer as { unref?: () => void }).unref?.();\n }\n return {\n state,\n async connect(providerId, secret, region, context) {\n await credentials.modify(providerId, async () => ({\n type: 'api_key',\n key: secret,\n ...(region ? { env: { AWS_REGION: region } } : {}),\n }));\n if (signIn.has(providerId)) await discover([providerId]);\n await refresh(context);\n },\n async signIn(providerId) {\n if (providerId !== ADOBE) return null;\n const { clientId, scopes, imsEnvironment } = await adobeConfig(endpoint, (input, init) =>\n globalThis.fetch(input, init)\n );\n return { clientId, scopes, imsEnvironment: imsEnvironment || 'prod' };\n },\n async disconnect(providerId, context) {\n await credentials.delete(providerId);\n await refresh(context);\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,13 +1,29 @@
1
1
  import { AgentAdapter } from "./agent-port.js";
2
2
  import { SettingsAdapter } from "./settings-port.js";
3
+ import { TrayAdapter } from "./tray-port.js";
3
4
  import { AgentAdapter as AgentAdapter2, CONE } from "./agent-port.js";
4
- import { assistant, errorAction, isBusy, queued, textOf, toMessages } from "./messages.js";
5
+ import {
6
+ assistant,
7
+ errorAction,
8
+ errorPart,
9
+ isBusy,
10
+ queued,
11
+ textOf,
12
+ toMessages
13
+ } from "./messages.js";
5
14
  import { SETTINGS_KEY, SettingsAdapter as SettingsAdapter2 } from "./settings-port.js";
15
+ import { TrayAdapter as TrayAdapter2 } from "./tray-port.js";
6
16
  function createAgentModel(connection, options = {}) {
7
17
  const agent = new AgentAdapter(connection);
8
18
  return {
9
19
  agent,
10
- settings: new SettingsAdapter(connection.settings, agent, options.storage ?? null)
20
+ settings: new SettingsAdapter(
21
+ connection.settings,
22
+ agent,
23
+ options.storage ?? null,
24
+ options.login ?? null
25
+ ),
26
+ tray: new TrayAdapter(connection.settings)
11
27
  };
12
28
  }
13
29
  export {
@@ -15,9 +31,11 @@ export {
15
31
  CONE,
16
32
  SETTINGS_KEY,
17
33
  SettingsAdapter2 as SettingsAdapter,
34
+ TrayAdapter2 as TrayAdapter,
18
35
  assistant,
19
36
  createAgentModel,
20
37
  errorAction,
38
+ errorPart,
21
39
  isBusy,
22
40
  queued,
23
41
  textOf,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/index.ts"],
4
- "sourcesContent": ["import type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { SettingsAdapter } from './settings-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { assistant, errorAction, isBusy, queued, textOf, toMessages } from './messages.ts';\nexport { SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: { storage?: Pick<Storage, 'getItem' | 'setItem'> | null } = {}\n): { agent: AgentAdapter; settings: SettingsAdapter } {\n const agent = new AgentAdapter(connection);\n return {\n agent,\n settings: new SettingsAdapter(connection.settings, agent, options.storage ?? null),\n };\n}\n"],
5
- "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAEhC,SAAS,gBAAAA,eAAc,YAAY;AACnC,SAAS,WAAW,aAAa,QAAQ,QAAQ,QAAQ,kBAAkB;AAC3E,SAAS,cAAc,mBAAAC,wBAAuB;AAEvC,SAAS,iBACd,YACA,UAAqE,CAAC,GAClB;AACpD,QAAM,QAAQ,IAAI,aAAa,UAAU;AACzC,SAAO;AAAA,IACL;AAAA,IACA,UAAU,IAAI,gBAAgB,WAAW,UAAU,OAAO,QAAQ,WAAW,IAAI;AAAA,EACnF;AACF;",
6
- "names": ["AgentAdapter", "SettingsAdapter"]
4
+ "sourcesContent": ["import type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: { storage?: Pick<Storage, 'getItem' | 'setItem'> | null; login?: Login } = {}\n): { agent: AgentAdapter; settings: SettingsAdapter; tray: TrayAdapter } {\n const agent = new AgentAdapter(connection);\n return {\n agent,\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 };\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAqB,uBAAuB;AAC5C,SAAS,mBAAmB;AAE5B,SAAS,gBAAAA,eAAc,YAAY;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAqB,cAAc,mBAAAC,wBAAuB;AAC1D,SAAS,eAAAC,oBAAmB;AAErB,SAAS,iBACd,YACA,UAAoF,CAAC,GACd;AACvE,QAAM,QAAQ,IAAI,aAAa,UAAU;AACzC,SAAO;AAAA,IACL;AAAA,IACA,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,EAC3C;AACF;",
6
+ "names": ["AgentAdapter", "SettingsAdapter", "TrayAdapter"]
7
7
  }
@@ -39,6 +39,22 @@ const statuses = { error: "error", aborted: "stopped" };
39
39
  const credentials = /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\b40[13]\b|api key|credential|bearer/i;
40
40
  const models = /model/i;
41
41
  const unavailable = /access|invalid|not found|identifier|unsupported|not supported|isn['’]t supported|not available|isn['’]t available/i;
42
+ const providerNames = { "amazon-bedrock": "Bedrock" };
43
+ const missing = /no api key|missing|not configured|no credentials/i;
44
+ function errorPart(error, provider) {
45
+ const action = errorAction(error);
46
+ if (action === "settings" && provider === "adobe") {
47
+ return {
48
+ type: "error",
49
+ message: "Your Adobe session has expired.",
50
+ detail: error,
51
+ action: "login"
52
+ };
53
+ }
54
+ const name = providerNames[provider ?? ""] ?? "The provider";
55
+ const lead = action === "settings" ? missing.test(error) ? `${name} needs an API key.` : `${name} rejected the API key.` : action === "change-model" ? "This model isn't available with the current account." : null;
56
+ return lead ? { type: "error", message: lead, detail: error, action } : { type: "error", message: error, action };
57
+ }
42
58
  function errorAction(message) {
43
59
  if (credentials.test(message)) return "settings";
44
60
  if (models.test(message) && unavailable.test(message)) return "change-model";
@@ -47,12 +63,7 @@ function errorAction(message) {
47
63
  function assistant(id, message, status) {
48
64
  const blocks = Array.isArray(message.content) ? message.content : [];
49
65
  const parts = blocks.map(part).filter((item) => item !== void 0);
50
- if (message.errorMessage)
51
- parts.push({
52
- type: "error",
53
- message: message.errorMessage,
54
- action: errorAction(message.errorMessage)
55
- });
66
+ if (message.errorMessage) parts.push(errorPart(message.errorMessage, message.provider));
56
67
  const used = usage(message);
57
68
  return {
58
69
  id,
@@ -177,6 +188,7 @@ function queued(view) {
177
188
  export {
178
189
  assistant,
179
190
  errorAction,
191
+ errorPart,
180
192
  isBusy,
181
193
  queued,
182
194
  textOf,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/messages.ts"],
4
- "sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport type { Delivered } from '../services.ts';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\nfunction toolCall(block: Block): ToolCall {\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(block.arguments),\n input: JSON.stringify(block.arguments ?? {}),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nexport function errorAction(message: string): ErrorAction {\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage)\n parts.push({\n type: 'error',\n message: message.errorMessage,\n action: errorAction(message.errorMessage),\n });\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(id: string, message: ModelMessage, delivered: Delivered | undefined): UserMessage {\n return {\n id,\n role: 'user',\n text: textOf(message.content),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage): void {\n const call = calls.get(result.toolCallId ?? '');\n if (!call) return;\n call.output = textOf(result.content);\n call.status = result.isError ? 'error' : 'done';\n}\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'pi.user' && model) out.push(user(id, model, delivered));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(message);\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered);\n previous = entry;\n }\n live(view, out, calls);\n return out;\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(view: ConversationView | undefined): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n }[];\n return items\n .filter((item) => item.mode !== 'write')\n .map((item) => ({\n id: `q${item.id}`,\n role: 'user',\n text: textOf(item.content),\n createdAt: 0,\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n }));\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport type { Delivered } from '../services.ts';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\nfunction toolCall(block: Block): ToolCall {\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(block.arguments),\n input: JSON.stringify(block.arguments ?? {}),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock' };\nconst missing = /no api key|missing|not configured|no credentials/i;\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'settings' && provider === 'adobe') {\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: error,\n action: 'login',\n };\n }\n const name = providerNames[provider ?? ''] ?? 'The provider';\n const lead =\n action === 'settings'\n ? missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`\n : action === 'change-model'\n ? \"This model isn't available with the current account.\"\n : null;\n return lead\n ? { type: 'error', message: lead, detail: error, action }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage) parts.push(errorPart(message.errorMessage, message.provider));\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(id: string, message: ModelMessage, delivered: Delivered | undefined): UserMessage {\n return {\n id,\n role: 'user',\n text: textOf(message.content),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage): void {\n const call = calls.get(result.toolCallId ?? '');\n if (!call) return;\n call.output = textOf(result.content);\n call.status = result.isError ? 'error' : 'done';\n}\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'pi.user' && model) out.push(user(id, model, delivered));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(message);\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered);\n previous = entry;\n }\n live(view, out, calls);\n return out;\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(view: ConversationView | undefined): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n }[];\n return items\n .filter((item) => item.mode !== 'write')\n .map((item) => ({\n id: `q${item.id}`,\n role: 'user',\n text: textOf(item.content),\n createdAt: 0,\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n }));\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,12 +13,14 @@ class SettingsAdapter extends Emitter {
13
13
  #settings;
14
14
  #agent;
15
15
  #storage;
16
+ #login;
16
17
  #local;
17
- constructor(settings, agent, storage) {
18
+ constructor(settings, agent, storage, login = null) {
18
19
  super();
19
20
  this.#settings = settings;
20
21
  this.#agent = agent;
21
22
  this.#storage = storage;
23
+ this.#login = login;
22
24
  const saved = JSON.parse(storage?.getItem(SETTINGS_KEY) ?? "{}");
23
25
  const { model: _model, thinking: _thinking, ...local } = { ...defaults, ...saved };
24
26
  this.#local = local;
@@ -59,8 +61,13 @@ class SettingsAdapter extends Emitter {
59
61
  );
60
62
  }
61
63
  async connect(id, secret, options = {}) {
62
- if (!secret) throw new Error(`${id} needs an API key`);
63
- await this.#settings.connect(id, secret, options.region ?? null, BACKGROUND_CONTEXT);
64
+ const account = this.#state().accounts.find((candidate) => candidate.id === id);
65
+ const login = account?.auth === "oauth" ? this.#login : null;
66
+ const options_ = login ? await this.#settings.signIn(id, BACKGROUND_CONTEXT) : null;
67
+ const key = secret ?? (login && options_ ? await login(id, options_) : void 0);
68
+ if (!key)
69
+ throw new Error(`${id} needs ${account?.auth === "oauth" ? "a sign-in" : "an API key"}`);
70
+ await this.#settings.connect(id, key, options.region ?? null, BACKGROUND_CONTEXT);
64
71
  }
65
72
  disconnect(id) {
66
73
  void this.#settings.disconnect(id, BACKGROUND_CONTEXT);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/settings-port.ts"],
4
- "sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AgentSettings, SettingsState } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { Emitter } from './emitter.ts';\n\nexport const SETTINGS_KEY = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n showThinking: false,\n diffStyle: 'unified',\n};\n\ntype Local = Omit<Settings, 'model' | 'thinking'>;\n\nexport class SettingsAdapter extends Emitter<SettingsEvents> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n #local: Local;\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null\n ) {\n super();\n this.#settings = settings;\n this.#agent = agent;\n this.#storage = storage;\n const saved = JSON.parse(storage?.getItem(SETTINGS_KEY) ?? '{}') as Partial<Local>;\n const { model: _model, thinking: _thinking, ...local } = { ...defaults, ...saved };\n this.#local = local;\n settings.state.subscribe(() => {\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #state(): SettingsState {\n return this.#settings.state.value ?? { models: [], accounts: [] };\n }\n\n get(): Settings {\n const cone = this.#agent.list()[0];\n return { ...this.#local, model: cone?.model ?? '', thinking: cone?.thinking ?? 'off' };\n }\n\n update(patch: Partial<Settings>): void {\n const { model, thinking, ...local } = patch;\n const id = this.#agent.active();\n if (model !== undefined) this.#agent.setModel(id, model);\n if (thinking !== undefined) this.#agent.setThinking(id, thinking);\n this.#local = { ...this.#local, ...local };\n this.#storage?.setItem(SETTINGS_KEY, JSON.stringify(this.#local));\n this.emit('settings', this.get());\n }\n\n models(): readonly ModelOption[] {\n return this.#state().models.map(({ id, label, provider, kind, reasoning }) => ({\n id,\n label,\n provider,\n kind,\n reasoning,\n }));\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) =>\n needs ? { ...account, needs } : account\n );\n }\n\n async connect(id: string, secret?: string, options: { region?: string } = {}): Promise<void> {\n if (!secret) throw new Error(`${id} needs an API key`);\n await this.#settings.connect(id, secret, options.region ?? null, BACKGROUND_CONTEXT);\n }\n\n disconnect(id: string): void {\n void this.#settings.disconnect(id, BACKGROUND_CONTEXT);\n }\n}\n"],
5
- "mappings": "AAOA,SAAS,0BAA0B;AAGnC,SAAS,eAAe;AAEjB,MAAM,eAAe;AAE5B,MAAM,WAAqB;AAAA,EACzB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AACb;AAIO,MAAM,wBAAwB,QAAgD;AAAA,EAC1E;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EAEA,YACE,UACA,OACA,SACA;AACA,UAAM;AACN,SAAK,YAAY;AACjB,SAAK,SAAS;AACd,SAAK,WAAW;AAChB,UAAM,QAAQ,KAAK,MAAM,SAAS,QAAQ,YAAY,KAAK,IAAI;AAC/D,UAAM,EAAE,OAAO,QAAQ,UAAU,WAAW,GAAG,MAAM,IAAI,EAAE,GAAG,UAAU,GAAG,MAAM;AACjF,SAAK,SAAS;AACd,aAAS,MAAM,UAAU,MAAM;AAC7B,WAAK,KAAK,YAAY,KAAK,SAAS,CAAC;AACrC,WAAK,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,IAClC,CAAC;AACD,UAAM,GAAG,UAAU,MAAM,KAAK,KAAK,YAAY,KAAK,IAAI,CAAC,CAAC;AAAA,EAC5D;AAAA,EAEA,SAAwB;AACtB,WAAO,KAAK,UAAU,MAAM,SAAS,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,EAAE;AAAA,EAClE;AAAA,EAEA,MAAgB;AACd,UAAM,OAAO,KAAK,OAAO,KAAK,EAAE,CAAC;AACjC,WAAO,EAAE,GAAG,KAAK,QAAQ,OAAO,MAAM,SAAS,IAAI,UAAU,MAAM,YAAY,MAAM;AAAA,EACvF;AAAA,EAEA,OAAO,OAAgC;AACrC,UAAM,EAAE,OAAO,UAAU,GAAG,MAAM,IAAI;AACtC,UAAM,KAAK,KAAK,OAAO,OAAO;AAC9B,QAAI,UAAU,OAAW,MAAK,OAAO,SAAS,IAAI,KAAK;AACvD,QAAI,aAAa,OAAW,MAAK,OAAO,YAAY,IAAI,QAAQ;AAChE,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,GAAG,MAAM;AACzC,SAAK,UAAU,QAAQ,cAAc,KAAK,UAAU,KAAK,MAAM,CAAC;AAChE,SAAK,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,EAClC;AAAA,EAEA,SAAiC;AAC/B,WAAO,KAAK,OAAO,EAAE,OAAO,IAAI,CAAC,EAAE,IAAI,OAAO,UAAU,MAAM,UAAU,OAAO;AAAA,MAC7E;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE;AAAA,EACJ;AAAA,EAEA,WAA+B;AAC7B,WAAO,KAAK,OAAO,EAAE,SAAS;AAAA,MAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,MACrD,QAAQ,EAAE,GAAG,SAAS,MAAM,IAAI;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,IAAY,QAAiB,UAA+B,CAAC,GAAkB;AAC3F,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,GAAG,EAAE,mBAAmB;AACrD,UAAM,KAAK,UAAU,QAAQ,IAAI,QAAQ,QAAQ,UAAU,MAAM,kBAAkB;AAAA,EACrF;AAAA,EAEA,WAAW,IAAkB;AAC3B,SAAK,KAAK,UAAU,WAAW,IAAI,kBAAkB;AAAA,EACvD;AACF;",
4
+ "sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AgentSettings, SettingsState, SignIn } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { Emitter } from './emitter.ts';\n\nexport const SETTINGS_KEY = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n showThinking: false,\n diffStyle: 'unified',\n};\n\ntype Local = Omit<Settings, 'model' | 'thinking'>;\n\nexport type Login = (providerId: string, signIn: SignIn) => Promise<string>;\n\nexport class SettingsAdapter extends Emitter<SettingsEvents> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n readonly #login: Login | null;\n #local: Local;\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null,\n login: Login | null = null\n ) {\n super();\n this.#settings = settings;\n this.#agent = agent;\n this.#storage = storage;\n this.#login = login;\n const saved = JSON.parse(storage?.getItem(SETTINGS_KEY) ?? '{}') as Partial<Local>;\n const { model: _model, thinking: _thinking, ...local } = { ...defaults, ...saved };\n this.#local = local;\n settings.state.subscribe(() => {\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #state(): SettingsState {\n return this.#settings.state.value ?? { models: [], accounts: [] };\n }\n\n get(): Settings {\n const cone = this.#agent.list()[0];\n return { ...this.#local, model: cone?.model ?? '', thinking: cone?.thinking ?? 'off' };\n }\n\n update(patch: Partial<Settings>): void {\n const { model, thinking, ...local } = patch;\n const id = this.#agent.active();\n if (model !== undefined) this.#agent.setModel(id, model);\n if (thinking !== undefined) this.#agent.setThinking(id, thinking);\n this.#local = { ...this.#local, ...local };\n this.#storage?.setItem(SETTINGS_KEY, JSON.stringify(this.#local));\n this.emit('settings', this.get());\n }\n\n models(): readonly ModelOption[] {\n return this.#state().models.map(({ id, label, provider, kind, reasoning }) => ({\n id,\n label,\n provider,\n kind,\n reasoning,\n }));\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) =>\n needs ? { ...account, needs } : account\n );\n }\n\n async connect(id: string, secret?: string, options: { region?: string } = {}): Promise<void> {\n const account = this.#state().accounts.find((candidate) => candidate.id === id);\n const login = account?.auth === 'oauth' ? this.#login : null;\n const options_ = login ? await this.#settings.signIn(id, BACKGROUND_CONTEXT) : null;\n const key = secret ?? (login && options_ ? await login(id, options_) : undefined);\n if (!key)\n throw new Error(`${id} needs ${account?.auth === 'oauth' ? 'a sign-in' : 'an API key'}`);\n await this.#settings.connect(id, key, options.region ?? null, BACKGROUND_CONTEXT);\n }\n\n disconnect(id: string): void {\n void this.#settings.disconnect(id, BACKGROUND_CONTEXT);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,31 @@
1
+ import { Emitter } from "./emitter.js";
2
+ class TrayAdapter extends Emitter {
3
+ #settings;
4
+ constructor(settings) {
5
+ super();
6
+ this.#settings = settings;
7
+ settings.state.subscribe(() => this.emit("status", this.status()));
8
+ }
9
+ status() {
10
+ const budget = this.#settings.state.value?.budget;
11
+ return {
12
+ name: "slicc",
13
+ kind: "hosted",
14
+ connection: "offline",
15
+ role: "none",
16
+ followers: [],
17
+ spent: 0,
18
+ rate: 0,
19
+ budget: budget ? { percent: budget.percent, window: budget.window, resets: budget.resets } : { percent: 0, window: "weekly", resets: "" },
20
+ joinUrl: ""
21
+ };
22
+ }
23
+ reconnect() {
24
+ }
25
+ disconnect() {
26
+ }
27
+ }
28
+ export {
29
+ TrayAdapter
30
+ };
31
+ //# sourceMappingURL=tray-port.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/spectrum/tray-port.ts"],
4
+ "sourcesContent": ["import type { TrayEvents, TrayPort, TrayStatus } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentSettings } from '../services.ts';\nimport { Emitter } from './emitter.ts';\n\nexport class TrayAdapter extends Emitter<TrayEvents> implements TrayPort {\n readonly #settings: AgentSettings;\n\n constructor(settings: AgentSettings) {\n super();\n this.#settings = settings;\n settings.state.subscribe(() => this.emit('status', this.status()));\n }\n\n status(): TrayStatus {\n const budget = this.#settings.state.value?.budget;\n return {\n name: 'slicc',\n kind: 'hosted',\n connection: 'offline',\n role: 'none',\n followers: [],\n spent: 0,\n rate: 0,\n budget: budget\n ? { percent: budget.percent, window: budget.window, resets: budget.resets }\n : { percent: 0, window: 'weekly', resets: '' },\n joinUrl: '',\n };\n }\n\n reconnect(): void {}\n\n disconnect(): void {}\n}\n"],
5
+ "mappings": "AAEA,SAAS,eAAe;AAEjB,MAAM,oBAAoB,QAAwC;AAAA,EAC9D;AAAA,EAET,YAAY,UAAyB;AACnC,UAAM;AACN,SAAK,YAAY;AACjB,aAAS,MAAM,UAAU,MAAM,KAAK,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC;AAAA,EACnE;AAAA,EAEA,SAAqB;AACnB,UAAM,SAAS,KAAK,UAAU,MAAM,OAAO;AAC3C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW,CAAC;AAAA,MACZ,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ,SACJ,EAAE,SAAS,OAAO,SAAS,QAAQ,OAAO,QAAQ,QAAQ,OAAO,OAAO,IACxE,EAAE,SAAS,GAAG,QAAQ,UAAU,QAAQ,GAAG;AAAA,MAC/C,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,YAAkB;AAAA,EAAC;AAAA,EAEnB,aAAmB;AAAA,EAAC;AACtB;",
6
+ "names": []
7
+ }
@@ -0,0 +1,63 @@
1
+ import type { Provider } from '@earendil-works/pi-ai';
2
+ export declare const ADOBE = "adobe";
3
+ export declare const ADOBE_PROXY = "https://adobe-llm-proxy.paolo-moz.workers.dev";
4
+ export interface AdobeModelInfo {
5
+ id: string;
6
+ name: string;
7
+ api?: 'anthropic' | 'openai';
8
+ context_window?: number;
9
+ max_tokens?: number;
10
+ reasoning?: boolean;
11
+ input?: string[];
12
+ cost?: {
13
+ input: number;
14
+ output: number;
15
+ cacheRead: number;
16
+ cacheWrite: number;
17
+ };
18
+ hidden?: boolean;
19
+ }
20
+ export interface AdobeConfig {
21
+ clientId: string;
22
+ scopes: string;
23
+ imsEnvironment: string;
24
+ models: AdobeModelInfo[];
25
+ }
26
+ export interface Budget {
27
+ percent: number;
28
+ window: 'weekly';
29
+ resets: string;
30
+ }
31
+ type Fetch = typeof fetch;
32
+ export declare function adobeModel(info: AdobeModelInfo, endpoint: string): {
33
+ id: string;
34
+ name: string;
35
+ provider: string;
36
+ api: "anthropic-messages" | "openai-completions";
37
+ baseUrl: string;
38
+ reasoning: boolean;
39
+ input: ("image" | "text")[];
40
+ cost: {
41
+ input: number;
42
+ output: number;
43
+ cacheRead: number;
44
+ cacheWrite: number;
45
+ };
46
+ contextWindow: number;
47
+ maxTokens: number;
48
+ compat?: {
49
+ supportsStore: boolean;
50
+ supportsDeveloperRole: boolean;
51
+ } | undefined;
52
+ };
53
+ export declare function adobeConfig(endpoint: string, fetcher: Fetch): Promise<AdobeConfig>;
54
+ export declare function adobeUsage(token: string, { endpoint, fetch: fetcher, }?: {
55
+ endpoint?: string;
56
+ fetch?: Fetch;
57
+ }): Promise<Budget | null>;
58
+ export declare function adobeProvider({ endpoint, session, fetch: fetcher, }?: {
59
+ endpoint?: string;
60
+ session?: string;
61
+ fetch?: Fetch;
62
+ }): Provider;
63
+ export {};
@@ -1,3 +1,4 @@
1
+ export { ADOBE, ADOBE_PROXY, type AdobeConfig, type AdobeModelInfo, adobeConfig, adobeModel, adobeProvider, adobeUsage, type Budget, } from './adobe.ts';
1
2
  export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';
2
3
  export { type AgentConnection, connectAgent } from './client.ts';
3
4
  export { createAgentControl } from './control.ts';
@@ -11,7 +12,7 @@ export { type Transport, type TransportResponse, transportFetch } from './net.ts
11
12
  export { AGENT_LOCK, type AgentOwner, type AgentWorker, type OwnerOptions, startAgent, } from './owner.ts';
12
13
  export { applyPatches, patchServerAccept } from './patches.ts';
13
14
  export { SliccPrompt } from './prompt.ts';
14
- export { type AccountState, AGENT_SESSION, AgentControl, AgentSessions, AgentSettings, type AgentSettingsChange, AgentTranscript, type ModelChoice, type OperationError, type PromptResult, type SendMode, type SendRequest, type SendResponse, type SettingsState, } from './services.ts';
15
+ export { type AccountState, AGENT_SESSION, AgentControl, AgentSessions, AgentSettings, type AgentSettingsChange, AgentTranscript, type BudgetState, type ModelChoice, type OperationError, type PromptResult, type SendMode, type SendRequest, type SendResponse, type SettingsState, type SignIn, } from './services.ts';
15
16
  export { BEDROCK, createAgentSettings, createSliccModels, DEFAULT_MODEL, DEFAULT_REGIONS, sliccProviders, withDefaultRegions, } from './settings.ts';
16
17
  export { AGENT_DIRECTORY, type OpfsSqliteOptions, openMemorySqliteStorage, openOpfsSqliteStorage, WasmSqliteDatabase, } from './sqlite.ts';
17
18
  export { PortChannel, type PortEndpoint, withUnrefTimers } from './wire.ts';
@@ -72,16 +72,29 @@ export interface AccountState {
72
72
  provider: string;
73
73
  identity: string;
74
74
  status: 'connected' | 'disconnected';
75
- auth: 'api-key';
75
+ auth: 'api-key' | 'oauth';
76
76
  needs: 'cors-free-transport' | null;
77
77
  }
78
+ export interface BudgetState {
79
+ provider: string;
80
+ percent: number;
81
+ window: 'weekly';
82
+ resets: string;
83
+ }
84
+ export interface SignIn {
85
+ clientId: string;
86
+ scopes: string;
87
+ imsEnvironment: string;
88
+ }
78
89
  export interface SettingsState {
79
90
  models: ModelChoice[];
80
91
  accounts: AccountState[];
92
+ budget?: BudgetState | null;
81
93
  }
82
94
  export interface AgentSettings {
83
95
  readonly state: ReplicatedState<SettingsState>;
84
96
  connect(providerId: string, secret: string, region: string | null, context: Context): Promise<void>;
85
97
  disconnect(providerId: string, context: Context): Promise<void>;
98
+ signIn(providerId: string, context: Context): Promise<SignIn | null>;
86
99
  }
87
100
  export declare const AgentSettings: import("@earendil-works/chord").Service<AgentSettings>;
@@ -1,5 +1,6 @@
1
1
  import type { CredentialStore, Provider } from '@earendil-works/pi-ai';
2
2
  import { type MutableModels } from '@earendil-works/pi-ai/models';
3
+ import { type Budget } from './adobe.ts';
3
4
  import type { AgentSettings } from './services.ts';
4
5
  export declare const BEDROCK = "amazon-bedrock";
5
6
  export declare const DEFAULT_MODEL: {
@@ -10,4 +11,8 @@ export declare const DEFAULT_REGIONS: Readonly<Record<string, string>>;
10
11
  export declare function sliccProviders(): Provider[];
11
12
  export declare function withDefaultRegions(credentials: CredentialStore): CredentialStore;
12
13
  export declare function createSliccModels(credentials: CredentialStore, providers?: Provider<import("@earendil-works/pi-ai").Api>[]): MutableModels;
13
- export declare function createAgentSettings(models: MutableModels, credentials: CredentialStore, providers?: Provider<import("@earendil-works/pi-ai").Api>[]): Promise<AgentSettings>;
14
+ export type UsageReader = (token: string) => Promise<Budget | null>;
15
+ export declare function createAgentSettings(models: MutableModels, credentials: CredentialStore, providers?: Provider<import("@earendil-works/pi-ai").Api>[], options?: {
16
+ usage?: UsageReader;
17
+ every?: number;
18
+ }): Promise<AgentSettings>;
@@ -1,12 +1,16 @@
1
1
  import type { AgentConnection } from '../client.ts';
2
2
  import { AgentAdapter } from './agent-port.ts';
3
- import { SettingsAdapter } from './settings-port.ts';
3
+ import { type Login, SettingsAdapter } from './settings-port.ts';
4
+ import { TrayAdapter } from './tray-port.ts';
4
5
  export { AgentAdapter, CONE } from './agent-port.ts';
5
- export { assistant, errorAction, isBusy, queued, textOf, toMessages } from './messages.ts';
6
- export { SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';
6
+ export { assistant, errorAction, errorPart, isBusy, queued, textOf, toMessages, } from './messages.ts';
7
+ export { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';
8
+ export { TrayAdapter } from './tray-port.ts';
7
9
  export declare function createAgentModel(connection: AgentConnection, options?: {
8
10
  storage?: Pick<Storage, 'getItem' | 'setItem'> | null;
11
+ login?: Login;
9
12
  }): {
10
13
  agent: AgentAdapter;
11
14
  settings: SettingsAdapter;
15
+ tray: TrayAdapter;
12
16
  };
@@ -1,4 +1,4 @@
1
- import type { AssistantMessage, AssistantStatus, ErrorAction, Message, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
1
+ import type { AssistantMessage, AssistantStatus, ErrorAction, Message, MessagePart, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
2
2
  import type { ConversationView } from '@earendil-works/pi-durable';
3
3
  import type { Delivered } from '../services.ts';
4
4
  type Json = Record<string, unknown>;
@@ -31,6 +31,7 @@ interface ModelMessage {
31
31
  isError?: boolean;
32
32
  }
33
33
  export declare function textOf(content: ModelMessage['content']): string;
34
+ export declare function errorPart(error: string, provider?: string): MessagePart;
34
35
  export declare function errorAction(message: string): ErrorAction;
35
36
  export declare function assistant(id: string, message: ModelMessage, status?: AssistantStatus): AssistantMessage;
36
37
  export declare function toMessages(view: ConversationView | undefined, deliveries?: Readonly<Record<string, Delivered>>): Message[];
@@ -1,11 +1,12 @@
1
1
  import type { Account, ModelOption, Settings, SettingsEvents, SettingsPort } from '@ai-ecoverse/slicc-spectrum/ui';
2
- import type { AgentSettings } from '../services.ts';
2
+ import type { AgentSettings, SignIn } from '../services.ts';
3
3
  import type { AgentAdapter } from './agent-port.ts';
4
4
  import { Emitter } from './emitter.ts';
5
5
  export declare const SETTINGS_KEY = "slicc-agent.settings";
6
+ export type Login = (providerId: string, signIn: SignIn) => Promise<string>;
6
7
  export declare class SettingsAdapter extends Emitter<SettingsEvents> implements SettingsPort {
7
8
  #private;
8
- constructor(settings: AgentSettings, agent: AgentAdapter, storage: Pick<Storage, 'getItem' | 'setItem'> | null);
9
+ constructor(settings: AgentSettings, agent: AgentAdapter, storage: Pick<Storage, 'getItem' | 'setItem'> | null, login?: Login | null);
9
10
  get(): Settings;
10
11
  update(patch: Partial<Settings>): void;
11
12
  models(): readonly ModelOption[];
@@ -0,0 +1,10 @@
1
+ import type { TrayEvents, TrayPort, TrayStatus } from '@ai-ecoverse/slicc-spectrum/ui';
2
+ import type { AgentSettings } from '../services.ts';
3
+ import { Emitter } from './emitter.ts';
4
+ export declare class TrayAdapter extends Emitter<TrayEvents> implements TrayPort {
5
+ #private;
6
+ constructor(settings: AgentSettings);
7
+ status(): TrayStatus;
8
+ reconnect(): void;
9
+ disconnect(): void;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
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",
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@ai-ecoverse/slicc-shared-web": "1.5.0",
63
- "@ai-ecoverse/slicc-spectrum": "1.8.2",
63
+ "@ai-ecoverse/slicc-spectrum": "1.11.0",
64
64
  "@ai-ecoverse/wasm-bash": "5.3.0-7",
65
65
  "@ai-ecoverse/wasm-coreutils": "9.7.0-3",
66
66
  "@biomejs/biome": "2.5.15",