typebulb 0.16.1 → 0.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.
package/README.md CHANGED
@@ -14,7 +14,7 @@ A `.bulb.md` file bundles code, styles, data, and config in one file.
14
14
 
15
15
  ## Features
16
16
 
17
- - **Server-side code** — Add a `**server.ts**` section; exported functions become callable from the browser via `tb.server.<name>()` (e.g., `export async function query(...)` → `await tb.server.query(...)`). Requires `--trust`.
17
+ - **Server-side code** — Add a `**server.ts**` section; exported functions become callable from the browser via `tb.server.<name>()` (e.g., `export async function query(...)` → `await tb.server.query(...)`). An `export async function*` **streams**: consume it with `for await (const chunk of tb.server.gen())`. Requires `--trust`.
18
18
  - **CLI logging** — `tb.server.log(...)` prints to the CLI's stdout
19
19
  - **Wake-on-event** — `typebulb wait <file|agent>` blocks until the target server logs a new line, prints it, and exits. Run in the background, that exit *is* an agent's wake-up: a user action a bulb logs, or an embed's render outcome — no polling.
20
20
  - **Env files** — `.env` / `.env.local` load from cwd, `.env.local` overriding `.env` (an exported shell var wins over both). `--mode <name>` adds `.env.<name>` to switch environments (local/staging/prod); a startup line reports which keys loaded from where.
@@ -29,6 +29,7 @@ A `.bulb.md` file bundles code, styles, data, and config in one file.
29
29
  - **Restricted by default** — A plain `npx typebulb my-app.bulb.md` runs with no filesystem or `server.ts` (like typebulb.com); `--trust` grants those for a run. Trust is **remembered**: `typebulb trust <file>` elevates a bulb once so later plain runs are trusted, `untrust` revokes it, and `--no-trust` forces a Restricted run.
30
30
  - **Predict trust** — `typebulb predict <file>` reports the capability a bulb will likely need (fs / AI / `server.ts`) without running it, so you can decide on `--trust` up front rather than after a mid-run permission failure.
31
31
  - **Agent mirror** — a browser view of your project's Claude Code sessions that renders embedded bulbs, KaTeX, and mermaid live inline, plus runs/stops local bulbs (see [Claude](#claude)). `typebulb agent:claude` opens it. `typebulb agent` (no target) is the first command an agent runs: it brings up the mirror without opening a browser, prints its link, and points at the authoring skill. `typebulb skill` prints this whole README as an Agent Skill the agent can read and save.
