typebulb 0.16.2 → 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.
@@ -0,0 +1,58 @@
1
+ import { AIProvider } from '../aiProvider.js';
2
+ export class OllamaProvider extends AIProvider {
3
+ constructor() {
4
+ super(...arguments);
5
+ this.providerName = 'Ollama';
6
+ this.defaultBaseUrl = 'http://localhost:11434';
7
+ this.path = '/v1/chat/completions';
8
+ }
9
+ // ── Request building ─────────────────────────────────────────────
10
+ // Ollama is keyless: send no Authorization header. The `apiKey` arg (empty from the
11
+ // resolver) is ignored.
12
+ buildHeaders(_apiKey) {
13
+ return {
14
+ 'Content-Type': 'application/json',
15
+ 'Accept': 'application/json'
16
+ };
17
+ }
18
+ // Web search and the reasoning-effort knob have no equivalent on the OpenAI-compat endpoint,
19
+ // so the payload stays minimal. Ollama-specific options (num_ctx, think, …) are intentionally
20
+ // out of scope for tb.ai — use a server.ts export for those (specs/TB-Server-Streaming.md).
21
+ buildPayload(messages, model, _opts, stream) {
22
+ return { model, messages, stream };
23
+ }
24
+ parseError(errorText, status) {
25
+ return this.parseJsonError(errorText, status, true);
26
+ }
27
+ // ── Response parsing ─────────────────────────────────────────────
28
+ parseNonStreamingResponse(json) {
29
+ if (!this.hasChoices(json))
30
+ return { text: '' };
31
+ const choice = json.choices[0];
32
+ const msg = choice?.message;
33
+ const text = msg?.content ?? '';
34
+ const reasoning = msg?.reasoning ?? msg?.reasoning_content;
35
+ return { text, reasoning };
36
+ }
37
+ parseProviderStreamChunk(json) {
38
+ if (!this.hasChoices(json))
39
+ return null;
40
+ const choice = json.choices[0];
41
+ if (!choice)
42
+ return null;
43
+ const delta = choice.delta || choice.message;
44
+ if (!delta)
45
+ return null;
46
+ const text = delta.content || undefined;
47
+ const reasoning = delta.reasoning || delta.reasoning_content || undefined;
48
+ if (!text && !reasoning)
49
+ return null;
50
+ return { text, reasoning };
51
+ }
52
+ // ── Private helpers ──────────────────────────────────────────────
53
+ hasChoices(json) {
54
+ return typeof json === 'object' && json !== null
55
+ && 'choices' in json && Array.isArray(json.choices);
56
+ }
57
+ }
58
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../../ai/src/providers/ollama.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,UAAU,EAAwB,MAAM,kBAAkB,CAAA;AA6CnE,MAAM,OAAO,cAAe,SAAQ,UAAU;IAA9C;;QACqB,iBAAY,GAAG,QAAQ,CAAA;QACjC,mBAAc,GAAG,wBAAwB,CAAA;QACzC,SAAI,GAAG,sBAAsB,CAAA;IAgExC,CAAC;IA9DC,oEAAoE;IAEpE,oFAAoF;IACpF,wBAAwB;IACxB,YAAY,CAAC,OAAe;QAC1B,OAAO;YACL,cAAc,EAAE,kBAAkB;YAClC,QAAQ,EAAE,kBAAkB;SAC7B,CAAA;IACH,CAAC;IAED,6FAA6F;IAC7F,8FAA8F;IAC9F,4FAA4F;IAC5F,YAAY,CACV,QAA0B,EAC1B,KAAa,EACb,KAAsB,EACtB,MAAe;QAEf,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;IACpC,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,MAAc;QAC1C,OAAO,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAA;IACrD,CAAC;IAED,oEAAoE;IAEpE,yBAAyB,CAAC,IAAyB;QACjD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;QAE/C,MAAM,MAAM,GAAI,IAA0B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QACrD,MAAM,GAAG,GAAG,MAAM,EAAE,OAAO,CAAA;QAC3B,MAAM,IAAI,GAAG,GAAG,EAAE,OAAO,IAAI,EAAE,CAAA;QAC/B,MAAM,SAAS,GAAG,GAAG,EAAE,SAAS,IAAI,GAAG,EAAE,iBAAiB,CAAA;QAC1D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IAC5B,CAAC;IAES,wBAAwB,CAAC,IAA4B;QAC7D,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QAEvC,MAAM,MAAM,GAAI,IAA6B,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QACxD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAExB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,CAAA;QAC5C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,IAAI,SAAS,CAAA;QACvC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,iBAAiB,IAAI,SAAS,CAAA;QAEzE,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAA;QAEpC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IAC5B,CAAC;IAED,oEAAoE;IAE5D,UAAU,CAAC,IAAa;QAC9B,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;eAC3C,SAAS,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,CAAE,IAA8B,CAAC,OAAO,CAAC,CAAA;IAClF,CAAC;CACF"}
@@ -2,7 +2,7 @@
2
2
  * Generic utilities for parsing Server-Sent Events (SSE) streams.
