@ai-ecoverse/slicc-agent 6.15.2 → 6.17.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.
Files changed (40) hide show
  1. package/README.md +3 -1
  2. package/dist/codex.js +50 -0
  3. package/dist/codex.js.map +7 -0
  4. package/dist/device.js +145 -0
  5. package/dist/device.js.map +7 -0
  6. package/dist/guests/guard.js +28 -1
  7. package/dist/guests/guard.js.map +2 -2
  8. package/dist/main.js +10 -6
  9. package/dist/main.js.map +2 -2
  10. package/dist/preferred.js +1 -0
  11. package/dist/preferred.js.map +2 -2
  12. package/dist/providers.js +16 -4
  13. package/dist/providers.js.map +2 -2
  14. package/dist/secrets/credentials.js +10 -2
  15. package/dist/secrets/credentials.js.map +2 -2
  16. package/dist/secrets/vault.js +4 -0
  17. package/dist/secrets/vault.js.map +2 -2
  18. package/dist/services.js.map +2 -2
  19. package/dist/settings.js +31 -5
  20. package/dist/settings.js.map +2 -2
  21. package/dist/sign-ins.js +25 -0
  22. package/dist/sign-ins.js.map +7 -0
  23. package/dist/spectrum/approvals-port.js +26 -2
  24. package/dist/spectrum/approvals-port.js.map +2 -2
  25. package/dist/spectrum/mcp-login.js +2 -1
  26. package/dist/spectrum/mcp-login.js.map +2 -2
  27. package/dist/spectrum/settings-port.js +65 -11
  28. package/dist/spectrum/settings-port.js.map +2 -2
  29. package/dist/types/codex.d.ts +4 -0
  30. package/dist/types/device.d.ts +24 -0
  31. package/dist/types/guests/guard.d.ts +1 -0
  32. package/dist/types/providers.d.ts +1 -0
  33. package/dist/types/secrets/vault.d.ts +6 -0
  34. package/dist/types/services.d.ts +2 -1
  35. package/dist/types/settings.d.ts +4 -0
  36. package/dist/types/sign-ins.d.ts +17 -0
  37. package/dist/types/spectrum/approvals-port.d.ts +1 -0
  38. package/dist/types/spectrum/mcp-login.d.ts +1 -0
  39. package/dist/types/spectrum/settings-port.d.ts +5 -2
  40. package/package.json +4 -4
@@ -1,8 +1,13 @@
1
- import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
1
+ import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
2
2
  import { preferredModel } from "../preferred.js";
3
+ import {
4
+ DEVICE_SIGN_INS,
5
+ httpsUrl,
6
+ SIGN_IN_UNFINISHED
7
+ } from "../sign-ins.js";
3
8
  import { attempt } from "./attempt.js";
4
9
  import { Emitter } from "./emitter.js";
5
- import { mcpLogin } from "./mcp-login.js";
10
+ import { mcpLogin, untilAborted } from "./mcp-login.js";
6
11
  const SETTINGS_KEY = "slicc-agent.settings";
