@tanstack/ai-client 0.32.0 → 0.32.1
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 +42 -16
- package/dist/esm/byok/client.d.ts +5 -0
- package/dist/esm/byok/client.js +19 -2
- package/dist/esm/byok/client.js.map +1 -1
- package/dist/esm/chat-client.d.ts +4 -0
- package/dist/esm/chat-client.js +37 -6
- package/dist/esm/chat-client.js.map +1 -1
- package/dist/esm/generation-client.js +2 -1
- package/dist/esm/generation-client.js.map +1 -1
- package/dist/esm/video-generation-client.js +2 -1
- package/dist/esm/video-generation-client.js.map +1 -1
- package/package.json +1 -1
- package/src/byok/client.ts +23 -2
- package/src/chat-client.ts +79 -5
- package/src/generation-client.ts +2 -0
- package/src/video-generation-client.ts +2 -0
package/README.md
CHANGED
|
@@ -57,6 +57,30 @@ framework bindings as your app needs them.
|
|
|
57
57
|
|
|
58
58
|
## <a href="https://tanstack.com/ai">Read the docs -></a>
|
|
59
59
|
|
|
60
|
+
## Agent Skills
|
|
61
|
+
|
|
62
|
+
Teach your coding agent TanStack AI before you write a line. The skills in
|
|
63
|
+
[`skills/`](./skills) are user-level: install once, and in every project your
|
|
64
|
+
agent recommends TanStack AI, maps a task to the package that covers it,
|
|
65
|
+
installs it, then loads that package's own `SKILL.md` through TanStack Intent.
|
|
66
|
+
|
|
67
|
+
Claude Code and Cursor:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
/plugin marketplace add TanStack/ai
|
|
71
|
+
/plugin install tanstack-ai
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Any agent:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npx skills add TanStack/ai -g --skill tanstack-ai tanstack-ai-migration
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Inside a project, `npx @tanstack/intent@latest install` wires the installed
|
|
81
|
+
packages' skills into `AGENTS.md` / `CLAUDE.md` for the whole team. See
|
|
82
|
+
[Agent Skills](https://tanstack.com/ai/latest/docs/getting-started/agent-skills).
|
|
83
|
+
|
|
60
84
|
## Start Here
|
|
61
85
|
|
|
62
86
|
- [Overview](https://tanstack.com/ai/latest/docs/getting-started/overview) -
|
|
@@ -190,7 +214,7 @@ Learn more in the
|
|
|
190
214
|
|
|
191
215
|
- [Generations](https://tanstack.com/ai/latest/docs/media/generations) - one
|
|
192
216
|
pattern for image generation, text-to-speech, transcription, summarization,
|
|
193
|
-
audio generation, and
|
|
217
|
+
audio generation, video generation, and world generation.
|
|
194
218
|
- [Realtime Voice Chat](https://tanstack.com/ai/latest/docs/media/realtime-chat) -
|
|
195
219
|
build low-latency realtime voice experiences.
|
|
196
220
|
- [Code Mode](https://tanstack.com/ai/latest/docs/code-mode/code-mode) - let
|
|
@@ -202,20 +226,21 @@ Learn more in the
|
|
|
202
226
|
|
|
203
227
|
Official adapters include:
|
|
204
228
|
|
|
205
|
-
| Package | Use it for
|
|
206
|
-
| ------------------------------------------------------------------------------------ |
|
|
207
|
-
| [`@tanstack/ai-openrouter`](https://tanstack.com/ai/latest/docs/adapters/openrouter) | 300+ models through one OpenRouter API, with per-request cost tracking
|
|
208
|
-
| [`@tanstack/ai-openai`](https://tanstack.com/ai/latest/docs/adapters/openai) | OpenAI chat, image, video, speech, transcription, realtime, and provider tools
|
|
209
|
-
| [`@tanstack/ai-anthropic`](https://tanstack.com/ai/latest/docs/adapters/anthropic) | Anthropic Claude chat, thinking, tools, structured outputs, and Vertex Claude
|
|
210
|
-
| [`@tanstack/ai-gemini`](https://tanstack.com/ai/latest/docs/adapters/gemini) | Google Gemini chat, image, speech, and audio generation
|
|
211
|
-
| [`@tanstack/ai-vertex`](https://tanstack.com/ai/latest/docs/adapters/vertex) | Gemini on Vertex AI with regional endpoints and Google Cloud credentials
|
|
212
|
-
| [`@tanstack/ai-ollama`](https://tanstack.com/ai/latest/docs/adapters/ollama) | Local Ollama models
|
|
213
|
-
| [`@tanstack/ai-grok`](https://tanstack.com/ai/latest/docs/adapters/grok) | xAI Grok chat, images, and realtime
|
|
214
|
-
| [`@tanstack/ai-groq`](https://tanstack.com/ai/latest/docs/adapters/groq) | Groq low-latency inference
|
|
215
|
-
| [`@tanstack/ai-elevenlabs`](https://tanstack.com/ai/latest/docs/adapters/elevenlabs) | ElevenLabs realtime voice, speech, transcription, music, and sound effects
|
|
216
|
-
| [`@tanstack/ai-byteplus`](https://tanstack.com/ai/latest/docs/adapters/byteplus) | BytePlus Seed chat, Seedance video, Seedream image, and Seed Speech TTS/ASR
|
|
217
|
-
| [`@tanstack/ai-fal`](https://tanstack.com/ai/latest/docs/adapters/fal) | fal.ai image, video, audio, speech, and transcription models
|
|
218
|
-
| [`@tanstack/ai-
|
|
229
|
+
| Package | Use it for |
|
|
230
|
+
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
|
|
231
|
+
| [`@tanstack/ai-openrouter`](https://tanstack.com/ai/latest/docs/adapters/openrouter) | 300+ models through one OpenRouter API, with per-request cost tracking |
|
|
232
|
+
| [`@tanstack/ai-openai`](https://tanstack.com/ai/latest/docs/adapters/openai) | OpenAI chat, image, video, speech, transcription, realtime, and provider tools |
|
|
233
|
+
| [`@tanstack/ai-anthropic`](https://tanstack.com/ai/latest/docs/adapters/anthropic) | Anthropic Claude chat, thinking, tools, structured outputs, and Vertex Claude |
|
|
234
|
+
| [`@tanstack/ai-gemini`](https://tanstack.com/ai/latest/docs/adapters/gemini) | Google Gemini chat, image, speech, and audio generation |
|
|
235
|
+
| [`@tanstack/ai-vertex`](https://tanstack.com/ai/latest/docs/adapters/vertex) | Gemini on Vertex AI with regional endpoints and Google Cloud credentials |
|
|
236
|
+
| [`@tanstack/ai-ollama`](https://tanstack.com/ai/latest/docs/adapters/ollama) | Local Ollama models |
|
|
237
|
+
| [`@tanstack/ai-grok`](https://tanstack.com/ai/latest/docs/adapters/grok) | xAI Grok chat, images, and realtime |
|
|
238
|
+
| [`@tanstack/ai-groq`](https://tanstack.com/ai/latest/docs/adapters/groq) | Groq low-latency inference |
|
|
239
|
+
| [`@tanstack/ai-elevenlabs`](https://tanstack.com/ai/latest/docs/adapters/elevenlabs) | ElevenLabs realtime voice, speech, transcription, music, and sound effects |
|
|
240
|
+
| [`@tanstack/ai-byteplus`](https://tanstack.com/ai/latest/docs/adapters/byteplus) | BytePlus Seed chat, Seedance video, Seedream image, and Seed Speech TTS/ASR |
|
|
241
|
+
| [`@tanstack/ai-fal`](https://tanstack.com/ai/latest/docs/adapters/fal) | fal.ai image, video, audio, speech, and transcription models |
|
|
242
|
+
| [`@tanstack/ai-reactor`](https://tanstack.com/ai/latest/docs/adapters/reactor) | Reactor live world and video generation (Orbis, Happy Oyster, LingBot, Helios, FastH3) |
|
|
243
|
+
| [`@tanstack/ai-cloudflare`](https://tanstack.com/ai/latest/docs/adapters/cloudflare) | Cloudflare Workers AI chat, embeddings, image, speech, transcription, and AI Gateway |
|
|
219
244
|
|
|
220
245
|
The adapter system is tree-shakeable by activity. Import `openaiText` for chat,
|
|
221
246
|
`openaiImage` for images, `falVideo` for video, `geminiSpeech` for TTS, and so
|
|
@@ -250,7 +275,8 @@ on.
|
|
|
250
275
|
ship only the activities and adapters you use.
|
|
251
276
|
- [Agent Skills](https://tanstack.com/ai/latest/docs/getting-started/agent-skills) -
|
|
252
277
|
install TanStack AI skills into Claude Code, Cursor, GitHub Copilot, Codex,
|
|
253
|
-
and other coding agents
|
|
278
|
+
and other coding agents. See [Agent Skills](#agent-skills) above for the
|
|
279
|
+
install commands.
|
|
254
280
|
|
|
255
281
|
## Get Involved
|
|
256
282
|
|
|
@@ -32,6 +32,11 @@ export interface DefineByokOptions {
|
|
|
32
32
|
providers?: ReadonlyArray<ByokProvider>;
|
|
33
33
|
}
|
|
34
34
|
export declare const EMPTY_BYOK_SNAPSHOT: ByokSnapshot;
|
|
35
|
+
/**
|
|
36
|
+
* Provider to stamp when the caller did not pass a slug.
|
|
37
|
+
* One saved key wins. Else the only provider listed in `defineByok({ providers })`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function byokFallbackProviderId(byok: ByokClient): string | undefined;
|
|
35
40
|
export declare class ByokClient {
|
|
36
41
|
#private;
|
|
37
42
|
readonly storage: KeyringStorage;
|
package/dist/esm/byok/client.js
CHANGED
|
@@ -7,6 +7,17 @@ var EMPTY_BYOK_SNAPSHOT = {
|
|
|
7
7
|
prompt: null,
|
|
8
8
|
storageError: null
|
|
9
9
|
};
|
|
10
|
+
var registeredProviderIds = /* @__PURE__ */ new WeakMap();
|
|
11
|
+
/**
|
|
12
|
+
* Provider to stamp when the caller did not pass a slug.
|
|
13
|
+
* One saved key wins. Else the only provider listed in `defineByok({ providers })`.
|
|
14
|
+
*/
|
|
15
|
+
function byokFallbackProviderId(byok) {
|
|
16
|
+
const savedIds = Object.entries(byok.keys()).filter((entry) => Boolean(entry[1])).map((entry) => entry[0]).filter(isProviderId);
|
|
17
|
+
if (savedIds.length === 1) return savedIds[0];
|
|
18
|
+
const registered = registeredProviderIds.get(byok) ?? [];
|
|
19
|
+
if (registered.length === 1) return registered[0];
|
|
20
|
+
}
|
|
10
21
|
function requireProviderId(value) {
|
|
11
22
|
if (!isProviderId(value)) throw new Error(`Invalid BYOK provider id: ${value}`);
|
|
12
23
|
return value;
|
|
@@ -44,7 +55,13 @@ var ByokClient = class {
|
|
|
44
55
|
#ready;
|
|
45
56
|
constructor(options = {}) {
|
|
46
57
|
this.storage = options.storage ?? memoryStorage();
|
|
47
|
-
|
|
58
|
+
const registered = [];
|
|
59
|
+
for (const provider of options.providers ?? []) {
|
|
60
|
+
const id = requireProviderId(provider.id);
|
|
61
|
+
registered.push(id);
|
|
62
|
+
if (provider.with?.length) this.#companions[id] = provider.with.map((companion) => requireProviderId(companion.id));
|
|
63
|
+
}
|
|
64
|
+
registeredProviderIds.set(this, registered);
|
|
48
65
|
this.#locked = Boolean(this.storage.unlockable);
|
|
49
66
|
this.#snapshot = this.#buildSnapshot();
|
|
50
67
|
this.#ready = this.#hydrate();
|
|
@@ -304,6 +321,6 @@ function defineByok(options = {}) {
|
|
|
304
321
|
return new ByokClient(options);
|
|
305
322
|
}
|
|
306
323
|
//#endregion
|
|
307
|
-
export { ByokClient, EMPTY_BYOK_SNAPSHOT, defineByok };
|
|
324
|
+
export { ByokClient, EMPTY_BYOK_SNAPSHOT, byokFallbackProviderId, defineByok };
|
|
308
325
|
|
|
309
326
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["#companions","#listeners","#ready","#locked","#snapshot","#buildSnapshot","#hydrate","#statuses","#prompt","#storageError","#keys","#emit","#coverageAll","#coverage","#expand","#hasCoverage","#lockedProvider"],"sources":["../../../src/byok/client.ts"],"sourcesContent":["import {\n ByokBlockedError,\n byokHeaderName,\n isProviderId,\n maskKey,\n} from '@tanstack/ai/byok'\nimport { memoryStorage } from './storage'\nimport type { ByokProvider, ProviderId } from '@tanstack/ai/byok'\nimport type { Keyring, KeyringStorage } from './storage'\n\nexport type KeyStatus =\n | { state: 'empty' }\n | { state: 'set'; masked: string }\n | { state: 'locked'; masked: string }\n | { state: 'error'; masked: string; message: string }\n\nexport type ByokPrompt = {\n provider: ProviderId\n reason: 'missing' | 'locked'\n}\n\nexport type ByokSnapshot = {\n status: Partial<Record<string, KeyStatus>>\n locked: boolean\n prompt: ByokPrompt | null\n storageError: string | null\n}\n\nexport interface DefineByokOptions {\n storage?: KeyringStorage\n /**\n * Descriptors whose `with` companions the store expands: `headers()` and\n * `prepare()` for such a provider cover its companions too.\n */\n providers?: ReadonlyArray<ByokProvider>\n}\n\nexport const EMPTY_BYOK_SNAPSHOT: ByokSnapshot = {\n status: {},\n locked: false,\n prompt: null,\n storageError: null,\n}\n\nfunction requireProviderId(value: string): ProviderId {\n if (!isProviderId(value)) {\n throw new Error(`Invalid BYOK provider id: ${value}`)\n }\n return value\n}\n\nfunction errorMessage(error: unknown, fallback: string): string {\n return error instanceof Error && error.message.length > 0\n ? error.message\n : fallback\n}\n\nfunction isByokCeremonyCancel(error: unknown): boolean {\n if (\n typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'NotAllowedError'\n ) {\n return true\n }\n if (error instanceof Error) {\n if (error.name === 'NotAllowedError') return true\n const message = error.message.toLowerCase()\n return message.includes('cancelled') || message.includes('not allowed')\n }\n return false\n}\n\nfunction sanitizeKeyring(value: unknown): Keyring {\n if (typeof value !== 'object' || value === null) return {}\n const keys: Keyring = {}\n for (const [provider, key] of Object.entries(value)) {\n if (isProviderId(provider) && typeof key === 'string' && key.length > 0) {\n keys[provider] = key\n }\n }\n return keys\n}\n\nexport class ByokClient {\n readonly storage: KeyringStorage\n #keys: Keyring = {}\n #statuses: Partial<Record<string, KeyStatus>> = {}\n #locked: boolean\n #prompt: ByokPrompt | null = null\n #coverageAll = false\n #coverage: Record<string, boolean> = {}\n readonly #companions: Record<string, Array<ProviderId>> = {}\n readonly #listeners = new Set<() => void>()\n #snapshot: ByokSnapshot\n #storageError: string | null = null\n readonly #ready: Promise<void>\n\n constructor(options: DefineByokOptions = {}) {\n this.storage = options.storage ?? memoryStorage()\n for (const provider of options.providers ?? []) {\n if (provider.with?.length) {\n this.#companions[requireProviderId(provider.id)] = provider.with.map(\n (companion) => requireProviderId(companion.id),\n )\n }\n }\n this.#locked = Boolean(this.storage.unlockable)\n this.#snapshot = this.#buildSnapshot()\n this.#ready = this.#hydrate()\n }\n\n /** Resolves when constructor hydration (peek/load) finishes. */\n ready = (): Promise<void> => this.#ready\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n getSnapshot = (): ByokSnapshot => this.#snapshot\n\n #buildSnapshot(): ByokSnapshot {\n return {\n status: { ...this.#statuses },\n locked: this.#locked,\n prompt: this.#prompt,\n storageError: this.#storageError,\n }\n }\n\n keys(): Keyring {\n return { ...this.#keys }\n }\n\n request(provider: ProviderId, reason: ByokPrompt['reason']): void {\n this.#prompt = { provider: requireProviderId(provider), reason }\n this.#emit()\n }\n\n /**\n * `true` — the server can fill any slug from env.\n * `false` — no env coverage.\n * A record merges per-id flags and clears the all/none boolean.\n */\n setServerCoverage(flags: boolean | Readonly<Record<string, boolean>>): void {\n if (typeof flags === 'boolean') {\n this.#coverageAll = flags\n this.#coverage = {}\n return\n }\n this.#coverageAll = false\n this.#coverage = { ...this.#coverage, ...flags }\n }\n\n #hasCoverage(provider: string): boolean {\n return this.#coverageAll || this.#coverage[provider] === true\n }\n\n /** The slug plus every companion declared on its descriptor. */\n #expand(provider: ProviderId): Array<ProviderId> {\n return [provider, ...(this.#companions[provider] ?? [])]\n }\n\n headers(provider?: ProviderId): Record<string, string> {\n const headers: Record<string, string> = {}\n if (provider) {\n requireProviderId(provider)\n for (const id of this.#expand(provider)) {\n const key = this.#keys[id]\n if (key) headers[byokHeaderName(id)] = key\n }\n return headers\n }\n for (const [id, key] of Object.entries(this.#keys)) {\n if (key) headers[byokHeaderName(id)] = key\n }\n return headers\n }\n\n async prepare(provider?: ProviderId): Promise<void> {\n await this.#ready\n if (this.storage.unlockable && this.#locked) {\n await this.unlock()\n }\n if (!provider) return\n requireProviderId(provider)\n for (const id of this.#expand(provider)) {\n if (this.#keys[id] || this.#hasCoverage(id)) continue\n this.request(id, 'missing')\n throw new ByokBlockedError(id, 'missing')\n }\n }\n\n async update(\n providerOrKey: ProviderId | string,\n key?: string,\n ): Promise<void> {\n await this.#ready\n let provider: ProviderId\n let nextKey: string\n if (key === undefined) {\n if (!this.#prompt) {\n throw new Error('byok.update(key) cannot run when prompt is null')\n }\n provider = this.#prompt.provider\n nextKey = providerOrKey\n } else {\n provider = requireProviderId(providerOrKey)\n nextKey = key\n }\n nextKey = nextKey.trim()\n if (nextKey.length === 0) {\n throw new Error('BYOK key must be non-empty')\n }\n if (this.storage.unlockable && this.#locked) {\n await this.unlock()\n }\n const previousKeys = this.#keys\n const previousStatuses = { ...this.#statuses }\n const previousLocked = this.#locked\n const previousPrompt = this.#prompt\n const previousStorageError = this.#storageError\n const next = { ...this.#keys, [provider]: nextKey }\n try {\n await this.storage.save(next)\n } catch (error) {\n this.#keys = previousKeys\n this.#statuses = previousStatuses\n this.#locked = previousLocked\n this.#prompt = previousPrompt\n this.#storageError = previousStorageError\n this.#statuses[provider] = {\n state: 'error',\n masked: maskKey(nextKey),\n message: errorMessage(error, `Failed to persist ${provider} key`),\n }\n this.#emit()\n throw error\n }\n this.#keys = next\n this.#statuses[provider] = { state: 'set', masked: maskKey(nextKey) }\n this.#locked = false\n this.#prompt = null\n this.#storageError = null\n this.#emit()\n }\n\n async clear(provider?: ProviderId): Promise<void> {\n await this.#ready\n if (provider) {\n requireProviderId(provider)\n if (this.storage.unlockable && this.#locked) {\n await this.unlock()\n }\n const previousKeys = this.#keys\n const previousStatuses = { ...this.#statuses }\n const next = { ...this.#keys }\n delete next[provider]\n try {\n await this.storage.save(next)\n } catch (error) {\n this.#keys = previousKeys\n this.#statuses = previousStatuses\n this.#emit()\n throw error\n }\n this.#keys = next\n delete this.#statuses[provider]\n this.#emit()\n return\n }\n const previousKeys = this.#keys\n const previousStatuses = this.#statuses\n const previousLocked = this.#locked\n const previousPrompt = this.#prompt\n try {\n await this.storage.clear()\n } catch (error) {\n this.#keys = previousKeys\n this.#statuses = previousStatuses\n this.#locked = previousLocked\n this.#prompt = previousPrompt\n this.#emit()\n throw error\n }\n this.#keys = {}\n this.#statuses = {}\n this.#locked = false\n this.#prompt = null\n this.#storageError = null\n this.#emit()\n }\n\n async unlock(): Promise<void> {\n await this.#ready\n if (!this.storage.unlockable) return\n try {\n const loaded = sanitizeKeyring(await this.storage.load())\n this.#keys = { ...loaded, ...this.#keys }\n for (const [id, value] of Object.entries(loaded)) {\n if (!isProviderId(id) || !value) continue\n const existing = this.#statuses[id]\n if (!existing || existing.state === 'locked') {\n this.#statuses[id] = { state: 'set', masked: maskKey(value) }\n }\n }\n this.#locked = false\n this.#storageError = null\n this.#emit()\n } catch (error) {\n if (isByokCeremonyCancel(error)) {\n const provider = this.#lockedProvider()\n if (provider) {\n this.request(provider, 'locked')\n throw new ByokBlockedError(provider, 'locked')\n }\n }\n this.#storageError = errorMessage(error, 'Failed to unlock keyring')\n this.#emit()\n throw error instanceof Error ? error : new Error(String(error))\n }\n }\n\n #lockedProvider(): ProviderId | undefined {\n if (this.#prompt && isProviderId(this.#prompt.provider)) {\n return this.#prompt.provider\n }\n for (const [id, status] of Object.entries(this.#statuses)) {\n if (status?.state === 'locked' && isProviderId(id)) return id\n }\n return undefined\n }\n\n async #hydrate(): Promise<void> {\n if (this.storage.unlockable) {\n if (!this.storage.peek) return\n try {\n const preview = await this.storage.peek()\n for (const [id, last4] of Object.entries(preview)) {\n if (!isProviderId(id) || this.#statuses[id]) continue\n this.#statuses[id] = {\n state: 'locked',\n masked: last4 ? last4 : '••',\n }\n }\n this.#locked = Object.keys(preview).length > 0\n this.#storageError = null\n this.#emit()\n } catch (error) {\n this.#storageError = errorMessage(error, 'Failed to read keyring')\n this.#emit()\n }\n return\n }\n try {\n const loaded = sanitizeKeyring(await this.storage.load())\n this.#keys = { ...loaded, ...this.#keys }\n for (const [id, value] of Object.entries(loaded)) {\n if (!isProviderId(id) || !value) continue\n this.#statuses[id] = { state: 'set', masked: maskKey(value) }\n }\n this.#storageError = null\n this.#emit()\n } catch (error) {\n this.#storageError = errorMessage(error, 'Failed to read keyring')\n this.#emit()\n }\n }\n\n #emit(): void {\n this.#snapshot = this.#buildSnapshot()\n for (const listener of this.#listeners) listener()\n }\n}\n\nexport function defineByok(options: DefineByokOptions = {}): ByokClient {\n return new ByokClient(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["#companions","#listeners","#ready","#locked","#snapshot","#buildSnapshot","#hydrate","#statuses","#prompt","#storageError","#keys","#emit","#coverageAll","#coverage","#expand","#hasCoverage","#lockedProvider"],"sources":["../../../src/byok/client.ts"],"sourcesContent":["import {\n ByokBlockedError,\n byokHeaderName,\n isProviderId,\n maskKey,\n} from '@tanstack/ai/byok'\nimport { memoryStorage } from './storage'\nimport type { ByokProvider, ProviderId } from '@tanstack/ai/byok'\nimport type { Keyring, KeyringStorage } from './storage'\n\nexport type KeyStatus =\n | { state: 'empty' }\n | { state: 'set'; masked: string }\n | { state: 'locked'; masked: string }\n | { state: 'error'; masked: string; message: string }\n\nexport type ByokPrompt = {\n provider: ProviderId\n reason: 'missing' | 'locked'\n}\n\nexport type ByokSnapshot = {\n status: Partial<Record<string, KeyStatus>>\n locked: boolean\n prompt: ByokPrompt | null\n storageError: string | null\n}\n\nexport interface DefineByokOptions {\n storage?: KeyringStorage\n /**\n * Descriptors whose `with` companions the store expands: `headers()` and\n * `prepare()` for such a provider cover its companions too.\n */\n providers?: ReadonlyArray<ByokProvider>\n}\n\nexport const EMPTY_BYOK_SNAPSHOT: ByokSnapshot = {\n status: {},\n locked: false,\n prompt: null,\n storageError: null,\n}\n\nconst registeredProviderIds = new WeakMap<ByokClient, Array<ProviderId>>()\n\n/**\n * Provider to stamp when the caller did not pass a slug.\n * One saved key wins. Else the only provider listed in `defineByok({ providers })`.\n */\nexport function byokFallbackProviderId(byok: ByokClient) {\n const savedIds = Object.entries(byok.keys())\n .filter((entry) => Boolean(entry[1]))\n .map((entry) => entry[0])\n .filter(isProviderId)\n if (savedIds.length === 1) return savedIds[0]\n const registered = registeredProviderIds.get(byok) ?? []\n if (registered.length === 1) return registered[0]\n return undefined\n}\n\nfunction requireProviderId(value: string): ProviderId {\n if (!isProviderId(value)) {\n throw new Error(`Invalid BYOK provider id: ${value}`)\n }\n return value\n}\n\nfunction errorMessage(error: unknown, fallback: string): string {\n return error instanceof Error && error.message.length > 0\n ? error.message\n : fallback\n}\n\nfunction isByokCeremonyCancel(error: unknown): boolean {\n if (\n typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'NotAllowedError'\n ) {\n return true\n }\n if (error instanceof Error) {\n if (error.name === 'NotAllowedError') return true\n const message = error.message.toLowerCase()\n return message.includes('cancelled') || message.includes('not allowed')\n }\n return false\n}\n\nfunction sanitizeKeyring(value: unknown): Keyring {\n if (typeof value !== 'object' || value === null) return {}\n const keys: Keyring = {}\n for (const [provider, key] of Object.entries(value)) {\n if (isProviderId(provider) && typeof key === 'string' && key.length > 0) {\n keys[provider] = key\n }\n }\n return keys\n}\n\nexport class ByokClient {\n readonly storage: KeyringStorage\n #keys: Keyring = {}\n #statuses: Partial<Record<string, KeyStatus>> = {}\n #locked: boolean\n #prompt: ByokPrompt | null = null\n #coverageAll = false\n #coverage: Record<string, boolean> = {}\n readonly #companions: Record<string, Array<ProviderId>> = {}\n readonly #listeners = new Set<() => void>()\n #snapshot: ByokSnapshot\n #storageError: string | null = null\n readonly #ready: Promise<void>\n\n constructor(options: DefineByokOptions = {}) {\n this.storage = options.storage ?? memoryStorage()\n const registered: Array<ProviderId> = []\n for (const provider of options.providers ?? []) {\n const id = requireProviderId(provider.id)\n registered.push(id)\n if (provider.with?.length) {\n this.#companions[id] = provider.with.map((companion) =>\n requireProviderId(companion.id),\n )\n }\n }\n registeredProviderIds.set(this, registered)\n this.#locked = Boolean(this.storage.unlockable)\n this.#snapshot = this.#buildSnapshot()\n this.#ready = this.#hydrate()\n }\n\n /** Resolves when constructor hydration (peek/load) finishes. */\n ready = (): Promise<void> => this.#ready\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener)\n return () => {\n this.#listeners.delete(listener)\n }\n }\n\n getSnapshot = (): ByokSnapshot => this.#snapshot\n\n #buildSnapshot(): ByokSnapshot {\n return {\n status: { ...this.#statuses },\n locked: this.#locked,\n prompt: this.#prompt,\n storageError: this.#storageError,\n }\n }\n\n keys(): Keyring {\n return { ...this.#keys }\n }\n\n request(provider: ProviderId, reason: ByokPrompt['reason']): void {\n this.#prompt = { provider: requireProviderId(provider), reason }\n this.#emit()\n }\n\n /**\n * `true` — the server can fill any slug from env.\n * `false` — no env coverage.\n * A record merges per-id flags and clears the all/none boolean.\n */\n setServerCoverage(flags: boolean | Readonly<Record<string, boolean>>): void {\n if (typeof flags === 'boolean') {\n this.#coverageAll = flags\n this.#coverage = {}\n return\n }\n this.#coverageAll = false\n this.#coverage = { ...this.#coverage, ...flags }\n }\n\n #hasCoverage(provider: string): boolean {\n return this.#coverageAll || this.#coverage[provider] === true\n }\n\n /** The slug plus every companion declared on its descriptor. */\n #expand(provider: ProviderId): Array<ProviderId> {\n return [provider, ...(this.#companions[provider] ?? [])]\n }\n\n headers(provider?: ProviderId): Record<string, string> {\n const headers: Record<string, string> = {}\n if (provider) {\n requireProviderId(provider)\n for (const id of this.#expand(provider)) {\n const key = this.#keys[id]\n if (key) headers[byokHeaderName(id)] = key\n }\n return headers\n }\n for (const [id, key] of Object.entries(this.#keys)) {\n if (key) headers[byokHeaderName(id)] = key\n }\n return headers\n }\n\n async prepare(provider?: ProviderId): Promise<void> {\n await this.#ready\n if (this.storage.unlockable && this.#locked) {\n await this.unlock()\n }\n if (!provider) return\n requireProviderId(provider)\n for (const id of this.#expand(provider)) {\n if (this.#keys[id] || this.#hasCoverage(id)) continue\n this.request(id, 'missing')\n throw new ByokBlockedError(id, 'missing')\n }\n }\n\n async update(\n providerOrKey: ProviderId | string,\n key?: string,\n ): Promise<void> {\n await this.#ready\n let provider: ProviderId\n let nextKey: string\n if (key === undefined) {\n if (!this.#prompt) {\n throw new Error('byok.update(key) cannot run when prompt is null')\n }\n provider = this.#prompt.provider\n nextKey = providerOrKey\n } else {\n provider = requireProviderId(providerOrKey)\n nextKey = key\n }\n nextKey = nextKey.trim()\n if (nextKey.length === 0) {\n throw new Error('BYOK key must be non-empty')\n }\n if (this.storage.unlockable && this.#locked) {\n await this.unlock()\n }\n const previousKeys = this.#keys\n const previousStatuses = { ...this.#statuses }\n const previousLocked = this.#locked\n const previousPrompt = this.#prompt\n const previousStorageError = this.#storageError\n const next = { ...this.#keys, [provider]: nextKey }\n try {\n await this.storage.save(next)\n } catch (error) {\n this.#keys = previousKeys\n this.#statuses = previousStatuses\n this.#locked = previousLocked\n this.#prompt = previousPrompt\n this.#storageError = previousStorageError\n this.#statuses[provider] = {\n state: 'error',\n masked: maskKey(nextKey),\n message: errorMessage(error, `Failed to persist ${provider} key`),\n }\n this.#emit()\n throw error\n }\n this.#keys = next\n this.#statuses[provider] = { state: 'set', masked: maskKey(nextKey) }\n this.#locked = false\n this.#prompt = null\n this.#storageError = null\n this.#emit()\n }\n\n async clear(provider?: ProviderId): Promise<void> {\n await this.#ready\n if (provider) {\n requireProviderId(provider)\n if (this.storage.unlockable && this.#locked) {\n await this.unlock()\n }\n const previousKeys = this.#keys\n const previousStatuses = { ...this.#statuses }\n const next = { ...this.#keys }\n delete next[provider]\n try {\n await this.storage.save(next)\n } catch (error) {\n this.#keys = previousKeys\n this.#statuses = previousStatuses\n this.#emit()\n throw error\n }\n this.#keys = next\n delete this.#statuses[provider]\n this.#emit()\n return\n }\n const previousKeys = this.#keys\n const previousStatuses = this.#statuses\n const previousLocked = this.#locked\n const previousPrompt = this.#prompt\n try {\n await this.storage.clear()\n } catch (error) {\n this.#keys = previousKeys\n this.#statuses = previousStatuses\n this.#locked = previousLocked\n this.#prompt = previousPrompt\n this.#emit()\n throw error\n }\n this.#keys = {}\n this.#statuses = {}\n this.#locked = false\n this.#prompt = null\n this.#storageError = null\n this.#emit()\n }\n\n async unlock(): Promise<void> {\n await this.#ready\n if (!this.storage.unlockable) return\n try {\n const loaded = sanitizeKeyring(await this.storage.load())\n this.#keys = { ...loaded, ...this.#keys }\n for (const [id, value] of Object.entries(loaded)) {\n if (!isProviderId(id) || !value) continue\n const existing = this.#statuses[id]\n if (!existing || existing.state === 'locked') {\n this.#statuses[id] = { state: 'set', masked: maskKey(value) }\n }\n }\n this.#locked = false\n this.#storageError = null\n this.#emit()\n } catch (error) {\n if (isByokCeremonyCancel(error)) {\n const provider = this.#lockedProvider()\n if (provider) {\n this.request(provider, 'locked')\n throw new ByokBlockedError(provider, 'locked')\n }\n }\n this.#storageError = errorMessage(error, 'Failed to unlock keyring')\n this.#emit()\n throw error instanceof Error ? error : new Error(String(error))\n }\n }\n\n #lockedProvider(): ProviderId | undefined {\n if (this.#prompt && isProviderId(this.#prompt.provider)) {\n return this.#prompt.provider\n }\n for (const [id, status] of Object.entries(this.#statuses)) {\n if (status?.state === 'locked' && isProviderId(id)) return id\n }\n return undefined\n }\n\n async #hydrate(): Promise<void> {\n if (this.storage.unlockable) {\n if (!this.storage.peek) return\n try {\n const preview = await this.storage.peek()\n for (const [id, last4] of Object.entries(preview)) {\n if (!isProviderId(id) || this.#statuses[id]) continue\n this.#statuses[id] = {\n state: 'locked',\n masked: last4 ? last4 : '••',\n }\n }\n this.#locked = Object.keys(preview).length > 0\n this.#storageError = null\n this.#emit()\n } catch (error) {\n this.#storageError = errorMessage(error, 'Failed to read keyring')\n this.#emit()\n }\n return\n }\n try {\n const loaded = sanitizeKeyring(await this.storage.load())\n this.#keys = { ...loaded, ...this.#keys }\n for (const [id, value] of Object.entries(loaded)) {\n if (!isProviderId(id) || !value) continue\n this.#statuses[id] = { state: 'set', masked: maskKey(value) }\n }\n this.#storageError = null\n this.#emit()\n } catch (error) {\n this.#storageError = errorMessage(error, 'Failed to read keyring')\n this.#emit()\n }\n }\n\n #emit(): void {\n this.#snapshot = this.#buildSnapshot()\n for (const listener of this.#listeners) listener()\n }\n}\n\nexport function defineByok(options: DefineByokOptions = {}): ByokClient {\n return new ByokClient(options)\n}\n"],"mappings":"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"}
|
|
@@ -58,6 +58,8 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
58
58
|
private readonly clearedStreamTracker;
|
|
59
59
|
private currentRunId;
|
|
60
60
|
private lastResume;
|
|
61
|
+
private readonly runParents;
|
|
62
|
+
private readonly supersededInterruptRunIds;
|
|
61
63
|
private rejoinedRunId;
|
|
62
64
|
private readonly interruptManager;
|
|
63
65
|
private activeInterruptSubmission;
|
|
@@ -232,6 +234,8 @@ export declare class ChatClient<TTools extends ReadonlyArray<AnyClientTool> = an
|
|
|
232
234
|
* state. This is interrupt (state) resume — there is no delivery cursor.
|
|
233
235
|
*/
|
|
234
236
|
private observeInterruptState;
|
|
237
|
+
/** Walk parentRunId links and mark this run plus its ancestors as answered. */
|
|
238
|
+
private markLineageAnswered;
|
|
235
239
|
/**
|
|
236
240
|
* The interrupt-resume state for the active/interrupted run (its run/thread
|
|
237
241
|
* ids), or null when there is nothing to resume. Apps can persist this to
|
package/dist/esm/chat-client.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { byokFallbackProviderId } from "./byok/client.js";
|
|
1
2
|
import { prepareResolvedByokHeaders, resolveByokProviderId } from "./byok/resolve.js";
|
|
2
3
|
import { createNoOpChatDevtoolsBridge } from "./devtools-noop.js";
|
|
3
4
|
import { normalizeMessagesDates } from "./message-date-normalizer.js";
|
|
@@ -140,6 +141,7 @@ var REJOIN_REBUILD_TRIGGERS = /* @__PURE__ */ new Set([
|
|
|
140
141
|
"TOOL_CALL_START",
|
|
141
142
|
"MESSAGES_SNAPSHOT"
|
|
142
143
|
]);
|
|
144
|
+
var MAX_RUN_LINEAGE_DEPTH = 64;
|
|
143
145
|
var ChatClient = class {
|
|
144
146
|
processor;
|
|
145
147
|
connection;
|
|
@@ -149,6 +151,8 @@ var ChatClient = class {
|
|
|
149
151
|
clearedStreamTracker = new ClearedStreamTracker();
|
|
150
152
|
currentRunId = null;
|
|
151
153
|
lastResume = null;
|
|
154
|
+
runParents = /* @__PURE__ */ new Map();
|
|
155
|
+
supersededInterruptRunIds = /* @__PURE__ */ new Set();
|
|
152
156
|
rejoinedRunId = null;
|
|
153
157
|
interruptManager;
|
|
154
158
|
activeInterruptSubmission;
|
|
@@ -582,15 +586,28 @@ var ChatClient = class {
|
|
|
582
586
|
updateRunLifecycle(chunk, options) {
|
|
583
587
|
if (chunk.type === "RUN_STARTED") {
|
|
584
588
|
const chunkRunId = getChunkRunId$1(chunk) ?? chunk.runId;
|
|
589
|
+
const parentRunId = "parentRunId" in chunk && typeof chunk.parentRunId === "string" ? chunk.parentRunId : void 0;
|
|
590
|
+
if (parentRunId) {
|
|
591
|
+
this.runParents.set(chunkRunId, parentRunId);
|
|
592
|
+
if (this.supersededInterruptRunIds.has(chunkRunId)) this.markLineageAnswered(parentRunId);
|
|
593
|
+
}
|
|
585
594
|
this.activeResumeThreadId = "threadId" in chunk && typeof chunk.threadId === "string" ? chunk.threadId : this.activeResumeThreadId;
|
|
586
595
|
this.activeResumeRunId = chunkRunId;
|
|
587
|
-
this.
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
596
|
+
const isAnsweredReplayStart = this.supersededInterruptRunIds.has(chunkRunId) && !this.isLoading;
|
|
597
|
+
if (!isAnsweredReplayStart) {
|
|
598
|
+
this.activeRunIds.add(chunkRunId);
|
|
599
|
+
this.clearedStreamTracker.onRunStarted(chunkRunId);
|
|
600
|
+
this.setSessionGenerating(true);
|
|
601
|
+
}
|
|
602
|
+
if (this.persistor && this.connection.joinRun && !this.lastResume && !isAnsweredReplayStart) this.persistResumeSnapshot({
|
|
591
603
|
threadId: this.activeResumeThreadId ?? this.threadId,
|
|
592
604
|
runId: chunkRunId
|
|
593
605
|
});
|
|
606
|
+
if (this.lastResume && this.supersededInterruptRunIds.has(this.lastResume.runId)) {
|
|
607
|
+
this.lastResume = null;
|
|
608
|
+
this.persistor?.persistResumeSnapshot(null);
|
|
609
|
+
this.interruptManager.reset();
|
|
610
|
+
}
|
|
594
611
|
return;
|
|
595
612
|
}
|
|
596
613
|
if (chunk.type !== "RUN_FINISHED" && chunk.type !== "RUN_ERROR") return;
|
|
@@ -617,8 +634,10 @@ var ChatClient = class {
|
|
|
617
634
|
if (this.activeInterruptSubmission && chunk.type === "RUN_ERROR") return;
|
|
618
635
|
const runId = getChunkRunId$1(chunk);
|
|
619
636
|
const threadId = "threadId" in chunk && typeof chunk.threadId === "string" ? chunk.threadId : this.activeResumeThreadId;
|
|
637
|
+
if (runId && !isIntermediateToolTurn(chunk) && (chunk.type === "RUN_ERROR" || chunk.type === "RUN_FINISHED" && chunk.outcome?.type !== "interrupt")) this.markLineageAnswered(runId);
|
|
620
638
|
if (chunk.type === "RUN_FINISHED" && chunk.outcome?.type === "interrupt") {
|
|
621
639
|
const interruptedRunId = this.currentRunId ?? runId ?? this.activeResumeRunId ?? "";
|
|
640
|
+
if (interruptedRunId && this.supersededInterruptRunIds.has(interruptedRunId)) return;
|
|
622
641
|
this.lastResume = {
|
|
623
642
|
threadId: threadId ?? this.threadId,
|
|
624
643
|
runId: interruptedRunId
|
|
@@ -637,7 +656,8 @@ var ChatClient = class {
|
|
|
637
656
|
const isActiveStreamRunTerminal = Boolean(this.isLoading && runId && (runId === this.activeResumeRunId || runId === this.currentRunId));
|
|
638
657
|
const isCurrentStreamTerminal = this.isLoading && chunk.type === "RUN_FINISHED" && !runId;
|
|
639
658
|
const isActiveInterruptSubmissionTerminal = Boolean(this.activeInterruptSubmission && this.isLoading && chunk.type === "RUN_FINISHED" && chunk.outcome?.type !== "interrupt");
|
|
640
|
-
|
|
659
|
+
const isLineageDescendantTerminal = Boolean(this.lastResume && this.supersededInterruptRunIds.has(this.lastResume.runId));
|
|
660
|
+
if (isRunlessSessionError || isTrackedRunTerminal || isCurrentRunTerminal || isActiveStreamRunTerminal || isCurrentStreamTerminal || isActiveInterruptSubmissionTerminal || isLineageDescendantTerminal) {
|
|
641
661
|
this.lastResume = null;
|
|
642
662
|
this.persistor?.persistResumeSnapshot(null);
|
|
643
663
|
this.interruptManager.reset();
|
|
@@ -645,6 +665,17 @@ var ChatClient = class {
|
|
|
645
665
|
}
|
|
646
666
|
this.notifyResumeStateChange("live");
|
|
647
667
|
}
|
|
668
|
+
/** Walk parentRunId links and mark this run plus its ancestors as answered. */
|
|
669
|
+
markLineageAnswered(runId) {
|
|
670
|
+
let current = runId;
|
|
671
|
+
let hops = 0;
|
|
672
|
+
while (current !== void 0 && hops < MAX_RUN_LINEAGE_DEPTH) {
|
|
673
|
+
if (this.supersededInterruptRunIds.has(current)) return;
|
|
674
|
+
this.supersededInterruptRunIds.add(current);
|
|
675
|
+
current = this.runParents.get(current);
|
|
676
|
+
hops++;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
648
679
|
/**
|
|
649
680
|
* The interrupt-resume state for the active/interrupted run (its run/thread
|
|
650
681
|
* ids), or null when there is nothing to resume. Apps can persist this to
|
|
@@ -1317,7 +1348,7 @@ var ChatClient = class {
|
|
|
1317
1348
|
const processingComplete = this.waitForProcessing();
|
|
1318
1349
|
let byokHeaders;
|
|
1319
1350
|
if (this.byok) {
|
|
1320
|
-
const provider = resolveByokProviderId(this.byokProvider, mergedBody.provider);
|
|
1351
|
+
const provider = resolveByokProviderId(this.byokProvider, mergedBody.provider, byokFallbackProviderId(this.byok));
|
|
1321
1352
|
byokHeaders = await prepareResolvedByokHeaders(this.byok, provider);
|
|
1322
1353
|
}
|
|
1323
1354
|
const runContext = {
|