3
3
  * Shared between client, CLI, and any other consumer of provider SSE streams.
4
4
  */
5
- import type { ProviderProtocol } from './protocol.js';
5
+ import type { ProviderProtocol, AiChunk } from './protocol.js';
6
6
  /**
7
7
  * Parses an SSE block (text between \n\n delimiters) into a data payload.
8
8
  * Handles multiple data: lines and the [DONE] sentinel.
@@ -20,6 +20,12 @@ export declare function parseSseBlock<T = any>(block: string): T | 'done' | null
20
20
  export declare function consumeSseStream<T = any>(reader: ReadableStreamDefaultReader<Uint8Array>, onChunk: (chunk: T) => void, signal?: AbortSignal): Promise<{
21
21
  receivedAnyData: boolean;
22
22
  }>;
23
+ /**
24
+ * Pull-based twin of {@link consumeSseStream}: yields each parsed SSE block as it arrives so a
25
+ * caller can `for await` the stream (and tear it down by breaking the loop). Same framing and
26
+ * `[DONE]` handling; single-sources the SSE parsing. Cancels the reader on early exit.
27
+ */
28
+ export declare function consumeSseStreamGen<T = any>(reader: ReadableStreamDefaultReader<Uint8Array>, signal?: AbortSignal): AsyncGenerator<T>;
23
29
  /**
24
30
  * Consume a provider SSE stream into a single text string.
25
31
  * Handles protocol detection, chunk parsing, and reasoning token filtering.
@@ -29,4 +35,14 @@ export declare function consumeSseStream<T = any>(reader: ReadableStreamDefaultR
29
35
  * @returns Accumulated text content (reasoning tokens discarded)
30
36
  */
31
37
  export declare function consumeStreamText(response: Response, protocol?: ProviderProtocol): Promise<string>;
38
+ /**
39
+ * Adapt a provider's SSE Response into a pull stream of public {@link AiChunk}s — the shared
40
+ * SSE→AiChunk conversion behind `tb.ai.stream()`, used by both the CLI bridge and the web sandbox
41
+ * bridge (each then wraps it in its own transport: NDJSON over HTTP / postMessage frames). Pure and
42
+ * browser-safe: only a `Response` reader plus the already-shared parsing. Each internal
43
+ * `{ text?, reasoning? }` piece becomes one or two discriminated chunks (reasoning before text).
44
+ * A provider error event throws `ProviderStreamError` out of `parseStreamChunk`, which the caller's
45
+ * transport turns into a terminal error so the client iterator rejects.
46
+ */
47
+ export declare function streamAiChunks(response: Response, protocol?: ProviderProtocol): AsyncGenerator<AiChunk>;
32
48
  //# sourceMappingURL=sseParser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sseParser.d.ts","sourceRoot":"","sources":["../../ai/src/sseParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAYrD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI,CAmBvE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,GAAG,GAAG,EAC5C,MAAM,EAAE,2BAA2B,CAAC,UAAU,CAAC,EAC/C,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC3B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAmDvC;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
1
+ {"version":3,"file":"sseParser.d.ts","sourceRoot":"","sources":["../../ai/src/sseParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAsB,MAAM,eAAe,CAAA;AAYlF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI,CAmBvE;AAED;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,GAAG,GAAG,EAC5C,MAAM,EAAE,2BAA2B,CAAC,UAAU,CAAC,EAC/C,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC3B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAmDvC;AAED;;;;GAIG;AACH,wBAAuB,mBAAmB,CAAC,CAAC,GAAG,GAAG,EAChD,MAAM,EAAE,2BAA2B,CAAC,UAAU,CAAC,EAC/C,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,CAAC,CAAC,CA4BnB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,OAAO,CAAC,MAAM,CAAC,CASjB;AAED;;;;;;;;GAQG;AACH,wBAAuB,cAAc,CACnC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,cAAc,CAAC,OAAO,CAAC,CAazB"}
@@ -93,6 +93,48 @@ export async function consumeSseStream(reader, onChunk, signal) {
93
93
  }
94
94
  return { receivedAnyData };
95
95
  }