7
12
  const defaults = {
8
13
  color: "system",
@@ -13,7 +18,8 @@ const defaults = {
13
18
  diffStyle: "unified"
14
19
  };
15
20
  const SIGN_IN_NEEDS_VAULT = "Adobe sign-in needs seven's vault: update seven";
16
- const UNFINISHED = "The sign-in didn't finish. Try again.";
21
+ const UNFINISHED = SIGN_IN_UNFINISHED;
22
+ const openPopup = () => globalThis.open?.("about:blank", "slicc-sign-in", "popup,width=520,height=720") ?? null;
17
23
  function readable(error) {
18
24
  if (error instanceof DOMException && error.name === "AbortError") return error;
19
25
  const text = (error instanceof Error ? error.message : String(error ?? "")).trim();
@@ -28,12 +34,15 @@ class SettingsAdapter extends Emitter {
28
34
  #login;
29
35
  #mcpLogin;
30
36
  #signing = /* @__PURE__ */ new Map();
37
+ #prompts = /* @__PURE__ */ new Map();
38
+ #open;
31
39
  #local;
32
40
  #offered = "";
33
41
  #urls = /* @__PURE__ */ new Map();
34
- constructor(settings, agent, storage, login = null, mcp = mcpLogin()) {
42
+ constructor(settings, agent, storage, login = null, mcp = mcpLogin(), open = openPopup) {
35
43
  super();
36
44
  this.#mcpLogin = mcp;
45
+ this.#open = open;
37
46
  this.#settings = settings;
38
47
  this.#agent = agent;
39
48
  this.#storage = storage;
@@ -126,20 +135,29 @@ class SettingsAdapter extends Emitter {
126
135
  return this.#state().accounts.map(({ needs, ...account }) => {
127
136
  const url = this.#urls.get(account.id);
128
137
  const located = url ? { ...account, url } : account;
129
- const shown = this.#signing.has(account.id) ? { ...located, status: "signing-in" } : located;
138
+ const prompt = this.#prompts.get(account.id);
139
+ const shown = this.#signing.has(account.id) ? {
140
+ ...located,
141
+ status: "signing-in",
142
+ ...prompt ? { signIn: { ...prompt } } : {}
143
+ } : located;
130
144
  return needs ? { ...shown, needs } : shown;
131
145
  });
132
146
  }
133
- async connect(id, secret, fields) {
147
+ async connect(id, secret, fields, options) {
134
148
  const account = this.#state().accounts.find((candidate) => candidate.id === id) ?? this.#offer(id);
135
149
  const mcp = account?.group === "mcp";
136
- const login = account?.auth !== "oauth" ? null : mcp ? this.#mcpLogin : this.#login;
137
150
  const given = fields ? { ...fields } : null;
151
+ const keyed = options?.method === "key";
152
+ if (account?.auth === "oauth" && DEVICE_SIGN_INS.has(id) && secret === void 0 && !keyed)
153
+ return this.#device(id, given);
154
+ const login = account?.auth !== "oauth" || keyed ? null : mcp ? this.#mcpLogin : this.#login;
155
+ if (keyed) this.cancel(id);
138
156
  if (secret !== void 0) {
139
157
  await this.#settings.connect(id, secret, given, BACKGROUND_CONTEXT);
140
158
  return;
141
159
  }
142
- if (account?.auth === "oauth" && !login) throw new Error(`${id} needs a sign-in`);
160
+ if (account?.auth === "oauth" && !login && !keyed) throw new Error(`${id} needs a sign-in`);
143
161
  if (!login) {
144
162
  if (account?.store !== "vault") throw new Error(`${id} needs an API key`);
145
163
  await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);
@@ -154,11 +172,47 @@ class SettingsAdapter extends Emitter {
154
172
  if (answer?.sealed !== true) throw new Error(UNFINISHED);
155
173
  await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);
156
174
  }
157
- async #signIn(id, login) {
175
+ #begin(id) {
158
176
  const controller = new AbortController();
159
177
  this.#signing.get(id)?.abort(new DOMException("A newer sign-in replaced this one.", "AbortError"));
160
178
  this.#signing.set(id, controller);
179
+ this.#prompts.delete(id);
180
+ this.emit("accounts", this.accounts());
181
+ return controller;
182
+ }
183
+ #end(id, controller) {
184
+ if (this.#signing.get(id) === controller) {
185
+ this.#signing.delete(id);
186
+ this.#prompts.delete(id);
187
+ }
161
188
  this.emit("accounts", this.accounts());
189
+ }
190
+ async #device(id, given) {
191
+ const popup = this.#open();
192
+ const controller = this.#begin(id);
193
+ const { signal } = controller;
194
+ const context = withAbortSignal(signal, BACKGROUND_CONTEXT);
195
+ try {
196
+ const started = await untilAborted(
197
+ this.#settings.signIn(id, context),
198
+ signal
199
+ );
200
+ if (!started || !httpsUrl(started.url)) throw new Error(UNFINISHED);
201
+ const code = typeof started.code === "string" && started.code ? started.code : void 0;
202
+ this.#prompts.set(id, { url: started.url, ...code ? { code } : {} });
203
+ this.emit("accounts", this.accounts());
204
+ if (popup) popup.location.href = started.url;
205
+ await untilAborted(this.#settings.connect(id, null, given, context), signal);
206
+ } catch (error) {
207
+ signal.throwIfAborted();
208
+ throw readable(error);
209
+ } finally {
210
+ popup?.close();
211
+ this.#end(id, controller);
212
+ }
213
+ }
214
+ async #signIn(id, login) {
215
+ const controller = this.#begin(id);
162
216
  try {
163
217
  const key = await login(
164
218
  id,
@@ -173,14 +227,14 @@ class SettingsAdapter extends Emitter {
173
227
  controller.signal.throwIfAborted();
174
228
  return key;
175
229
  } finally {
176
- if (this.#signing.get(id) === controller) this.#signing.delete(id);
177
- this.emit("accounts", this.accounts());
230
+ this.#end(id, controller);
178
231
  }
179
232
  }
180
233
  cancel(id) {
181
234
  const controller = this.#signing.get(id);
182
235
  if (!controller) return;
183
236
  this.#signing.delete(id);
237
+ this.#prompts.delete(id);
184
238
  controller.abort(new DOMException("The sign-in was cancelled.", "AbortError"));
185
239
  this.emit("accounts", this.accounts());
186
240
  }
@@ -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 { preferredModel } from '../preferred.ts';\nimport type { AgentSettings, ProviderOption, SettingsState, SignIn } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { attempt } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\nimport { mcpLogin } from './mcp-login.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 Sealed = { sealed: true };\n\nexport type Login = (\n providerId: string,\n signIn: () => Promise<SignIn | null>,\n options: { signal: AbortSignal }\n) => Promise<string | Sealed>;\n\nexport const SIGN_IN_NEEDS_VAULT = \"Adobe sign-in needs seven's vault: update seven\";\nexport const UNFINISHED = \"The sign-in didn't finish. Try again.\";\n\nexport function readable(error: unknown): Error {\n if (error instanceof DOMException && error.name === 'AbortError') return error;\n const text = (error instanceof Error ? error.message : String(error ?? '')).trim();\n if (!text) return new Error(UNFINISHED);\n const lead = /^[a-z]+\\b(?![-.@/])/.test(text) ? text[0].toUpperCase() + text.slice(1) : text;\n return new Error(/[.!?]$/.test(lead) ? lead : `${lead}.`);\n}\n\ntype Events = SettingsEvents & { providers: readonly ProviderOption[] };\n\nexport class SettingsAdapter extends Emitter<Events> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n readonly #login: Login | null;\n readonly #mcpLogin: Login;\n readonly #signing = new Map<string, AbortController>();\n #local: Local;\n #offered = '';\n readonly #urls = new Map<string, string>();\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null,\n login: Login | null = null,\n mcp: Login = mcpLogin() as Login\n ) {\n super();\n this.#mcpLogin = mcp;\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 this.#offered = JSON.stringify(this.providers());\n this.#locate();\n settings.state.subscribe(() => {\n this.#locate();\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n const offered = JSON.stringify(this.providers());\n if (offered === this.#offered) return;\n this.#offered = offered;\n this.emit('providers', this.providers());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #locate(): void {\n const servers = this.#state()\n .accounts.filter(({ group }) => group === 'mcp')\n .map(({ id }) => id);\n for (const id of this.#urls.keys()) if (!servers.includes(id)) this.#urls.delete(id);\n for (const id of servers) {\n void this.#settings\n .serverUrl?.(id, BACKGROUND_CONTEXT)\n .then((url) => {\n if ((url ?? undefined) === this.#urls.get(id)) return;\n if (url) this.#urls.set(id, url);\n else this.#urls.delete(id);\n this.emit('accounts', this.accounts());\n })\n .catch(() => undefined);\n }\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 const choices = this.#state().models;\n const groups = Map.groupBy(choices, ({ id }) => id.slice(0, id.indexOf('/')));\n const chosen = new Set(\n [...groups].map(\n ([providerId, group]) =>\n preferredModel(\n providerId,\n group.map((choice) => ({ id: choice.id.slice(providerId.length + 1), choice }))\n )?.choice\n )\n );\n return choices.map((choice) => {\n const { id, label, provider, kind, reasoning, images } = choice;\n return {\n id,\n label,\n provider,\n kind,\n reasoning,\n ...(images === undefined ? {} : { images }),\n ...(chosen.has(choice) ? { default: true } : {}),\n };\n });\n }\n\n #offer(id: string) {\n const option = this.providers().find((candidate) => candidate.id === id);\n if (!option) return undefined;\n return {\n auth: option.auth === 'oauth' ? ('oauth' as const) : ('api-key' as const),\n store: 'vault' as const,\n group: 'providers' as const,\n };\n }\n\n providers(): readonly ProviderOption[] {\n return this.#state().providers ?? [];\n }\n\n async remove(id: string): Promise<void> {\n this.cancel(id);\n await this.#settings.remove(id, BACKGROUND_CONTEXT);\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) => {\n const url = this.#urls.get(account.id);\n const located = url ? { ...account, url } : account;\n const shown = this.#signing.has(account.id)\n ? { ...located, status: 'signing-in' as const }\n : located;\n return needs ? { ...shown, needs } : shown;\n });\n }\n\n async connect(id: string, secret?: string, fields?: Record<string, string>): Promise<void> {\n const account =\n this.#state().accounts.find((candidate) => candidate.id === id) ?? this.#offer(id);\n const mcp = account?.group === 'mcp';\n const login = account?.auth !== 'oauth' ? null : mcp ? this.#mcpLogin : this.#login;\n const given = fields ? { ...fields } : null;\n if (secret !== undefined) {\n await this.#settings.connect(id, secret, given, BACKGROUND_CONTEXT);\n return;\n }\n if (account?.auth === 'oauth' && !login) throw new Error(`${id} needs a sign-in`);\n if (!login) {\n if (account?.store !== 'vault') throw new Error(`${id} needs an API key`);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n return;\n }\n const answer = await this.#signIn(id, login);\n if (typeof answer === 'string') {\n if (!mcp) throw new Error(SIGN_IN_NEEDS_VAULT);\n await this.#settings.connect(id, answer, given, BACKGROUND_CONTEXT);\n return;\n }\n if (answer?.sealed !== true) throw new Error(UNFINISHED);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n }\n\n async #signIn(id: string, login: Login): Promise<string | Sealed> {\n const controller = new AbortController();\n this.#signing\n .get(id)\n ?.abort(new DOMException('A newer sign-in replaced this one.', 'AbortError'));\n this.#signing.set(id, controller);\n this.emit('accounts', this.accounts());\n try {\n const key = await login(\n id,\n () => this.#settings.signIn(id, BACKGROUND_CONTEXT) as Promise<SignIn | null>,\n {\n signal: controller.signal,\n }\n ).catch((error: unknown) => {\n controller.signal.throwIfAborted();\n throw readable(error);\n });\n controller.signal.throwIfAborted();\n return key;\n } finally {\n if (this.#signing.get(id) === controller) this.#signing.delete(id);\n this.emit('accounts', this.accounts());\n }\n }\n\n cancel(id: string): void {\n const controller = this.#signing.get(id);\n if (!controller) return;\n this.#signing.delete(id);\n controller.abort(new DOMException('The sign-in was cancelled.', 'AbortError'));\n this.emit('accounts', this.accounts());\n }\n\n disconnect(id: string): void {\n attempt(this.#settings.disconnect(id, BACKGROUND_CONTEXT), 'disconnect accounts', (text) =>\n this.#agent.notify(text)\n );\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT, withAbortSignal } from '@earendil-works/chord/context';\nimport { preferredModel } from '../preferred.ts';\nimport type { AgentSettings, ProviderOption, SettingsState, SignIn } from '../services.ts';\nimport {\n type ConnectOptions,\n DEVICE_SIGN_INS,\n type DeviceSignIn,\n httpsUrl,\n SIGN_IN_UNFINISHED,\n} from '../sign-ins.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { attempt } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\nimport { mcpLogin, type Popup, untilAborted } from './mcp-login.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 Sealed = { sealed: true };\n\nexport type Login = (\n providerId: string,\n signIn: () => Promise<SignIn | null>,\n options: { signal: AbortSignal }\n) => Promise<string | Sealed>;\n\nexport const SIGN_IN_NEEDS_VAULT = \"Adobe sign-in needs seven's vault: update seven\";\nexport const UNFINISHED = SIGN_IN_UNFINISHED;\n\nexport type OpenPopup = () => Popup | null;\n\nconst openPopup: OpenPopup = () =>\n globalThis.open?.('about:blank', 'slicc-sign-in', 'popup,width=520,height=720') ?? null;\n\nexport function readable(error: unknown): Error {\n if (error instanceof DOMException && error.name === 'AbortError') return error;\n const text = (error instanceof Error ? error.message : String(error ?? '')).trim();\n if (!text) return new Error(UNFINISHED);\n const lead = /^[a-z]+\\b(?![-.@/])/.test(text) ? text[0].toUpperCase() + text.slice(1) : text;\n return new Error(/[.!?]$/.test(lead) ? lead : `${lead}.`);\n}\n\ntype Events = SettingsEvents & { providers: readonly ProviderOption[] };\n\nexport class SettingsAdapter extends Emitter<Events> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n readonly #login: Login | null;\n readonly #mcpLogin: Login;\n readonly #signing = new Map<string, AbortController>();\n readonly #prompts = new Map<string, DeviceSignIn>();\n readonly #open: OpenPopup;\n #local: Local;\n #offered = '';\n readonly #urls = new Map<string, string>();\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null,\n login: Login | null = null,\n mcp: Login = mcpLogin() as Login,\n open: OpenPopup = openPopup\n ) {\n super();\n this.#mcpLogin = mcp;\n this.#open = open;\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 this.#offered = JSON.stringify(this.providers());\n this.#locate();\n settings.state.subscribe(() => {\n this.#locate();\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n const offered = JSON.stringify(this.providers());\n if (offered === this.#offered) return;\n this.#offered = offered;\n this.emit('providers', this.providers());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #locate(): void {\n const servers = this.#state()\n .accounts.filter(({ group }) => group === 'mcp')\n .map(({ id }) => id);\n for (const id of this.#urls.keys()) if (!servers.includes(id)) this.#urls.delete(id);\n for (const id of servers) {\n void this.#settings\n .serverUrl?.(id, BACKGROUND_CONTEXT)\n .then((url) => {\n if ((url ?? undefined) === this.#urls.get(id)) return;\n if (url) this.#urls.set(id, url);\n else this.#urls.delete(id);\n this.emit('accounts', this.accounts());\n })\n .catch(() => undefined);\n }\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 const choices = this.#state().models;\n const groups = Map.groupBy(choices, ({ id }) => id.slice(0, id.indexOf('/')));\n const chosen = new Set(\n [...groups].map(\n ([providerId, group]) =>\n preferredModel(\n providerId,\n group.map((choice) => ({ id: choice.id.slice(providerId.length + 1), choice }))\n )?.choice\n )\n );\n return choices.map((choice) => {\n const { id, label, provider, kind, reasoning, images } = choice;\n return {\n id,\n label,\n provider,\n kind,\n reasoning,\n ...(images === undefined ? {} : { images }),\n ...(chosen.has(choice) ? { default: true } : {}),\n };\n });\n }\n\n #offer(id: string) {\n const option = this.providers().find((candidate) => candidate.id === id);\n if (!option) return undefined;\n return {\n auth: option.auth === 'oauth' ? ('oauth' as const) : ('api-key' as const),\n store: 'vault' as const,\n group: 'providers' as const,\n };\n }\n\n providers(): readonly ProviderOption[] {\n return this.#state().providers ?? [];\n }\n\n async remove(id: string): Promise<void> {\n this.cancel(id);\n await this.#settings.remove(id, BACKGROUND_CONTEXT);\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) => {\n const url = this.#urls.get(account.id);\n const located = url ? { ...account, url } : account;\n const prompt = this.#prompts.get(account.id);\n const shown = this.#signing.has(account.id)\n ? {\n ...located,\n status: 'signing-in' as const,\n ...(prompt ? { signIn: { ...prompt } } : {}),\n }\n : located;\n return needs ? { ...shown, needs } : shown;\n });\n }\n\n async connect(\n id: string,\n secret?: string,\n fields?: Record<string, string>,\n options?: ConnectOptions\n ): Promise<void> {\n const account =\n this.#state().accounts.find((candidate) => candidate.id === id) ?? this.#offer(id);\n const mcp = account?.group === 'mcp';\n const given = fields ? { ...fields } : null;\n const keyed = options?.method === 'key';\n if (account?.auth === 'oauth' && DEVICE_SIGN_INS.has(id) && secret === undefined && !keyed)\n return this.#device(id, given);\n const login = account?.auth !== 'oauth' || keyed ? null : mcp ? this.#mcpLogin : this.#login;\n if (keyed) this.cancel(id);\n if (secret !== undefined) {\n await this.#settings.connect(id, secret, given, BACKGROUND_CONTEXT);\n return;\n }\n if (account?.auth === 'oauth' && !login && !keyed) throw new Error(`${id} needs a sign-in`);\n if (!login) {\n if (account?.store !== 'vault') throw new Error(`${id} needs an API key`);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n return;\n }\n const answer = await this.#signIn(id, login);\n if (typeof answer === 'string') {\n if (!mcp) throw new Error(SIGN_IN_NEEDS_VAULT);\n await this.#settings.connect(id, answer, given, BACKGROUND_CONTEXT);\n return;\n }\n if (answer?.sealed !== true) throw new Error(UNFINISHED);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n }\n\n #begin(id: string): AbortController {\n const controller = new AbortController();\n this.#signing\n .get(id)\n ?.abort(new DOMException('A newer sign-in replaced this one.', 'AbortError'));\n this.#signing.set(id, controller);\n this.#prompts.delete(id);\n this.emit('accounts', this.accounts());\n return controller;\n }\n\n #end(id: string, controller: AbortController): void {\n if (this.#signing.get(id) === controller) {\n this.#signing.delete(id);\n this.#prompts.delete(id);\n }\n this.emit('accounts', this.accounts());\n }\n\n async #device(id: string, given: Record<string, string> | null): Promise<void> {\n const popup = this.#open();\n const controller = this.#begin(id);\n const { signal } = controller;\n const context = withAbortSignal(signal, BACKGROUND_CONTEXT);\n try {\n const started = (await untilAborted(\n this.#settings.signIn(id, context),\n signal\n )) as DeviceSignIn | null;\n if (!started || !httpsUrl(started.url)) throw new Error(UNFINISHED);\n const code = typeof started.code === 'string' && started.code ? started.code : undefined;\n this.#prompts.set(id, { url: started.url, ...(code ? { code } : {}) });\n this.emit('accounts', this.accounts());\n if (popup) popup.location.href = started.url;\n await untilAborted(this.#settings.connect(id, null, given, context), signal);\n } catch (error) {\n signal.throwIfAborted();\n throw readable(error);\n } finally {\n popup?.close();\n this.#end(id, controller);\n }\n }\n\n async #signIn(id: string, login: Login): Promise<string | Sealed> {\n const controller = this.#begin(id);\n try {\n const key = await login(\n id,\n () => this.#settings.signIn(id, BACKGROUND_CONTEXT) as Promise<SignIn | null>,\n {\n signal: controller.signal,\n }\n ).catch((error: unknown) => {\n controller.signal.throwIfAborted();\n throw readable(error);\n });\n controller.signal.throwIfAborted();\n return key;\n } finally {\n this.#end(id, controller);\n }\n }\n\n cancel(id: string): void {\n const controller = this.#signing.get(id);\n if (!controller) return;\n this.#signing.delete(id);\n this.#prompts.delete(id);\n controller.abort(new DOMException('The sign-in was cancelled.', 'AbortError'));\n this.emit('accounts', this.accounts());\n }\n\n disconnect(id: string): void {\n attempt(this.#settings.disconnect(id, BACKGROUND_CONTEXT), 'disconnect accounts', (text) =>\n this.#agent.notify(text)\n );\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,4 @@
1
+ import type { Provider } from '@earendil-works/pi-ai';
2
+ export declare function codexKey(placeholder: string, account: string): string;
3
+ export declare function codexProvider(): Provider;
4
+ export declare function codexFetch(inner: typeof fetch): typeof fetch;
@@ -0,0 +1,24 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import type { OAuthCredential, Provider } from '@earendil-works/pi-ai';
3
+ import { type DeviceSignIn } from './sign-ins.ts';
4
+ export declare const ATTACH_MS = 60000;
5
+ export interface ProviderGrant {
6
+ provider: string;
7
+ name: string;
8
+ response: Uint8Array;
9
+ settings?: Record<string, string>;
10
+ }
11
+ export type Grant = (grant: ProviderGrant) => Promise<unknown>;
12
+ export interface DeviceFlows {
13
+ has(providerId: string): boolean;
14
+ start(provider: Provider, name: string, context: Context): Promise<DeviceSignIn>;
15
+ finish(providerId: string, context: Context): Promise<void>;
16
+ stop(providerId: string): void;
17
+ }
18
+ export declare function keptError(error: unknown): Error;
19
+ export declare function flowError(error: unknown): Error;
20
+ export declare function tokenResponse(credential: OAuthCredential, now: number): Uint8Array;
21
+ export declare function deviceFlows(grant: Grant, options?: {
22
+ attachMs?: number;
23
+ now?: () => number;
24
+ }): DeviceFlows;
@@ -31,6 +31,7 @@ type Taint = {
31
31
  tier: string;
32
32
  };
33
33
  export declare function canonical(value: unknown): string;
34
+ export declare function scrubbedArguments(args: Record<string, unknown>): Promise<Record<string, unknown>>;
34
35
  export declare function summaryOf(args: Record<string, unknown>): string;
35
36
  export declare function reasonOf(item: Approval): string;
36
37
  export declare function taintsOf(inputs: readonly SubmissionId[], seats: Readonly<Record<string, SeatRecord>>, harness: Pick<Harness, 'submission'>, context: Context): Promise<Taint[]>;
@@ -24,6 +24,7 @@ export interface KeyProvider {
24
24
  description: string;
25
25
  keyUrl: string;
26
26
  cors: boolean;
27
+ signIn?: true;
27
28
  fields?: readonly FieldSpec[];
28
29
  }
29
30
  export declare function httpsBase(value: string): URL | undefined;
@@ -49,6 +49,12 @@ export declare class Vault implements Scrub {
49
49
  mcp(server: string, request: OutgoingRequest): Promise<OutgoingRequest>;
50
50
  records(): Promise<AgentRecord[]>;
51
51
  grant(server: string, response: string | Uint8Array, oauth: OAuthConfig, name?: string): Promise<Granted>;
52
+ grantProvider(grant: {
53
+ provider: string;
54
+ name: string;
55
+ response: Uint8Array;
56
+ settings?: Record<string, string>;
57
+ }): Promise<Omit<Granted, 'response'>>;
52
58
  refresh(target: RefreshTarget, name?: string): Promise<string>;
53
59
  endpoints(set: EndpointSet): Promise<void>;
54
60
  stream(): ScrubStream;
@@ -11,6 +11,7 @@ import type { GuestDraft, GuestGates, GuestSeat } from './guests/seats.ts';
11
11
  import type { MemoryEntry, MemoryScope, MemoryTag } from './memory/format.ts';
12
12
  import type { AgentRemote } from './remote/host.ts';
13
13
  import type { VaultState } from './secrets/vault.ts';
14
+ import type { DeviceSignIn } from './sign-ins.ts';
14
15
  import type { SkillPackage, SkillSource } from './skills/skills.ts';
15
16
  import type { Sprinkle } from './sprinkles/kind.ts';
16
17
  export type SendMode = 'steer' | 'followUp' | 'reject';
@@ -409,7 +410,7 @@ export interface AgentSettings {
409
410
  disconnect(providerId: string, context: Context): Promise<void>;
410
411
  remove(providerId: string, context: Context): Promise<void>;
411
412
  serverUrl(providerId: string, context: Context): Promise<string | null>;
412
- signIn(providerId: string, context: Context): Promise<SignIn | McpSignIn | null>;
413
+ signIn(providerId: string, context: Context): Promise<SignIn | McpSignIn | DeviceSignIn | null>;
413
414
  dismissMoved(context: Context): Promise<void>;
414
415
  }
415
416
  export declare const SETTINGS_METHODS: ("connect" | "disconnect" | "dismissMoved" | "remove" | "serverUrl" | "signIn")[];
@@ -1,11 +1,13 @@
1
1
  import type { CredentialStore, ModelsStore, Provider } from '@earendil-works/pi-ai';
2
2
  import { type MutableModels } from '@earendil-works/pi-ai/models';
3
3
  import { type Budget } from './adobe.ts';
4
+ import { type DeviceFlows } from './device.ts';
4
5
  import type { McpAccounts } from './mcp/index.ts';
5
6
  import type { Reach } from './net.ts';
6
7
  import type { KeysMoved } from './secrets/legacy.ts';
7
8
  import type { VaultRecords } from './secrets/records.ts';
8
9
  import type { PlainStore } from './secrets/store.ts';
10
+ import type { Vault } from './secrets/vault.ts';
9
11
  import type { AccountField, AgentSettings } from './services.ts';
10
12
  export { BEDROCK, PREFERRED_MODELS, preferredModel } from './preferred.ts';
11
13
  export declare const DEFAULT_MODEL: {
@@ -45,4 +47,6 @@ export declare function createAgentSettings(models: MutableModels, sources: Sett
45
47
  mcp?: McpAccounts;
46
48
  moved?: KeysMoved;
47
49
  reach?: Reach;
50
+ vault?: Pick<Vault, 'connected' | 'needed' | 'grantProvider'>;
51
+ flows?: DeviceFlows;
48
52
  }): Promise<AgentSettings>;
@@ -0,0 +1,17 @@
1
+ export declare const CODEX = "openai-codex";
2
+ export declare const CODEX_ACCOUNT = "CHATGPT_ACCOUNT_ID";
3
+ export declare const DEVICE_SIGN_INS: ReadonlySet<string>;
4
+ export declare const SIGN_IN_REFUSED = "The sign-in was refused.";
5
+ export declare const SIGN_IN_UNFINISHED = "The sign-in didn't finish. Try again.";
6
+ export declare const NAME_TAKEN = "This name holds another provider's key.";
7
+ export declare const SIGN_IN_LOCKED = "Unlock the vault to sign in.";
8
+ export declare const SIGN_IN_UNKEPT = "The vault didn't keep the sign-in. Try again.";
9
+ export declare const DEVICE_NEEDS_VAULT = "Signing in needs seven's vault: update seven";
10
+ export interface DeviceSignIn {
11
+ url: string;
12
+ code?: string;
13
+ }
14
+ export interface ConnectOptions {
15
+ method?: 'sign-in' | 'key';
16
+ }
17
+ export declare function httpsUrl(value: unknown): value is string;
@@ -2,6 +2,7 @@ import type { Approval, ApprovalDecision, ApprovalsEvents, ApprovalsPort } from
2
2
  import type { Approval as Item } from '../approvals/record.ts';
3
3
  import type { AgentConnection } from '../client.ts';
4
4
  import { Emitter } from './emitter.ts';
5
+ export declare const ARGS_LENGTH = 65536;
5
6
  export declare function shown(item: Item, agentName: string | undefined): Approval;
6
7
  export type HostApprovals = ApprovalsPort;
7
8
  export declare const NOT_HERE = "This request can't be answered in this tab.";
@@ -21,6 +21,7 @@ export interface McpLoginHost {
21
21
  timeoutMs?: number;
22
22
  }
23
23
  export declare function safeAuthorizationUrl(value: string): boolean;
24
+ export declare function untilAborted<T>(promise: Promise<T>, signal: AbortSignal): Promise<T>;
24
25
  export declare function mcpLogin(host?: McpLoginHost): (providerId: string, signIn: () => Promise<SignIn | McpSignIn | null>, options: {
25
26
  signal: AbortSignal;
26
27
  }) => Promise<string>;
@@ -1,7 +1,9 @@
1
1
  import type { Account, ModelOption, Settings, SettingsEvents, SettingsPort } from '@ai-ecoverse/slicc-spectrum/ui';
2
2
  import type { AgentSettings, ProviderOption, SignIn } from '../services.ts';
3
+ import { type ConnectOptions } from '../sign-ins.ts';
3
4
  import type { AgentAdapter } from './agent-port.ts';
4
5
  import { Emitter } from './emitter.ts';
6
+ import { type Popup } from './mcp-login.ts';
5
7
  export declare const SETTINGS_KEY = "slicc-agent.settings";
6
8
  export type Sealed = {
7
9
  sealed: true;
@@ -11,20 +13,21 @@ export type Login = (providerId: string, signIn: () => Promise<SignIn | null>, o
11
13
  }) => Promise<string | Sealed>;
12
14
  export declare const SIGN_IN_NEEDS_VAULT = "Adobe sign-in needs seven's vault: update seven";
13
15
  export declare const UNFINISHED = "The sign-in didn't finish. Try again.";
16
+ export type OpenPopup = () => Popup | null;
14
17
  export declare function readable(error: unknown): Error;
15
18
  type Events = SettingsEvents & {
16
19
  providers: readonly ProviderOption[];
17
20
  };
18
21
  export declare class SettingsAdapter extends Emitter<Events> implements SettingsPort {
19
22
  #private;
20
- constructor(settings: AgentSettings, agent: AgentAdapter, storage: Pick<Storage, 'getItem' | 'setItem'> | null, login?: Login | null, mcp?: Login);
23
+ constructor(settings: AgentSettings, agent: AgentAdapter, storage: Pick<Storage, 'getItem' | 'setItem'> | null, login?: Login | null, mcp?: Login, open?: OpenPopup);
21
24
  get(): Settings;
22
25
  update(patch: Partial<Settings>): void;
23
26
  models(): readonly ModelOption[];
24
27
  providers(): readonly ProviderOption[];
25
28
  remove(id: string): Promise<void>;
26
29
  accounts(): readonly Account[];
27
- connect(id: string, secret?: string, fields?: Record<string, string>): Promise<void>;
30
+ connect(id: string, secret?: string, fields?: Record<string, string>, options?: ConnectOptions): Promise<void>;
28
31
  cancel(id: string): void;
29
32
  disconnect(id: string): void;
30
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "6.15.2",
3
+ "version": "6.17.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",
@@ -65,10 +65,10 @@
65
65
  "release": "semantic-release"
66
66
  },
67
67
  "devDependencies": {
68
- "@ai-ecoverse/slicc-kernel": "1.134.1",
68
+ "@ai-ecoverse/slicc-kernel": "1.134.3",
69
69
  "@ai-ecoverse/slicc-scrub": "1.0.0",
70
- "@ai-ecoverse/slicc-shared-web": "1.25.1",
71
- "@ai-ecoverse/slicc-spectrum": "1.101.1",
70
+ "@ai-ecoverse/slicc-shared-web": "1.27.0",
71
+ "@ai-ecoverse/slicc-spectrum": "1.101.2",
72
72
  "@ai-ecoverse/wasi-fnox": "1.39.0-5",
73
73
  "@ai-ecoverse/wasm-bash": "5.3.0-21",
74
74
  "@ai-ecoverse/wasm-coreutils": "9.12.0-10",