32
+ - **Proxying Claude** — the agent mirror lets you proxy Claude with a model from [OpenRouter](https://openrouter.ai). This will apply to your project only.
32
33
 
33
34
  ## Usage
34
35
 
@@ -180,6 +181,7 @@ npm install -g typebulb
180
181
  | `tb.fs.read/readBytes/write` | Read and write local files | ✅ `--trust` | ❌ |
181
182
  | `tb.server.<name>(...)` | Call a function exported from the `server.ts` block | ✅ `--trust` | ❌ |
182
183
  | `tb.ai({ messages, … })` | General-purpose AI call (chat, agents) | ✅ `--trust` | ❌ |
184
+ | `tb.ai.stream({ … })` | Streaming AI — `for await` an `AsyncIterable<{ kind, text }>` of deltas | ✅ `--trust` | ❌ |
183
185
  | `tb.infer()` | One-shot LLM call driven by the `infer.md` block | ❌ | ❌ |
184
186
 
185
187
  - **❌ (embedded):** the call throws `"not available in an embedded bulb"` — an embed is a client-only sandboxed iframe with **no persistent storage** either (`localStorage`, `IndexedDB`, cookies, same-origin Workers all fail), so keep state in memory. `tb.mode === 'embedded'` lets a bulb detect this and self-adjust.
@@ -260,7 +262,7 @@ Run a bulb **once** and let hot reload drive the loop.
260
262
  - **Mount to the container your `index.html` declares.** The corpus convention is `<div id="root"></div>` with `createRoot(document.getElementById("root")!)`.
261
263
  - **All imports at the top of `code.tsx`.** Bare imports (`react`, `d3`, `three`, …) auto-resolve from a CDN — no install step. Declare them in `config.json` `dependencies` anyway: that's what lets `npx typebulb check` fetch type defs (without it you get errors like `TS2875: react/jsx-runtime`) and pins versions.
262
264
  - **Theme-aware styling.** Style off CSS variables / `currentColor` so the bulb reads correctly in both light and dark; the host sets the theme.
263
- - **`tb.ai()` takes more than the basics** — the full shape is `tb.ai({ messages, system?, reasoning?, provider?, model?, webSearch? })` → `Promise<{ text }>` (non-streaming). `webSearch` defaults **on** in the CLI (you supply your own key); pass `webSearch: false` to turn it off.
265
+ - **`tb.ai()` takes more than the basics** — the full shape is `tb.ai({ messages, system?, reasoning?, provider?, model?, webSearch? })` → `Promise<{ text }>`. `webSearch` defaults **on** in the CLI (you supply your own key); pass `webSearch: false` to turn it off. For token-by-token output use `tb.ai.stream(...)` (see [`tb.ai()` § Streaming](#streaming)).
264
266
  - **`tb.theme` drives the `html[data-theme]` attribute** — style off that selector (`html[data-theme="dark"] { … }`); don't read `tb.theme` to branch your rendering.
265
267
  - **`color-scheme` is set for you** — the host always applies `html[data-theme="dark"] { color-scheme: dark }` / `html[data-theme="light"] { color-scheme: light }` on top of your `styles.css`.
266
268
  - **Math (KaTeX) renders in your replies** — write inline `$…$` / display `$$…$$` (prefer `$y = x^2$` over inline-code or a Unicode `y = x²`). The mirror's KaTeX renders only in prose and doesn't reach inside a fenced block (bulb, mermaid, svg, code).
@@ -322,9 +324,16 @@ Which must be declared in the dependencies section:
322
324
 
323
325
  Typebulb has a package resolver that will load and cache these packages from `esm.sh` when the bulb runs.
324
326
 
325
- ## `tb.ai()`
327
+ ## Custom AI Models
326
328
 
327
- Trusted bulbs can call AI providers **from their own code** at runtime, billed to your API keys. Don't confuse this with the agent loop ([Wake-on-event](#wake-on-event)): there Claude drives a bulb from the chat session and *is* the intelligence — no provider, no key, and the bulb stays plain. `tb.ai()` is for bulbs that are themselves AI apps (chatbots, agents, experiments). Add API keys to your `.env` file:
329
+ Two ways to use non-default models in typebulb:
330
+
331
+ * **`tb.ai()`** — a bulb's own code calling AI providers with your keys
332
+ * **proxy claude** — backs your `claude` sessions with an alternate (OpenRouter) model
333
+
334
+ ### .env setup
335
+
336
+ Add API keys to your `.env` file:
328
337
 
329
338
  | Provider name | API key env var |
330
339
  |---------------|-----------------|
@@ -332,17 +341,24 @@ Trusted bulbs can call AI providers **from their own code** at runtime, billed t
332
341
  | `openai` | `OPENAI_API_KEY` |
333
342
  | `gemini` | `GOOGLE_API_KEY` |
334
343
  | `openrouter` | `OPENROUTER_API_KEY` |
344
+ | `ollama` | *(none — local server)* |
335
345
 
336
- Set your default provider and model:
346
+ Optionally, set your default provider and model:
337
347
 
338
348
  ```
339
349
  TB_AI_PROVIDER=anthropic
340
350
  TB_AI_MODEL=claude-haiku-4-5-20251001
341
351
  ```
342
352
 
343
- Both can be overridden per-call: `tb.ai({ provider: "gemini", model: "gemini-3.1-flash-lite", ... })`.
353
+ Run `typebulb models` to list the models available for the providers specified.
354
+
355
+ ### `tb.ai()`
344
356
 
345
- Run `typebulb models` to list the available model ids instead of guessing one.
357
+ Trusted bulbs can call AI providers **from their own code** at runtime, billed to your API keys.
358
+
359
+ You can call the provider and model explicitly like this: `tb.ai({ provider: "gemini", model: "gemini-3.1-flash-lite", ... })`.
360
+
361
+ Or you can rely on the default provider and model if you set them in `.env`.
346
362
 
347
363
  ### Reasoning
348
364
 
@@ -362,7 +378,26 @@ const { text } = await tb.ai({
362
378
  });
363
379
  ```
364
380
 
365
- Provider support varies — the level is mapped to provider-specific parameters (e.g. Anthropic's adaptive thinking, OpenAI's reasoning effort).
381
+ ### Streaming
382
+
383
+ `tb.ai.stream({ … })` is the streaming counterpart of `tb.ai()` — an async iterable of `{ kind: "text" | "reasoning", text }` deltas. `tb.ai()` (await the full text) is unchanged; reach for `.stream` only when a response is long enough to be worth showing as it arrives.
384
+
385
+ ```ts
386
+ let answer = "";
387
+ for await (const c of tb.ai.stream({ messages })) {
388
+ if (c.kind === "text") { answer += c.text; render(answer); } // c.kind === "reasoning" for thinking deltas
389
+ }
390
+ ```
391
+
392
+ Breaking the loop stops the stream; same options as `tb.ai()`. **`kind: "reasoning"` chunks require `reasoning: 1-3` and a thinking-capable model**.
393
+
394
+ ### Local Models
395
+
396
+ `provider: "ollama"` talks to a local [Ollama](https://ollama.com) server over its OpenAI-compatible endpoint — no API key, defaults to `http://localhost:11434` (override with `OLLAMA_HOST`). CLI-only (typebulb.com can't reach your localhost). `typebulb models` lists your installed Ollama models alongside cloud ones.
397
+
398
+ ### Proxying Claude
399
+
400
+ The user can proxy claude with the agent mirror's model switcher, to any model on [OpenRouter](https://openrouter.ai) model instead of Anthropic. This lets the user use OpenRouter models with Claude Code's harness.
366
401
 
367
402
  ## Charts
368
403
 
@@ -940,6 +940,128 @@ const something = require('module-name') // NOT SUPPORTED!
940
940
  } catch { console.log(...args); }
941
941
  };
942
942
 
943
+ // Streaming. The server tunnels an async iterable as enveloped NDJSON over
944
+ // the bridge POST; isStreamResp flags it via a response header so a normal single-return call is
945
+ // untouched. readStream yields each {type:'chunk'} value; a {type:'error'} line rejects the
946
+ // iterator (so try/catch around for-await fires); breaking the loop cancels the body (finally),
947
+ // which disconnects the server and tears down the source generator \u2014 that's how Stop/unmount
948
+ // actually aborts the upstream rather than just hiding it.
949
+ const isStreamResp = (resp) => resp.headers.get('X-TB-Stream') === '1';
950
+ async function* readStream(resp) {
951
+ const reader = resp.body.getReader();
952
+ const dec = new TextDecoder();
953
+ let buf = '';
954
+ const decodeLine = (line) => {
955
+ if (!line.trim()) return undefined;
956
+ const env = JSON.parse(line);
957
+ if (env.type === 'error') {
958
+ const e = new Error((env.error && env.error.message) || 'stream error');
959
+ e.code = env.error && env.error.code;
960
+ e.retryable = !!(env.error && env.error.retryable);
961
+ throw e;
962
+ }
963
+ return env.type === 'chunk' ? { value: env.value } : undefined;
964
+ };
965
+ try {
966
+ while (true) {
967
+ const { done, value } = await reader.read();
968
+ if (done) break;
969
+ buf += dec.decode(value, { stream: true });
970
+ let nl;
971
+ while ((nl = buf.indexOf('\\n')) !== -1) {
972
+ const r = decodeLine(buf.slice(0, nl));
973
+ buf = buf.slice(nl + 1);
974
+ if (r) yield r.value;
975
+ }
976
+ }
977
+ const r = decodeLine(buf);
978
+ if (r) yield r.value;
979
+ } finally {
980
+ try { await reader.cancel(); } catch {}
981
+ }
982
+ }
983
+
984
+ // tb.ai(): non-streaming, resolves with the full { text } (unchanged 90% path).
985
+ const aiCall = async ({ messages, system, reasoning, provider, model, webSearch, signal } = {}) => {
986
+ if (isEmbedded) throw embedErr('tb.ai()');
987
+ const resp = await fetch('/__ai', {
988
+ method: 'POST',
989
+ headers: { 'Content-Type': 'application/json' },
990
+ body: JSON.stringify({ messages, system, reasoning, provider, model, webSearch }),
991
+ signal
992
+ });
993
+ if (resp.status === 403) throw new Error((await resp.text().catch(() => '')) || 'tb.ai() is blocked \u2014 re-run with --trust');
994
+ const data = await resp.json();
995
+ if (!resp.ok) {
996
+ const err = new Error(data.message || 'tb.ai() call failed');
997
+ err.code = data.code || 'unknown';
998
+ err.retryable = !!data.retryable;
999
+ throw err;
1000
+ }
1001
+ return data;
1002
+ };
1003
+ // tb.ai.stream(): async iterable of AiChunk ({ kind:'text'|'reasoning', text }). Same idiom as a
1004
+ // streaming tb.server.<gen>(). Break the loop (or abort the signal) to cancel.
1005
+ const aiStream = (opts = {}) => (async function* () {
1006
+ if (isEmbedded) throw embedErr('tb.ai.stream()');
1007
+ const { messages, system, reasoning, provider, model, webSearch, signal } = opts;
1008
+ const resp = await fetch('/__ai', {
1009
+ method: 'POST',
1010
+ headers: { 'Content-Type': 'application/json' },
1011
+ body: JSON.stringify({ messages, system, reasoning, provider, model, webSearch, stream: true }),
1012
+ signal
1013
+ });
1014
+ if (resp.status === 403) throw new Error((await resp.text().catch(() => '')) || 'tb.ai() is blocked \u2014 re-run with --trust');
1015
+ if (!resp.ok && !isStreamResp(resp)) {
1016
+ const data = await resp.json().catch(() => ({}));
1017
+ const err = new Error(data.message || 'tb.ai() call failed');
1018
+ err.code = data.code || 'unknown';
1019
+ err.retryable = !!data.retryable;
1020
+ throw err;
1021
+ }
1022
+ yield* readStream(resp);
1023
+ })();
1024
+ const ai = Object.assign(aiCall, { stream: aiStream });
1025
+
1026
+ // tb.server.<fn>(...) \u2014 one call object that is both awaitable (single result) and async-iterable
1027
+ // (a streamed async-generator export). The server picks by export kind; this stays graceful if
1028
+ // they're mismatched (await a stream \u2192 array of chunks; for-await a normal result \u2192 one value).
1029
+ const serverCall = (name, args) => {
1030
+ const ensureOk403 = async (resp) => {
1031
+ if (resp.status === 403) throw new Error((await resp.text().catch(() => '')) || ('tb.server.' + name + '() is blocked \u2014 re-run with --trust'));
1032
+ };
1033
+ let respP = null;
1034
+ const start = () => respP || (respP = fetch('/__api/' + name, {
1035
+ method: 'POST',
1036
+ headers: { 'Content-Type': 'application/json' },
1037
+ body: JSON.stringify({ args })
1038
+ }));
1039
+ const single = async () => {
1040
+ if (isEmbedded) throw embedErr('tb.server.' + name + '()');
1041
+ const resp = await start();
1042
+ await ensureOk403(resp);
1043
+ if (isStreamResp(resp)) { const out = []; for await (const v of readStream(resp)) out.push(v); return out; }
1044
+ const data = await resp.json();
1045
+ if (!resp.ok) throw new Error(data.error || 'API call failed');
1046
+ return data.result;
1047
+ };
1048
+ const iterate = async function* () {
1049
+ if (isEmbedded) throw embedErr('tb.server.' + name + '()');
1050
+ const resp = await start();
1051
+ await ensureOk403(resp);
1052
+ if (isStreamResp(resp)) { yield* readStream(resp); return; }
1053
+ const data = await resp.json();
1054
+ if (!resp.ok) throw new Error(data.error || 'API call failed');
1055
+ yield data.result;
1056
+ };
1057
+ return {
1058
+ then: (f, r) => single().then(f, r),
1059
+ catch: (r) => single().catch(r),
1060
+ finally: (f) => single().finally(f),
1061
+ [Symbol.asyncIterator]: iterate
1062
+ };
1063
+ };
1064
+
943
1065
  // tb namespace
944
1066
  globalThis.tb = Object.freeze({
945
1067
  data: (index) => getData()[index],
@@ -956,24 +1078,8 @@ const something = require('module-name') // NOT SUPPORTED!
956
1078
  setData: () => {},
957
1079
  resetInferenceState: () => {},
958
1080
 
959
- // AI - calls local server which proxies to LLM provider
960
- ai: async ({ messages, system, reasoning, provider, model, webSearch }) => {
961
- if (isEmbedded) throw embedErr('tb.ai()');
962
- const resp = await fetch('/__ai', {
963
- method: 'POST',
964
- headers: { 'Content-Type': 'application/json' },
965
- body: JSON.stringify({ messages, system, reasoning, provider, model, webSearch })
966
- });
967
- if (resp.status === 403) throw new Error((await resp.text().catch(() => '')) || 'tb.ai() is blocked \u2014 re-run with --trust');
968
- const data = await resp.json();
969
- if (!resp.ok) {
970
- const err = new Error(data.message || 'tb.ai() call failed');
971
- err.code = data.code || 'unknown';
972
- err.retryable = !!data.retryable;
973
- throw err;
974
- }
975
- return data;
976
- },
1081
+ // AI - tb.ai() proxies to the provider via the local server; tb.ai.stream() streams AiChunks.
1082
+ ai,
977
1083
 
978
1084
  // Model discovery - fetches catalog from typebulb.com, filtered by local API keys
979
1085
  models: async () => {
@@ -1003,27 +1109,17 @@ const something = require('module-name') // NOT SUPPORTED!
1003
1109
  return '/proxy/' + clean;
1004
1110
  },
1005
1111
 
1006
- // Server API - call functions from **server.ts**
1007
- api: async (name, ...args) => {
1008
- if (isEmbedded) throw embedErr('tb.server.' + name + '()');
1009
- const resp = await fetch('/__api/' + name, {
1010
- method: 'POST',
1011
- headers: { 'Content-Type': 'application/json' },
1012
- body: JSON.stringify({ args })
1013
- });
1014
- if (resp.status === 403) throw new Error((await resp.text().catch(() => '')) || ('tb.server.' + name + '() is blocked \u2014 re-run with --trust'));
1015
- const data = await resp.json();
1016
- if (!resp.ok) throw new Error(data.error || 'API call failed');
1017
- return data.result;
1018
- },
1112
+ // Server API - call functions from **server.ts**. Returns the hybrid call object: await it for
1113
+ // a normal export's result, or for-await it for an async-generator export's stream.
1114
+ api: (name, ...args) => serverCall(name, args),
1019
1115
 
1020
- // Server proxy - tb.server.fn(...) delegates to tb.api('fn', ...). 'log' is special: it's the
1021
- // ungated diagnostic (serverLog -> /__log, built-in only), so it prints untrusted; everything
1022
- // else is a gated /__api call into the bulb's own server.ts.
1116
+ // Server proxy - tb.server.fn(...) delegates to serverCall. 'log' is special: it's the ungated
1117
+ // diagnostic (serverLog -> /__log, built-in only), so it prints untrusted; everything else is a
1118
+ // gated /__api call into the bulb's own server.ts.
1023
1119
  server: new Proxy({}, {
1024
1120
  get: (_, name) => name === 'log'
1025
1121
  ? (...args) => serverLog(...args)
1026
- : (...args) => globalThis.tb.api(name, ...args)
1122
+ : (...args) => serverCall(name, args)
1027
1123
  }),
1028
1124
 
1029
1125
  // Filesystem - local CLI extension
@@ -1 +1 @@
1
- {"version":3,"file":"aiProviders.d.ts","sourceRoot":"","sources":["../../ai/src/aiProviders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACzG,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAa5C,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,UAAU,CAMlE;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CAC3D;AAED,uCAAuC;AACvC,wBAAsB,aAAa,CACjC,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,QAAQ,CAAC,CAqBnB;AAED,uEAAuE;AACvE,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAWvC"}
1
+ {"version":3,"file":"aiProviders.d.ts","sourceRoot":"","sources":["../../ai/src/aiProviders.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACzG,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAe5C,4DAA4D;AAC5D,wBAAgB,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,UAAU,CAMlE;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CAC3D;AAED,uCAAuC;AACvC,wBAAsB,aAAa,CACjC,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,QAAQ,CAAC,CAqBnB;AAED,uEAAuE;AACvE,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,gBAAgB,GACzB,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAWvC"}
@@ -2,11 +2,13 @@ import { OpenAIProvider } from './providers/openAI.js';
2
2
  import { OpenRouterProvider } from './providers/openRouter.js';
3
3
  import { AnthropicProvider } from './providers/anthropic.js';
4
4
  import { GeminiProvider } from './providers/gemini.js';
5
+ import { OllamaProvider } from './providers/ollama.js';
5
6
  const providers = new Map([
6
7
  ['openai', new OpenAIProvider()],
7
8
  ['openrouter', new OpenRouterProvider()],
8
9
  ['anthropic', new AnthropicProvider()],
9
- ['gemini', new GeminiProvider()]
10
+ ['gemini', new GeminiProvider()],
11
+ ['ollama', new OllamaProvider()]
10
12
  ]);
11
13
  /** Get the provider implementation for a given protocol. */
12
14
  export function getProvider(protocol) {
@@ -1 +1 @@
1
- {"version":3,"file":"aiProviders.js","sourceRoot":"","sources":["../../ai/src/aiProviders.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,MAAM,SAAS,GAAG,IAAI,GAAG,CAA+B;IACtD,CAAC,QAAQ,EAAE,IAAI,cAAc,EAAE,CAAC;IAChC,CAAC,YAAY,EAAE,IAAI,kBAAkB,EAAE,CAAC;IACxC,CAAC,WAAW,EAAE,IAAI,iBAAiB,EAAE,CAAC;IACtC,CAAC,QAAQ,EAAE,IAAI,cAAc,EAAE,CAAC;CACjC,CAAC,CAAA;AAEF,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,QAA0B;IACpD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAA;IACtD,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAuBD,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAA4B,EAC5B,IAAuB;IAEvB,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAClD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAA;IAEtD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAC/B,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,KAAK,EACV,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,EACxD,IAAI,CAAC,MAAM,CACe,CAAA;IAE5B,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,CAAC,CAAA;IAE7B,OAAO,KAAK,CAAC,GAAG,EAAE;QAChB,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC,CAAA;AACJ,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,QAAkB,EAClB,QAA0B;IAE1B,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;IAClC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IACvD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC/D,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;IAE9B,IAAI,IAAI,GAA+B,SAAS,CAAA;IAChD,IAAI,MAAM,KAAK,GAAG;QAAE,IAAI,GAAG,YAAY,CAAA;SAClC,IAAI,MAAM,KAAK,GAAG;QAAE,IAAI,GAAG,kBAAkB,CAAA;IAElD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,KAAK,GAAG,EAAE,CAAA;AACrD,CAAC"}
1
+ {"version":3,"file":"aiProviders.js","sourceRoot":"","sources":["../../ai/src/aiProviders.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,10 @@
1
1
  export * from './protocol.js';
2
2
  export { AIProvider, ProviderStreamError, type ChatRequestOpts } from './aiProvider.js';
3
3
  export { getProvider, sendAIRequest, normalizeUpstreamError, type ResolvedAIProvider, type SendAIRequestOpts } from './aiProviders.js';
4
- export { parseSseBlock, consumeSseStream, consumeStreamText } from './sseParser.js';
4
+ export { parseSseBlock, consumeSseStream, consumeSseStreamGen, consumeStreamText, streamAiChunks } from './sseParser.js';
5
5
  export * from './providers/anthropic.js';
6
6
  export * from './providers/gemini.js';
7
7
  export * from './providers/openAI.js';
8
8
  export * from './providers/openRouter.js';
9
+ export * from './providers/ollama.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../ai/src/index.ts"],"names":[],"mappings":"AACA,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACvF,OAAO,EACL,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAGnF,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../ai/src/index.ts"],"names":[],"mappings":"AACA,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACvF,OAAO,EACL,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAGxH,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA"}
package/dist/ai/index.js CHANGED
@@ -2,10 +2,11 @@
2
2
  export * from './protocol.js';
3
3
  export { AIProvider, ProviderStreamError } from './aiProvider.js';
4
4
  export { getProvider, sendAIRequest, normalizeUpstreamError } from './aiProviders.js';
5
- export { parseSseBlock, consumeSseStream, consumeStreamText } from './sseParser.js';
5
+ export { parseSseBlock, consumeSseStream, consumeSseStreamGen, consumeStreamText, streamAiChunks } from './sseParser.js';
6
6
  // Per-provider wire DTOs + provider implementations.
7
7
  export * from './providers/anthropic.js';
8
8
  export * from './providers/gemini.js';
9
9
  export * from './providers/openAI.js';
10
10
  export * from './providers/openRouter.js';
11
+ export * from './providers/ollama.js';
11
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../ai/src/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAwB,MAAM,iBAAiB,CAAA;AACvF,OAAO,EACL,WAAW,EACX,aAAa,EACb,sBAAsB,EAGvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEnF,qDAAqD;AACrD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../ai/src/index.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAwB,MAAM,iBAAiB,CAAA;AACvF,OAAO,EACL,WAAW,EACX,aAAa,EACb,sBAAsB,EAGvB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAExH,qDAAqD;AACrD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,2BAA2B,CAAA;AACzC,cAAc,uBAAuB,CAAA"}
@@ -1,9 +1,22 @@
1
1
  export * from './chat.js';
2
2
  export * from './stream.js';
3
- /** AI provider wire protocol identifier */
4
- export type ProviderProtocol = 'openai' | 'anthropic' | 'openrouter' | 'gemini';
3
+ /** AI provider wire protocol identifier.
4
+ * `ollama` is CLI-only (talks to a local server over its OpenAI-compatible endpoint); it
5
+ * resolves with no API key and is never reachable from the web runtime. See
6
+ * specs/TB-AI-Local-Models.md. */
7
+ export type ProviderProtocol = 'openai' | 'anthropic' | 'openrouter' | 'gemini' | 'ollama';
5
8
  /** Extended-thinking hint, 0 (off) – 3 (max) */
6
9
  export type ReasoningDepth = 0 | 1 | 2 | 3;
10
+ /** A streamed delta from `tb.ai.stream()`. Discriminated union — exactly one kind per chunk, so
11
+ * `switch (chunk.kind)` is exhaustive and no `{}`/both-fields state is representable. The public
12
+ * shape; the internal wire piece (`ChatStreamPieceDto { text?, reasoning? }`) stays loose. */
13
+ export type AiChunk = {
14
+ kind: 'text';
15
+ text: string;
16
+ } | {
17
+ kind: 'reasoning';
18
+ text: string;
19
+ };
7
20
  /** tb.models() response — model available to the current user */
8
21
  export interface TbModelDto {
9
22
  /** Provider protocol: "anthropic", "openai", "gemini", "openrouter" */
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../ai/src/protocol.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAE3B,2CAA2C;AAC3C,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAA;AAE/E,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAE1C,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAA;IACpB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAA;CACrB"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../ai/src/protocol.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,aAAa,CAAA;AAE3B;;;mCAGmC;AACnC,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE1F,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAE1C;;+FAE+F;AAC/F,MAAM,MAAM,OAAO,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAA;IACpB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAA;CACrB"}
@@ -7,6 +7,8 @@ import { AIProvider, type ChatRequestOpts } from '../aiProvider.js';
7
7
  export type GeminiRole = 'user' | 'model' | 'system';
8
8
  export interface GeminiTextPart {
9
9
  text: string;
10
+ /** When true, this part is a thought summary (reasoning), not answer text. */
11
+ thought?: boolean;
10
12
  }
11
13
  export interface GeminiContent {
12
14
  role: GeminiRole;
@@ -16,6 +18,12 @@ export interface GeminiGoogleSearchTool {
16
18
  google_search: Record<string, never>;
17
19
  }
18
20
  export type GeminiTool = GeminiGoogleSearchTool;
21
+ export interface GeminiThinkingConfig {
22
+ /** Surface thought summaries as `thought` parts in the response. */
23
+ includeThoughts?: boolean;
24
+ /** Token budget for thinking; -1 = dynamic (model self-regulates depth). */
25
+ thinkingBudget?: number;
26
+ }
19
27
  export interface GeminiGenerationConfig {
20
28
  temperature?: number;
21
29
  topP?: number;
@@ -23,6 +31,7 @@ export interface GeminiGenerationConfig {
23
31
  maxOutputTokens?: number;
24
32
  stopSequences?: string[];
25
33
  candidateCount?: number;
34
+ thinkingConfig?: GeminiThinkingConfig;
26
35
  }
27
36
  export type GeminiFinishReason = 'FINISH_REASON_UNSPECIFIED' | 'STOP' | 'MAX_TOKENS' | 'SAFETY' | 'RECITATION' | 'LANGUAGE' | 'OTHER';
28
37
  export interface GeminiCandidate {
@@ -55,7 +64,9 @@ export declare class GeminiProvider extends AIProvider {
55
64
  parseNonStreamingResponse(json: ProviderResponseDto): ChatResponseDto;
56
65
  protected parseProviderStreamChunk(json: ProviderStreamEventDto): ChatStreamPieceDto | null;
57
66
  private isGeminiResponse;
58
- private extractText;
67
+ /** Split a candidate's parts into answer text and thought (reasoning) summaries — Gemini marks
68
+ * thought parts with `thought: true`. Returns the `{ text?, reasoning? }` stream-piece shape. */
69
+ private extractParts;
59
70
  private checkGeminiError;
60
71
  }
61
72
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../ai/src/providers/gemini.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,UAAU,EAAuB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAIxF,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEpD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB;AAGD,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;CACrC;AAED,MAAM,MAAM,UAAU,GAAG,sBAAsB,CAAA;AAG/C,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAGD,MAAM,MAAM,kBAAkB,GAC1B,2BAA2B,GAC3B,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,YAAY,GACZ,UAAU,GACV,OAAO,CAAA;AAEX,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,aAAa,CAAA;IACtB,YAAY,CAAC,EAAE,kBAAkB,CAAA;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAGD,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;IAC9B,cAAc,CAAC,EAAE,oBAAoB,CAAA;CACtC;AAGD,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,CAAA;AAIjD,4CAA4C;AAC5C,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,iBAAiB,CAAC,EAAE,aAAa,CAAA;IACjC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAA;IACpB,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;CAC1C;AAED,qBAAa,cAAe,SAAQ,UAAU;IAC5C,SAAS,CAAC,QAAQ,CAAC,YAAY,YAAW;IAC1C,QAAQ,CAAC,cAAc,+CAA8C;IACrE,QAAQ,CAAC,IAAI,oBAAmB;IAIvB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM;IAMxD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAOpD,YAAY,CACV,QAAQ,EAAE,cAAc,EAAE,EAC1B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,OAAO,GACf,oBAAoB;IA+BvB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB;IAkC/D,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,eAAe;IAoBrE,SAAS,CAAC,wBAAwB,CAAC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,GAAG,IAAI;IAa3F,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,gBAAgB;CAsBzB"}
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../ai/src/providers/gemini.ts"],"names":[],"mappings":"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"}
@@ -35,9 +35,13 @@ export class GeminiProvider extends AIProvider {
35
35
  parts: [{ text: system }]
36
36
  };
37
37
  }
38
+ // Request thought summaries when reasoning is on. `thinkingBudget: -1` = dynamic, so the model
39
+ // self-regulates depth — the robust analog of a 1-3 dial across Gemini models (avoids per-model
40
+ // budget maxes); `includeThoughts` makes those thoughts stream as `thought` parts, which the
41
+ // parser maps to AiChunk `{ kind: "reasoning" }`.
38
42
  if (this.isReasoningEnabled(opts)) {
39
43
  payload.generationConfig = {
40
- temperature: 0.7 + (opts.reasoning * 0.1)
44
+ thinkingConfig: { includeThoughts: true, thinkingBudget: -1 }
41
45
  };
42
46
  }
43
47
  return payload;
@@ -75,7 +79,7 @@ export class GeminiProvider extends AIProvider {
75
79
  if (!this.isGeminiResponse(json)) {
76
80
  return { text: '', status: 'failed', error: 'Invalid response format' };
77
81
  }
78
- const text = this.extractText(json) || '';
82
+ const text = this.extractParts(json).text || '';
79
83
  const finishReason = json.candidates?.[0]?.finishReason;
80
84
  let status = 'complete';
81
85
  if (finishReason === 'MAX_TOKENS') {
@@ -90,10 +94,8 @@ export class GeminiProvider extends AIProvider {
90
94
  this.checkGeminiError(json);
91
95
  if (!this.isGeminiResponse(json))
92
96
  return null;
93
- const text = this.extractText(json);
94
- if (!text)
95
- return null;
96
- return { text };
97
+ const piece = this.extractParts(json);
98
+ return (piece.text || piece.reasoning) ? piece : null;
97
99
  }
98
100
  // ── Private helpers ──────────────────────────────────────────────
99
101
  isGeminiResponse(json) {
@@ -102,14 +104,22 @@ export class GeminiProvider extends AIProvider {
102
104
  'candidates' in json &&
103
105
  Array.isArray(json.candidates));
104
106
  }
105
- extractText(response) {
106
- const candidate = response.candidates?.[0];
107
- if (!candidate?.content?.parts)
108
- return undefined;
109
- return candidate.content.parts
110
- .map(part => part.text)
111
- .filter(Boolean)
112
- .join('');
107
+ /** Split a candidate's parts into answer text and thought (reasoning) summaries — Gemini marks
108
+ * thought parts with `thought: true`. Returns the `{ text?, reasoning? }` stream-piece shape. */
109
+ extractParts(response) {
110
+ const parts = response.candidates?.[0]?.content?.parts;
111
+ if (!parts)
112
+ return {};
113
+ let text = '', reasoning = '';
114
+ for (const part of parts) {
115
+ if (!part.text)
116
+ continue;
117
+ if (part.thought)
118
+ reasoning += part.text;
119
+ else
120
+ text += part.text;
121
+ }
122
+ return { text: text || undefined, reasoning: reasoning || undefined };
113
123
  }
114
124
  checkGeminiError(json) {
115
125
  if (typeof json === 'object' && json !== null && 'error' in json) {
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../ai/src/providers/gemini.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../ai/src/providers/gemini.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Ollama via its OpenAI-compatible endpoint (`/v1/chat/completions`, Chat Completions style
3
+ * with choices[]) — wire types and provider implementation.
4
+ *
5
+ * CLI-only. Ollama runs locally and needs no API key, so `buildHeaders` sends no auth and the
6
+ * resolver supplies an empty key (see runtime/cli/src/serve/server.ts `resolveLocalProvider`).
7
+ * The base URL defaults to http://localhost:11434 and is overridable via OLLAMA_HOST.
8
+ *
9
+ * Ollama does NOT implement the OpenAI Responses API the `openai` protocol targets, which is why
10
+ * this is its own provider rather than a reuse of OpenAIProvider. The native `/api/chat` endpoint
11
+ * (NDJSON, first-class `message.thinking`) is the richer path for reasoning streaming and Ollama
12
+ * options — a deliberate follow-up; see specs/TB-AI-Local-Models.md.
13
+ */
14
+ import type { ChatMessageDto, UpstreamErrorDto, ChatStreamPieceDto, ChatResponseDto, ProviderResponseDto, ProviderStreamEventDto } from '../protocol.js';
15
+ import { AIProvider, type ChatRequestOpts } from '../aiProvider.js';
16
+ export interface OllamaMessageDto {
17
+ content: string;
18
+ reasoning?: string;
19
+ reasoning_content?: string;
20
+ }
21
+ export interface OllamaChoiceDto {
22
+ message?: OllamaMessageDto;
23
+ }
24
+ export interface OllamaResponseDto extends ProviderResponseDto {
25
+ choices: OllamaChoiceDto[];
26
+ }
27
+ export interface OllamaDeltaDto {
28
+ content?: string;
29
+ reasoning?: string;
30
+ reasoning_content?: string;
31
+ }
32
+ export interface OllamaStreamChoiceDto {
33
+ delta?: OllamaDeltaDto;
34
+ message?: OllamaDeltaDto;
35
+ }
36
+ export interface OllamaStreamEventDto extends ProviderStreamEventDto {
37
+ choices: OllamaStreamChoiceDto[];
38
+ }
39
+ /** Ollama OpenAI-compatible request payload */
40
+ export type OllamaRequestPayload = {
41
+ model: string;
42
+ messages: ChatMessageDto[];
43
+ stream: boolean;
44
+ };
45
+ export declare class OllamaProvider extends AIProvider {
46
+ protected readonly providerName = "Ollama";
47
+ readonly defaultBaseUrl = "http://localhost:11434";
48
+ readonly path = "/v1/chat/completions";
49
+ buildHeaders(_apiKey: string): Record<string, string>;
50
+ buildPayload(messages: ChatMessageDto[], model: string, _opts: ChatRequestOpts, stream: boolean): OllamaRequestPayload;
51
+ parseError(errorText: string, status: number): UpstreamErrorDto;
52
+ parseNonStreamingResponse(json: ProviderResponseDto): ChatResponseDto;
53
+ protected parseProviderStreamChunk(json: ProviderStreamEventDto): ChatStreamPieceDto | null;
54
+ private hasChoices;
55
+ }
56
+ //# sourceMappingURL=ollama.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../../ai/src/providers/ollama.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAKnE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IAEf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,gBAAgB,CAAA;CAC3B;AAED,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,OAAO,EAAE,eAAe,EAAE,CAAA;CAC3B;AAGD,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,sBAAsB;IAClE,OAAO,EAAE,qBAAqB,EAAE,CAAA;CACjC;AAID,+CAA+C;AAC/C,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,cAAc,EAAE,CAAA;IAC1B,MAAM,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,qBAAa,cAAe,SAAQ,UAAU;IAC5C,SAAS,CAAC,QAAQ,CAAC,YAAY,YAAW;IAC1C,QAAQ,CAAC,cAAc,4BAA2B;IAClD,QAAQ,CAAC,IAAI,0BAAyB;IAMtC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAUrD,YAAY,CACV,QAAQ,EAAE,cAAc,EAAE,EAC1B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,OAAO,GACd,oBAAoB;IAIvB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB;IAM/D,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,eAAe;IAUrE,SAAS,CAAC,wBAAwB,CAAC,IAAI,EAAE,sBAAsB,GAAG,kBAAkB,GAAG,IAAI;IAmB3F,OAAO,CAAC,UAAU;CAInB"}