96
+ /**
97
+ * Pull-based twin of {@link consumeSseStream}: yields each parsed SSE block as it arrives so a
98
+ * caller can `for await` the stream (and tear it down by breaking the loop). Same framing and
99
+ * `[DONE]` handling; single-sources the SSE parsing. Cancels the reader on early exit.
100
+ */
101
+ export async function* consumeSseStreamGen(reader, signal) {
102
+ const decoder = new TextDecoder();
103
+ let buffer = '';
104
+ try {
105
+ while (true) {
106
+ if (signal?.aborted)
107
+ return;
108
+ const { done, value } = await reader.read();
109
+ if (done) {
110
+ if (buffer.trim()) {
111
+ const parsed = parseSseBlock(buffer);
112
+ if (parsed !== null && parsed !== 'done')
113
+ yield parsed;
114
+ }
115
+ return;
116
+ }
117
+ buffer += decoder.decode(value, { stream: true });
118
+ let { pos: sep, len: sepLen } = findSeparator(buffer);
119
+ while (sep !== -1) {
120
+ const block = buffer.slice(0, sep);
121
+ buffer = buffer.slice(sep + sepLen);
122
+ const parsed = parseSseBlock(block);
123
+ if (parsed === 'done')
124
+ return;
125
+ if (parsed !== null)
126
+ yield parsed;
127
+ ({ pos: sep, len: sepLen } = findSeparator(buffer));
128
+ }
129
+ }
130
+ }
131
+ finally {
132
+ try {
133
+ await reader.cancel();
134
+ }
135
+ catch { /* already closed */ }
136
+ }
137
+ }
96
138
  /**
97
139
  * Consume a provider SSE stream into a single text string.
98
140
  * Handles protocol detection, chunk parsing, and reasoning token filtering.
@@ -102,20 +144,41 @@ export async function consumeSseStream(reader, onChunk, signal) {
102
144
  * @returns Accumulated text content (reasoning tokens discarded)
103
145
  */
104
146
  export async function consumeStreamText(response, protocol) {
147
+ if (!response.body)
148
+ throw new Error('Response body is missing');
149
+ // The text projection of streamAiChunks: accumulate `text` deltas, drop reasoning. Protocol
150
+ // resolution, SSE framing, and error-event handling all live there, single-sourced.
151
+ let fullText = '';
152
+ for await (const chunk of streamAiChunks(response, protocol)) {
153
+ if (chunk.kind === 'text')
154
+ fullText += chunk.text;
155
+ }
156
+ return fullText;
157
+ }
158
+ /**
159
+ * Adapt a provider's SSE Response into a pull stream of public {@link AiChunk}s — the shared
160
+ * SSE→AiChunk conversion behind `tb.ai.stream()`, used by both the CLI bridge and the web sandbox
161
+ * bridge (each then wraps it in its own transport: NDJSON over HTTP / postMessage frames). Pure and
162
+ * browser-safe: only a `Response` reader plus the already-shared parsing. Each internal
163
+ * `{ text?, reasoning? }` piece becomes one or two discriminated chunks (reasoning before text).
164
+ * A provider error event throws `ProviderStreamError` out of `parseStreamChunk`, which the caller's
165
+ * transport turns into a terminal error so the client iterator rejects.
166
+ */
167
+ export async function* streamAiChunks(response, protocol) {
168
+ // Like consumeStreamText: if no protocol is passed, read it from the server-set header (the web
169
+ // bridge doesn't track the resolved protocol; the CLI passes it explicitly).
105
170
  const p = protocol
106
- // Header set by server on success. Missing = server-generated error SSE;
107
- // any provider parser handles it via checkAndThrowError's unified format.
108
171
  ?? (response.headers.get('X-Provider-Protocol') || 'openai');
109
172
  const spec = getProvider(p);
110
173
  if (!response.body)
111
- throw new Error('Response body is missing');
174
+ return;
112
175
  const reader = response.body.getReader();
113
- let fullText = '';
114
- await consumeSseStream(reader, (json) => {
176
+ for await (const json of consumeSseStreamGen(reader)) {
115
177
  const piece = spec.parseStreamChunk(json);
178
+ if (piece?.reasoning)
179
+ yield { kind: 'reasoning', text: piece.reasoning };
116
180
  if (piece?.text)
117
- fullText += piece.text;
118
- });
119
- return fullText;
181
+ yield { kind: 'text', text: piece.text };
182
+ }
120
183
  }
121
184
  //# sourceMappingURL=sseParser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sseParser.js","sourceRoot":"","sources":["../../ai/src/sseParser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,sFAAsF;AACtF,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IACvC,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,IAAI,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAA;IACzE,IAAI,EAAE,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,CAAA;IACzC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAA;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAU,KAAa;IAClD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IAE1D,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAElC,MAAM,UAAU,GAAG,SAAS;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;SACpC,IAAI,CAAC,IAAI,CAAC;SACV,IAAI,EAAE,CAAA;IAET,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAA;IAC5B,IAAI,UAAU,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAA;IAE1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAAM,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAA+C,EAC/C,OAA2B,EAC3B,MAAoB;IAEpB,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IACjC,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,IAAI,eAAe,GAAG,KAAK,CAAA;IAE3B,0FAA0F;IAC1F,MAAM,YAAY,GAA0B,MAAM;QAChD,CAAC,CAAC,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YAC/B,IAAI,MAAM,CAAC,OAAO;gBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAA;YAChD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACtF,CAAC,CAAC;QACJ,CAAC,CAAC,IAAI,CAAA;IAER,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,YAAY;YAC7B,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,YAAY,CAAC,CAAC;YACnD,CAAC,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QACvB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAA;QAClC,IAAI,IAAI,EAAE,CAAC;YACT,uCAAuC;YACvC,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,aAAa,CAAI,MAAM,CAAC,CAAA;gBACvC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;oBACzC,OAAO,CAAC,MAAM,CAAC,CAAA;gBACjB,CAAC;YACH,CAAC;YACD,MAAK;QACP,CAAC;QAED,eAAe,GAAG,IAAI,CAAA;QACtB,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;QAEjD,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;QACrD,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;YAClC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,CAAA;YAEnC,MAAM,MAAM,GAAG,aAAa,CAAI,KAAK,CAAC,CAAA;YACtC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;gBACtB,MAAM,GAAG,EAAE,CAAA;gBACX,MAAK;YACP,CAAC;YACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,CAAC,MAAM,CAAC,CAAA;YACjB,CAAC;YAED,CAAC;YAAA,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,eAAe,EAAE,CAAA;AAC5B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAkB,EAClB,QAA2B;IAE3B,MAAM,CAAC,GAAG,QAAQ;QAChB,yEAAyE;QACzE,0EAA0E;WACvE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,QAAQ,CAAqB,CAAA;IAClF,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;IAC3B,IAAI,CAAC,QAAQ,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;IAC/D,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAA;IACxC,IAAI,QAAQ,GAAG,EAAE,CAAA;IAEjB,MAAM,gBAAgB,CAAC,MAAM,EAAE,CAAC,IAAS,EAAE,EAAE;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACzC,IAAI,KAAK,EAAE,IAAI;YAAE,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACjB,CAAC"}
1
+ {"version":3,"file":"sseParser.js","sourceRoot":"","sources":["../../ai/src/sseParser.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@
9
9
  * TypeChecker, and the CLI's emitted typecheck dirs.
10
10
  */
11
11
  /** Typebulb globals available in browser-side code (code.tsx). */
12
- export declare const clientTbTypings = "\n/**\n * Typebulb utilities namespace.\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Async value inspector for tensor-like objects.\n *\n * Materializes lazy values (like GPU tensors) and logs them with metadata.\n * Handles objects with `.js()`, `.data()`, `.array()`, `.arraySync()`, etc.\n *\n * @remarks\n * - Always use `await` - materialization may be async (GPU\u2192CPU readback)\n * - Large values are truncated (max 1000 elements)\n * - Promises are logged as `[Promise]` (not awaited - could hang)\n */\n dump(...args: any[]): Promise<void>;\n /**\n * Trigger inference to generate new insight data.\n *\n * Opens a confirmation modal showing the data to be analyzed, then streams\n * the inference result. On success, updates the insight so subsequent\n * `tb.insight()` calls return the new value.\n *\n * @param opts - Options for inference\n * @param opts.data - Data to pre-populate in the modal (string or array of strings). If omitted, modal opens with empty textarea for user to paste.\n * @returns Promise that resolves with the parsed insight JSON\n * @throws If inference is already in progress, or on network/parse/rate limit errors\n */\n infer<T = unknown>(opts?: { data?: string | string[] }): Promise<T>;\n /**\n * Get the current inference state.\n *\n * @returns 'idle' | 'running' | 'complete' | 'error'\n */\n inferenceState(): 'idle' | 'running' | 'complete' | 'error';\n /**\n * Set a data chunk for the next inference call.\n *\n * Use this to programmatically set data that will be sent when `tb.infer()` is called\n * without the `data` option.\n *\n * @param index - The chunk index (0-based)\n * @param content - The content for this chunk\n */\n setData(index: number, content: string): void;\n /**\n * Proxy a CDN URL through the sandbox origin for Web Worker/WASM same-origin loading.\n *\n * In the sandbox, prepends `/proxy/` so the URL is served from the same origin.\n * Outside the sandbox (exported HTML, CLI), returns the URL unchanged.\n *\n * @param url - Full HTTPS URL to an allowlisted CDN (esm.sh, unpkg.com, cdn.jsdelivr.net, cdnjs.cloudflare.com)\n * @returns The proxied URL (sandbox) or the original URL (standalone/CLI)\n */\n proxy(url: string): string;\n /**\n * Copy text to clipboard.\n * Must be called synchronously within a user gesture (click/keydown).\n * @returns true if successful, false otherwise\n */\n copy(text: string): Promise<boolean>;\n /**\n * Get the canonical URL of this bulb.\n *\n * Returns the parent typebulb.com URL (including path, query, and `#tb=` fragment),\n * resolving correctly from inside the cross-origin sandbox iframe.\n * Use this instead of `location.href` or `document.referrer`.\n *\n * @returns The full canonical URL\n */\n url(): Promise<string>;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * Server-side function proxy.\n *\n * In the CLI, calls exported functions from the `**server.ts**` section.\n * `tb.server.log(...)` is a built-in that prints to CLI stdout (falls back to console.log on web).\n * User exports override built-ins of the same name.\n */\n server: Record<string, (...args: any[]) => Promise<any>>;\n /**\n * Subscribe to a value pushed from the terminal via `typebulb send <file> [message]`.\n *\n * The dual of `tb.server.log` (data out): a value sent *in* from the CLI, no `--trust` required.\n * Use it to start expensive work on demand instead of on load \u2014 e.g. `tb.onMessage(() => start())`\n * \u2014 so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.\n *\n * The message is the JSON-parsed value of what `send` was given (or the raw string if it isn't\n * JSON; `undefined` for a bare `typebulb send <file>`). Returns an unsubscribe function. Inert in\n * an embedded bulb (no sender) \u2014 the handler is registered but never fires.\n *\n * @param handler - Called with each pushed message.\n * @returns An unsubscribe function.\n */\n onMessage(handler: (message: any) => void): () => void;\n /**\n * General-purpose AI call.\n *\n * @param options - Messages, system prompt, optional provider/model override\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */\n reasoning?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n }): Promise<{ text: string }>;\n /**\n * Local filesystem access (CLI only).\n *\n * Paths are resolved relative to the current working directory (where the CLI\n * was launched), consistent with the .env cascade \u2014 not the bulb file's folder.\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * The bulb's theme override (`<html data-theme>`).\n *\n * - Get: the current override \u2014 `'dark'` | `'light'`, or `undefined` when\n * following the OS preference.\n * - Set `'dark'`/`'light'` to force and persist it (per-bulb); set\n * `undefined` to clear the override and follow the OS again.\n *\n * Drives `<html data-theme>`, so render off `html[data-theme=\"\u2026\"]` selectors\n * (or observe the attribute) rather than reading `tb.theme`.\n */\n theme: 'light' | 'dark' | undefined;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
12
+ export declare const clientTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace.\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Async value inspector for tensor-like objects.\n *\n * Materializes lazy values (like GPU tensors) and logs them with metadata.\n * Handles objects with `.js()`, `.data()`, `.array()`, `.arraySync()`, etc.\n *\n * @remarks\n * - Always use `await` - materialization may be async (GPU\u2192CPU readback)\n * - Large values are truncated (max 1000 elements)\n * - Promises are logged as `[Promise]` (not awaited - could hang)\n */\n dump(...args: any[]): Promise<void>;\n /**\n * Trigger inference to generate new insight data.\n *\n * Opens a confirmation modal showing the data to be analyzed, then streams\n * the inference result. On success, updates the insight so subsequent\n * `tb.insight()` calls return the new value.\n *\n * @param opts - Options for inference\n * @param opts.data - Data to pre-populate in the modal (string or array of strings). If omitted, modal opens with empty textarea for user to paste.\n * @returns Promise that resolves with the parsed insight JSON\n * @throws If inference is already in progress, or on network/parse/rate limit errors\n */\n infer<T = unknown>(opts?: { data?: string | string[] }): Promise<T>;\n /**\n * Get the current inference state.\n *\n * @returns 'idle' | 'running' | 'complete' | 'error'\n */\n inferenceState(): 'idle' | 'running' | 'complete' | 'error';\n /**\n * Set a data chunk for the next inference call.\n *\n * Use this to programmatically set data that will be sent when `tb.infer()` is called\n * without the `data` option.\n *\n * @param index - The chunk index (0-based)\n * @param content - The content for this chunk\n */\n setData(index: number, content: string): void;\n /**\n * Proxy a CDN URL through the sandbox origin for Web Worker/WASM same-origin loading.\n *\n * In the sandbox, prepends `/proxy/` so the URL is served from the same origin.\n * Outside the sandbox (exported HTML, CLI), returns the URL unchanged.\n *\n * @param url - Full HTTPS URL to an allowlisted CDN (esm.sh, unpkg.com, cdn.jsdelivr.net, cdnjs.cloudflare.com)\n * @returns The proxied URL (sandbox) or the original URL (standalone/CLI)\n */\n proxy(url: string): string;\n /**\n * Copy text to clipboard.\n * Must be called synchronously within a user gesture (click/keydown).\n * @returns true if successful, false otherwise\n */\n copy(text: string): Promise<boolean>;\n /**\n * Get the canonical URL of this bulb.\n *\n * Returns the parent typebulb.com URL (including path, query, and `#tb=` fragment),\n * resolving correctly from inside the cross-origin sandbox iframe.\n * Use this instead of `location.href` or `document.referrer`.\n *\n * @returns The full canonical URL\n */\n url(): Promise<string>;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * Server-side function proxy.\n *\n * In the CLI, calls exported functions from the `**server.ts**` section.\n * `tb.server.log(...)` is a built-in that prints to CLI stdout (falls back to console.log on web).\n * User exports override built-ins of the same name.\n *\n * A normal export is awaited for its result (`await tb.server.fn()`). An `async function*`\n * export streams: `for await (const chunk of tb.server.gen())`. The call object supports both;\n * break the `for await` to cancel and tear down the server generator.\n */\n server: Record<string, (...args: any[]) => Promise<any> & AsyncIterable<any>>;\n /**\n * Subscribe to a value pushed from the terminal via `typebulb send <file> [message]`.\n *\n * The dual of `tb.server.log` (data out): a value sent *in* from the CLI, no `--trust` required.\n * Use it to start expensive work on demand instead of on load \u2014 e.g. `tb.onMessage(() => start())`\n * \u2014 so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.\n *\n * The message is the JSON-parsed value of what `send` was given (or the raw string if it isn't\n * JSON; `undefined` for a bare `typebulb send <file>`). Returns an unsubscribe function. Inert in\n * an embedded bulb (no sender) \u2014 the handler is registered but never fires.\n *\n * @param handler - Called with each pushed message.\n * @returns An unsubscribe function.\n */\n onMessage(handler: (message: any) => void): () => void;\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */\n reasoning?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `reasoning: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */\n reasoning?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * Local filesystem access (CLI only).\n *\n * Paths are resolved relative to the current working directory (where the CLI\n * was launched), consistent with the .env cascade \u2014 not the bulb file's folder.\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * The bulb's theme override (`<html data-theme>`).\n *\n * - Get: the current override \u2014 `'dark'` | `'light'`, or `undefined` when\n * following the OS preference.\n * - Set `'dark'`/`'light'` to force and persist it (per-bulb); set\n * `undefined` to clear the override and follow the OS again.\n *\n * Drives `<html data-theme>`, so render off `html[data-theme=\"\u2026\"]` selectors\n * (or observe the attribute) rather than reading `tb.theme`.\n */\n theme: 'light' | 'dark' | undefined;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
13
13
  /** Typebulb globals available in Node-side code (server.ts). */
14
- export declare const serverTbTypings = "\n/**\n * Typebulb utilities namespace (server-side).\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * General-purpose AI call.\n *\n * @param options - Messages, system prompt, optional provider/model override\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */\n reasoning?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n }): Promise<{ text: string }>;\n /**\n * Local filesystem access (CLI only).\n *\n * Paths are resolved relative to the current working directory (where the CLI\n * was launched), consistent with the .env cascade \u2014 not the bulb file's folder.\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * Server-side function proxy. The built-in `log` prints to CLI stdout\n * (falls back to console.log on web). On the server side, this object only\n * exposes `log` \u2014 server-to-server calls are not supported.\n */\n server: {\n log(...args: any[]): Promise<void>;\n };\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
14
+ export declare const serverTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace (server-side).\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */\n reasoning?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `reasoning: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */\n reasoning?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * Local filesystem access (CLI only).\n *\n * Paths are resolved relative to the current working directory (where the CLI\n * was launched), consistent with the .env cascade \u2014 not the bulb file's folder.\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * Server-side function proxy. The built-in `log` prints to CLI stdout\n * (falls back to console.log on web). On the server side, this object only\n * exposes `log` \u2014 server-to-server calls are not supported.\n */\n server: {\n log(...args: any[]): Promise<void>;\n };\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n }>>;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
15
15
  //# sourceMappingURL=tbTypings.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tbTypings.d.ts","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmNH,kEAAkE;AAClE,eAAO,MAAM,eAAe,kwPAO3B,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,eAAe,umHAO3B,CAAA"}
1
+ {"version":3,"file":"tbTypings.d.ts","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoPH,kEAAkE;AAClE,eAAO,MAAM,eAAe,sjTAO3B,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,eAAe,onKAO3B,CAAA"}
@@ -30,15 +30,8 @@ const insight = `
30
30
  * @returns The parsed insight JSON, or undefined if no insight is available
31
31
  */
32
32
  insight<T = unknown>(): T | undefined;`;
33
- const ai = `
34
- /**
35
- * General-purpose AI call.
36
- *
37
- * @param options - Messages, system prompt, optional provider/model override
38
- * @returns Promise resolving to { text: string }
39
- * @throws On rate limit, network error, or provider error
40
- */
41
- ai(options: {
33
+ // One options shape, reused by tb.ai() and tb.ai.stream().
34
+ const aiOptions = `options: {
42
35
  messages: Array<{ role: "user" | "assistant"; content: string }>;
43
36
  system?: string;
44
37
  /** Reasoning depth hint (0=min, 1=low, 2=med, 3=high). Mapped to provider-specific parameters (e.g. Anthropic adaptive thinking, OpenAI reasoning effort). Default: 0. */
@@ -47,7 +40,41 @@ const ai = `
47
40
  model?: string;
48
41
  /** Enable/disable web search. Default: on for BYOK, always off for free model. */
49
42
  webSearch?: boolean;
50
- }): Promise<{ text: string }>;`;
43
+ /** Abort the request. On abort the promise rejects / the stream ends. */
44
+ signal?: AbortSignal;
45
+ }`;
46
+ /** A streamed delta from \`tb.ai.stream()\` — a discriminated union, exactly one kind per chunk. */
47
+ const aiChunkType = `
48
+ /** A single streamed delta from \`tb.ai.stream()\`. Discriminated by \`kind\`. */
49
+ type AiChunk =
50
+ | { kind: "text"; text: string }
51
+ | { kind: "reasoning"; text: string };
52
+ `;
53
+ const ai = `
54
+ /**
55
+ * General-purpose AI call. \`tb.ai(opts)\` resolves with the full text; \`tb.ai.stream(opts)\`
56
+ * returns an async iterable of {@link AiChunk} deltas you consume with \`for await\`.
57
+ *
58
+ * @returns Promise resolving to { text: string }
59
+ * @throws On rate limit, network error, or provider error
60
+ */
61
+ ai: {
62
+ (${aiOptions}): Promise<{ text: string }>;
63
+ /**
64
+ * Streaming counterpart of \`tb.ai()\`. Yields \`{ kind: "text" | "reasoning", text }\` deltas
65
+ * as they arrive; break the loop (or abort \`signal\`) to cancel and stop the upstream.
66
+ *
67
+ * \`kind: "reasoning"\` deltas only arrive when you pass \`reasoning: 1-3\` AND use a
68
+ * thinking-capable model; otherwise the stream is \`text\`-only.
69
+ *
70
+ * @example
71
+ * let answer = "";
72
+ * for await (const c of tb.ai.stream({ messages })) {
73
+ * if (c.kind === "text") answer += c.text;
74
+ * }
75
+ */
76
+ stream(${aiOptions}): AsyncIterable<AiChunk>;
77
+ };`;
51
78
  const models = `
52
79
  /**
53
80
  * Returns AI models available to the current user.
@@ -204,10 +231,14 @@ const clientServerProxy = `
204
231
  * In the CLI, calls exported functions from the \`**server.ts**\` section.
205
232
  * \`tb.server.log(...)\` is a built-in that prints to CLI stdout (falls back to console.log on web).
206
233
  * User exports override built-ins of the same name.
234
+ *
235
+ * A normal export is awaited for its result (\`await tb.server.fn()\`). An \`async function*\`
236
+ * export streams: \`for await (const chunk of tb.server.gen())\`. The call object supports both;
237
+ * break the \`for await\` to cancel and tear down the server generator.
207
238
  */
208
- server: Record<string, (...args: any[]) => Promise<any>>;`;
239
+ server: Record<string, (...args: any[]) => Promise<any> & AsyncIterable<any>>;`;
209
240
  /** Typebulb globals available in browser-side code (code.tsx). */
210
- export const clientTbTypings = `
241
+ export const clientTbTypings = `${aiChunkType}
211
242
  /**
212
243
  * Typebulb utilities namespace.
213
244
  * Type \`tb.\` to discover available helpers.
@@ -216,7 +247,7 @@ declare const tb: {${dataAndJson}${clientOnlyMembers}${insight}${clientServerPro
216
247
  };
217
248
  `;
218
249
  /** Typebulb globals available in Node-side code (server.ts). */
219
- export const serverTbTypings = `
250
+ export const serverTbTypings = `${aiChunkType}
220
251
  /**
221
252
  * Typebulb utilities namespace (server-side).
222
253
  * Type \`tb.\` to discover available helpers.
@@ -1 +1 @@
1
- {"version":3,"file":"tbTypings.js","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;uCAWmB,CAAA;AAEvC,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,CAAA;AAEzC,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;;;iCAiBsB,CAAA;AAEjC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;OAeR,CAAA;AAEP,MAAM,KAAK,GAAG;;;;;;;;;;;;uCAYyB,CAAA;AAEvC,MAAM,IAAI,GAAG;;;;;;;;;;uDAU0C,CAAA;AAEvD,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;KAeN,CAAA;AAEL,MAAM,aAAa,GAAG;;;;;;;;KAQjB,CAAA;AAEL,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAmEA,CAAA;AAE1B,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;0DAewC,CAAA;AAE1D,MAAM,iBAAiB,GAAG;;;;;;;;4DAQkC,CAAA;AAE5D,kEAAkE;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;qBAKV,WAAW,GAAG,iBAAiB,GAAG,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI;;CAE/H,CAAA;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;qBAKV,WAAW,GAAG,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,aAAa,GAAG,MAAM,GAAG,IAAI;;CAEnF,CAAA"}
1
+ {"version":3,"file":"tbTypings.js","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;uCAWmB,CAAA;AAEvC,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,CAAA;AAEzC,2DAA2D;AAC3D,MAAM,SAAS,GAAG;;;;;;;;;;;IAWd,CAAA;AAEJ,oGAAoG;AACpG,MAAM,WAAW,GAAG;;;;;CAKnB,CAAA;AAED,MAAM,EAAE,GAAG;;;;;;;;;OASJ,SAAS;;;;;;;;;;;;;;aAcH,SAAS;KACjB,CAAA;AAEL,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;OAeR,CAAA;AAEP,MAAM,KAAK,GAAG;;;;;;;;;;;;uCAYyB,CAAA;AAEvC,MAAM,IAAI,GAAG;;;;;;;;;;uDAU0C,CAAA;AAEvD,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;KAeN,CAAA;AAEL,MAAM,aAAa,GAAG;;;;;;;;KAQjB,CAAA;AAEL,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAmEA,CAAA;AAE1B,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;0DAewC,CAAA;AAE1D,MAAM,iBAAiB,GAAG;;;;;;;;;;;;iFAYuD,CAAA;AAEjF,kEAAkE;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,WAAW;;;;;qBAKxB,WAAW,GAAG,iBAAiB,GAAG,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI;;CAE/H,CAAA;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,WAAW;;;;;qBAKxB,WAAW,GAAG,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,aAAa,GAAG,MAAM,GAAG,IAAI;;CAEnF,CAAA"}