@fgv/ts-extras 5.1.0-16 → 5.1.0-18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/index.browser.js +2 -1
  2. package/dist/packlets/ai-assist/apiClient.js +570 -58
  3. package/dist/packlets/ai-assist/chatRequestBuilders.js +180 -0
  4. package/dist/packlets/ai-assist/index.js +4 -3
  5. package/dist/packlets/ai-assist/model.js +20 -3
  6. package/dist/packlets/ai-assist/registry.js +66 -10
  7. package/dist/packlets/ai-assist/sseParser.js +122 -0
  8. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +192 -0
  9. package/dist/packlets/ai-assist/streamingAdapters/common.js +77 -0
  10. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +160 -0
  11. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +149 -0
  12. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +163 -0
  13. package/dist/packlets/ai-assist/streamingAdapters/proxy.js +157 -0
  14. package/dist/packlets/ai-assist/streamingClient.js +88 -0
  15. package/dist/packlets/conversion/converters.js +1 -1
  16. package/dist/packlets/crypto-utils/index.browser.js +2 -0
  17. package/dist/packlets/crypto-utils/index.js +2 -0
  18. package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +47 -0
  19. package/dist/packlets/crypto-utils/keystore/converters.js +101 -9
  20. package/dist/packlets/crypto-utils/keystore/index.js +1 -0
  21. package/dist/packlets/crypto-utils/keystore/keyStore.js +271 -46
  22. package/dist/packlets/crypto-utils/keystore/model.js +22 -1
  23. package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
  24. package/dist/packlets/crypto-utils/model.js +5 -0
  25. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +44 -1
  26. package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
  27. package/dist/test/unit/crypto/keystore/inMemoryPrivateKeyStorage.js +78 -0
  28. package/dist/ts-extras.d.ts +1089 -37
  29. package/lib/index.browser.d.ts +2 -1
  30. package/lib/index.browser.js +3 -1
  31. package/lib/packlets/ai-assist/apiClient.d.ts +103 -1
  32. package/lib/packlets/ai-assist/apiClient.js +574 -58
  33. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +89 -0
  34. package/lib/packlets/ai-assist/chatRequestBuilders.js +189 -0
  35. package/lib/packlets/ai-assist/index.d.ts +4 -3
  36. package/lib/packlets/ai-assist/index.js +10 -1
  37. package/lib/packlets/ai-assist/model.d.ts +271 -2
  38. package/lib/packlets/ai-assist/model.js +21 -3
  39. package/lib/packlets/ai-assist/registry.d.ts +10 -1
  40. package/lib/packlets/ai-assist/registry.js +67 -11
  41. package/lib/packlets/ai-assist/sseParser.d.ts +45 -0
  42. package/lib/packlets/ai-assist/sseParser.js +127 -0
  43. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +18 -0
  44. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +195 -0
  45. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +71 -0
  46. package/lib/packlets/ai-assist/streamingAdapters/common.js +81 -0
  47. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +19 -0
  48. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +163 -0
  49. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +18 -0
  50. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +152 -0
  51. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +19 -0
  52. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +166 -0
  53. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +34 -0
  54. package/lib/packlets/ai-assist/streamingAdapters/proxy.js +160 -0
  55. package/lib/packlets/ai-assist/streamingClient.d.ts +33 -0
  56. package/lib/packlets/ai-assist/streamingClient.js +93 -0
  57. package/lib/packlets/conversion/converters.d.ts +1 -1
  58. package/lib/packlets/conversion/converters.js +1 -1
  59. package/lib/packlets/crypto-utils/index.browser.d.ts +1 -0
  60. package/lib/packlets/crypto-utils/index.browser.js +4 -1
  61. package/lib/packlets/crypto-utils/index.d.ts +1 -0
  62. package/lib/packlets/crypto-utils/index.js +4 -1
  63. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +39 -0
  64. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +50 -0
  65. package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
  66. package/lib/packlets/crypto-utils/keystore/converters.js +100 -8
  67. package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
  68. package/lib/packlets/crypto-utils/keystore/index.js +1 -0
  69. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +77 -9
  70. package/lib/packlets/crypto-utils/keystore/keyStore.js +271 -46
  71. package/lib/packlets/crypto-utils/keystore/model.d.ts +238 -19
  72. package/lib/packlets/crypto-utils/keystore/model.js +24 -2
  73. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +50 -0
  74. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
  75. package/lib/packlets/crypto-utils/model.d.ts +38 -0
  76. package/lib/packlets/crypto-utils/model.js +6 -1
  77. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +26 -1
  78. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +43 -0
  79. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +2 -2
  80. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
  81. package/package.json +7 -7
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Erik Fortune
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.openSseConnection = openSseConnection;
23
+ exports.validateEventPayload = validateEventPayload;
24
+ /**
25
+ * Shared infrastructure for the per-format streaming adapters: HTTP connection
26
+ * helper, common config and request-params types, and a typed-validation
27
+ * helper for SSE event payloads.
28
+ *
29
+ * @packageDocumentation
30
+ */
31
+ const ts_utils_1 = require("@fgv/ts-utils");
32
+ /**
33
+ * Opens an SSE-style POST connection. Returns the underlying Response on a
34
+ * 2xx; failures (network, non-2xx, missing body) surface as Result.fail
35
+ * carrying the body text.
36
+ *
37
+ * @internal
38
+ */
39
+ async function openSseConnection(url, headers, body, logger, signal) {
40
+ /* c8 ignore next 1 - optional logger */
41
+ logger === null || logger === void 0 ? void 0 : logger.detail(`AI streaming request: POST ${url}`);
42
+ let response;
43
+ try {
44
+ response = await fetch(url, {
45
+ method: 'POST',
46
+ headers: Object.assign({ 'Content-Type': 'application/json', Accept: 'text/event-stream' }, headers),
47
+ body: JSON.stringify(body),
48
+ signal
49
+ });
50
+ }
51
+ catch (err) {
52
+ const detail = err instanceof Error ? err.message : String(err);
53
+ /* c8 ignore next 1 - optional logger */
54
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming request failed: ${detail}`);
55
+ return (0, ts_utils_1.fail)(`AI streaming request failed: ${detail}`);
56
+ }
57
+ if (!response.ok) {
58
+ const errorText = await response.text().catch(() => 'unknown error');
59
+ /* c8 ignore next 1 - optional logger */
60
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI streaming API returned ${response.status}: ${errorText}`);
61
+ return (0, ts_utils_1.fail)(`AI streaming API returned ${response.status}: ${errorText}`);
62
+ }
63
+ if (!response.body) {
64
+ return (0, ts_utils_1.fail)('AI streaming API returned an empty body');
65
+ }
66
+ return (0, ts_utils_1.succeed)(response);
67
+ }
68
+ /**
69
+ * Validates a parsed SSE event payload against a typed shape, returning the
70
+ * validated value or `undefined` if validation fails. Adapters use the
71
+ * `undefined` return to skip unrecognized event shapes — the same lenient
72
+ * behavior the inline casts had, but with a real type-checked path on the
73
+ * happy case.
74
+ *
75
+ * @internal
76
+ */
77
+ function validateEventPayload(json, validator) {
78
+ const result = validator.validate(json);
79
+ return result.isSuccess() ? result.value : undefined;
80
+ }
81
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Streaming adapter for Gemini's `streamGenerateContent` endpoint. Gemini
3
+ * emits no explicit tool-progress events even when `google_search` is
4
+ * enabled — grounding metadata arrives attached to text chunks — so this
5
+ * adapter never yields `tool-event`s.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { type Logging, Result } from '@fgv/ts-utils';
10
+ import { AiPrompt, type AiServerToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';
11
+ import { IStreamApiConfig } from './common';
12
+ /**
13
+ * Issues a streaming Gemini request and returns the unified-event iterable
14
+ * on success.
15
+ *
16
+ * @internal
17
+ */
18
+ export declare function callGeminiStream(config: IStreamApiConfig, prompt: AiPrompt, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number, tools: ReadonlyArray<AiServerToolConfig> | undefined, logger?: Logging.ILogger, signal?: AbortSignal): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
19
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Erik Fortune
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
22
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
23
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
24
+ var m = o[Symbol.asyncIterator], i;
25
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
26
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
27
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
+ };
29
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
30
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
31
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
32
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
33
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
34
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
35
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
36
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
37
+ function fulfill(value) { resume("next", value); }
38
+ function reject(value) { resume("throw", value); }
39
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.callGeminiStream = callGeminiStream;
43
+ /**
44
+ * Streaming adapter for Gemini's `streamGenerateContent` endpoint. Gemini
45
+ * emits no explicit tool-progress events even when `google_search` is
46
+ * enabled — grounding metadata arrives attached to text chunks — so this
47
+ * adapter never yields `tool-event`s.
48
+ *
49
+ * @packageDocumentation
50
+ */
51
+ const ts_utils_1 = require("@fgv/ts-utils");
52
+ const chatRequestBuilders_1 = require("../chatRequestBuilders");
53
+ const sseParser_1 = require("../sseParser");
54
+ const toolFormats_1 = require("../toolFormats");
55
+ const common_1 = require("./common");
56
+ const geminiStreamPart = ts_utils_1.Validators.object({ text: ts_utils_1.Validators.string.optional() }, { options: { optionalFields: ['text'] } });
57
+ const geminiStreamContent = ts_utils_1.Validators.object({ parts: ts_utils_1.Validators.arrayOf(geminiStreamPart).optional() }, { options: { optionalFields: ['parts'] } });
58
+ const geminiStreamCandidate = ts_utils_1.Validators.object({
59
+ content: geminiStreamContent.optional(),
60
+ finishReason: ts_utils_1.Validators.string.optional()
61
+ }, { options: { optionalFields: ['content', 'finishReason'] } });
62
+ const geminiStreamChunk = ts_utils_1.Validators.object({
63
+ candidates: ts_utils_1.Validators.arrayOf(geminiStreamCandidate)
64
+ });
65
+ // ============================================================================
66
+ // Stream translator
67
+ // ============================================================================
68
+ /**
69
+ * Translates a Gemini streamGenerateContent SSE stream into unified events.
70
+ *
71
+ * @internal
72
+ */
73
+ function translateGeminiStream(response) {
74
+ return __asyncGenerator(this, arguments, function* translateGeminiStream_1() {
75
+ var _a, e_1, _b, _c;
76
+ var _d;
77
+ let fullText = '';
78
+ let truncated = false;
79
+ let receivedFinishReason = false;
80
+ try {
81
+ /* c8 ignore next - body is non-null at this point per openSseConnection */
82
+ if (!response.body)
83
+ return yield __await(void 0);
84
+ try {
85
+ for (var _e = true, _f = __asyncValues((0, sseParser_1.readSseEvents)(response.body)), _g; _g = yield __await(_f.next()), _a = _g.done, !_a; _e = true) {
86
+ _c = _g.value;
87
+ _e = false;
88
+ const message = _c;
89
+ const json = (0, sseParser_1.parseSseEventJson)(message.data);
90
+ if (json === undefined) {
91
+ continue;
92
+ }
93
+ const chunk = (0, common_1.validateEventPayload)(json, geminiStreamChunk);
94
+ const candidate = chunk === null || chunk === void 0 ? void 0 : chunk.candidates[0];
95
+ if (!candidate) {
96
+ continue;
97
+ }
98
+ const parts = (_d = candidate.content) === null || _d === void 0 ? void 0 : _d.parts;
99
+ if (parts) {
100
+ for (const part of parts) {
101
+ if (typeof part.text === 'string' && part.text.length > 0) {
102
+ fullText += part.text;
103
+ yield yield __await({ type: 'text-delta', delta: part.text });
104
+ }
105
+ }
106
+ }
107
+ const finishReason = candidate.finishReason;
108
+ if (typeof finishReason === 'string' && finishReason.length > 0) {
109
+ truncated = finishReason === 'MAX_TOKENS';
110
+ receivedFinishReason = true;
111
+ }
112
+ }
113
+ }
114
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
115
+ finally {
116
+ try {
117
+ if (!_e && !_a && (_b = _f.return)) yield __await(_b.call(_f));
118
+ }
119
+ finally { if (e_1) throw e_1.error; }
120
+ }
121
+ }
122
+ catch (err) {
123
+ yield yield __await({ type: 'error', message: err instanceof Error ? err.message : String(err) });
124
+ return yield __await(void 0);
125
+ }
126
+ if (receivedFinishReason) {
127
+ yield yield __await({ type: 'done', truncated, fullText });
128
+ }
129
+ else {
130
+ yield yield __await({ type: 'error', message: 'Gemini stream ended without a finishReason' });
131
+ }
132
+ });
133
+ }
134
+ // ============================================================================
135
+ // Per-format request caller
136
+ // ============================================================================
137
+ /**
138
+ * Issues a streaming Gemini request and returns the unified-event iterable
139
+ * on success.
140
+ *
141
+ * @internal
142
+ */
143
+ async function callGeminiStream(config, prompt, messagesBefore, temperature, tools, logger, signal) {
144
+ const url = `${config.baseUrl}/models/${config.model}:streamGenerateContent?alt=sse`;
145
+ const contents = (0, chatRequestBuilders_1.buildGeminiContents)(prompt, { head: messagesBefore });
146
+ const body = {
147
+ systemInstruction: { parts: [{ text: prompt.system }] },
148
+ contents,
149
+ generationConfig: { temperature }
150
+ };
151
+ if (tools && tools.length > 0) {
152
+ body.tools = (0, toolFormats_1.toGeminiTools)(tools);
153
+ }
154
+ const headers = { 'x-goog-api-key': config.apiKey };
155
+ /* c8 ignore next 3 - optional logger diagnostic output */
156
+ if (logger) {
157
+ const toolTypes = tools && tools.length > 0 ? tools.map((t) => t.type).join(',') : 'none';
158
+ logger.info(`Gemini streaming: model=${config.model}, tools=${toolTypes}`);
159
+ }
160
+ const conn = await (0, common_1.openSseConnection)(url, headers, body, logger, signal);
161
+ return conn.onSuccess((response) => (0, ts_utils_1.succeed)(translateGeminiStream(response)));
162
+ }
163
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Streaming adapter for OpenAI Chat Completions (also used for Groq, Mistral,
3
+ * and other Chat-Completions-compatible providers when no tools are
4
+ * requested).
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ import { type Logging, Result } from '@fgv/ts-utils';
9
+ import { AiPrompt, type IAiStreamEvent, type IChatMessage } from '../model';
10
+ import { IStreamApiConfig } from './common';
11
+ /**
12
+ * Issues a streaming Chat Completions request and returns the unified-event
13
+ * iterable on success.
14
+ *
15
+ * @internal
16
+ */
17
+ export declare function callOpenAiChatStream(config: IStreamApiConfig, prompt: AiPrompt, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number, logger?: Logging.ILogger, signal?: AbortSignal): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
18
+ //# sourceMappingURL=openaiChat.d.ts.map
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Erik Fortune
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
22
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
23
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
24
+ var m = o[Symbol.asyncIterator], i;
25
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
26
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
27
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
+ };
29
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
30
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
31
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
32
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
33
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
34
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
35
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
36
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
37
+ function fulfill(value) { resume("next", value); }
38
+ function reject(value) { resume("throw", value); }
39
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.callOpenAiChatStream = callOpenAiChatStream;
43
+ /**
44
+ * Streaming adapter for OpenAI Chat Completions (also used for Groq, Mistral,
45
+ * and other Chat-Completions-compatible providers when no tools are
46
+ * requested).
47
+ *
48
+ * @packageDocumentation
49
+ */
50
+ const ts_utils_1 = require("@fgv/ts-utils");
51
+ const chatRequestBuilders_1 = require("../chatRequestBuilders");
52
+ const sseParser_1 = require("../sseParser");
53
+ const common_1 = require("./common");
54
+ // eslint-disable-next-line @rushstack/no-new-null
55
+ const stringOrNull = ts_utils_1.Validators.isA('string-or-null',
56
+ // eslint-disable-next-line @rushstack/no-new-null
57
+ (v) => typeof v === 'string' || v === null);
58
+ const openAiChatStreamChoice = ts_utils_1.Validators.object({
59
+ delta: ts_utils_1.Validators.object({ content: ts_utils_1.Validators.string }, { options: { optionalFields: ['content'] } }).optional(),
60
+ finish_reason: stringOrNull.optional()
61
+ }, { options: { optionalFields: ['delta', 'finish_reason'] } });
62
+ const openAiChatStreamChunk = ts_utils_1.Validators.object({
63
+ choices: ts_utils_1.Validators.arrayOf(openAiChatStreamChoice)
64
+ });
65
+ // ============================================================================
66
+ // Stream translator
67
+ // ============================================================================
68
+ /**
69
+ * Translates an OpenAI Chat Completions SSE stream into unified events.
70
+ *
71
+ * @internal
72
+ */
73
+ function translateOpenAiChatStream(response) {
74
+ return __asyncGenerator(this, arguments, function* translateOpenAiChatStream_1() {
75
+ var _a, e_1, _b, _c;
76
+ var _d;
77
+ let fullText = '';
78
+ let truncated = false;
79
+ let receivedDone = false;
80
+ try {
81
+ /* c8 ignore next - body is non-null at this point per openSseConnection */
82
+ if (!response.body)
83
+ return yield __await(void 0);
84
+ try {
85
+ for (var _e = true, _f = __asyncValues((0, sseParser_1.readSseEvents)(response.body)), _g; _g = yield __await(_f.next()), _a = _g.done, !_a; _e = true) {
86
+ _c = _g.value;
87
+ _e = false;
88
+ const message = _c;
89
+ const json = (0, sseParser_1.parseSseEventJson)(message.data);
90
+ if (json === undefined) {
91
+ // [DONE] sentinel or unparseable; skip
92
+ continue;
93
+ }
94
+ const chunk = (0, common_1.validateEventPayload)(json, openAiChatStreamChunk);
95
+ const choice = chunk === null || chunk === void 0 ? void 0 : chunk.choices[0];
96
+ if (!choice) {
97
+ continue;
98
+ }
99
+ const delta = (_d = choice.delta) === null || _d === void 0 ? void 0 : _d.content;
100
+ if (typeof delta === 'string' && delta.length > 0) {
101
+ fullText += delta;
102
+ yield yield __await({ type: 'text-delta', delta });
103
+ }
104
+ const finish = choice.finish_reason;
105
+ if (typeof finish === 'string' && finish.length > 0) {
106
+ truncated = finish === 'length';
107
+ receivedDone = true;
108
+ }
109
+ }
110
+ }
111
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
112
+ finally {
113
+ try {
114
+ if (!_e && !_a && (_b = _f.return)) yield __await(_b.call(_f));
115
+ }
116
+ finally { if (e_1) throw e_1.error; }
117
+ }
118
+ }
119
+ catch (err) {
120
+ yield yield __await({ type: 'error', message: err instanceof Error ? err.message : String(err) });
121
+ return yield __await(void 0);
122
+ }
123
+ if (receivedDone) {
124
+ yield yield __await({ type: 'done', truncated, fullText });
125
+ }
126
+ else {
127
+ yield yield __await({ type: 'error', message: 'OpenAI stream ended without a finish_reason' });
128
+ }
129
+ });
130
+ }
131
+ // ============================================================================
132
+ // Per-format request caller
133
+ // ============================================================================
134
+ /**
135
+ * Issues a streaming Chat Completions request and returns the unified-event
136
+ * iterable on success.
137
+ *
138
+ * @internal
139
+ */
140
+ async function callOpenAiChatStream(config, prompt, messagesBefore, temperature, logger, signal) {
141
+ const url = `${config.baseUrl}/chat/completions`;
142
+ const messages = (0, chatRequestBuilders_1.buildMessages)(prompt.system, (0, chatRequestBuilders_1.buildOpenAiChatUserContent)(prompt), {
143
+ head: messagesBefore
144
+ });
145
+ const body = { model: config.model, messages, temperature, stream: true };
146
+ const headers = { Authorization: `Bearer ${config.apiKey}` };
147
+ /* c8 ignore next 1 - optional logger */
148
+ logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI streaming completion: model=${config.model}`);
149
+ const conn = await (0, common_1.openSseConnection)(url, headers, body, logger, signal);
150
+ return conn.onSuccess((response) => (0, ts_utils_1.succeed)(translateOpenAiChatStream(response)));
151
+ }
152
+ //# sourceMappingURL=openaiChat.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Streaming adapter for the OpenAI / xAI Responses API. This is the format
3
+ * used when server-side tools (e.g. web_search) are requested — Chat
4
+ * Completions doesn't support tool progress events, but the Responses API
5
+ * does.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { type Logging, Result } from '@fgv/ts-utils';
10
+ import { AiPrompt, type AiServerToolConfig, type IAiStreamEvent, type IChatMessage } from '../model';
11
+ import { IStreamApiConfig } from './common';
12
+ /**
13
+ * Issues a streaming Responses API request (with tools) and returns the
14
+ * unified-event iterable on success.
15
+ *
16
+ * @internal
17
+ */
18
+ export declare function callOpenAiResponsesStream(config: IStreamApiConfig, prompt: AiPrompt, tools: ReadonlyArray<AiServerToolConfig>, messagesBefore: ReadonlyArray<IChatMessage> | undefined, temperature: number, logger?: Logging.ILogger, signal?: AbortSignal): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
19
+ //# sourceMappingURL=openaiResponses.d.ts.map
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Erik Fortune
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
22
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
23
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
24
+ var m = o[Symbol.asyncIterator], i;
25
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
26
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
27
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
+ };
29
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
30
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
31
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
32
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
33
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
34
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
35
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
36
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
37
+ function fulfill(value) { resume("next", value); }
38
+ function reject(value) { resume("throw", value); }
39
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.callOpenAiResponsesStream = callOpenAiResponsesStream;
43
+ /**
44
+ * Streaming adapter for the OpenAI / xAI Responses API. This is the format
45
+ * used when server-side tools (e.g. web_search) are requested — Chat
46
+ * Completions doesn't support tool progress events, but the Responses API
47
+ * does.
48
+ *
49
+ * @packageDocumentation
50
+ */
51
+ const ts_utils_1 = require("@fgv/ts-utils");
52
+ const chatRequestBuilders_1 = require("../chatRequestBuilders");
53
+ const sseParser_1 = require("../sseParser");
54
+ const toolFormats_1 = require("../toolFormats");
55
+ const common_1 = require("./common");
56
+ const responsesDeltaPayload = ts_utils_1.Validators.object({
57
+ delta: ts_utils_1.Validators.string
58
+ });
59
+ const responsesCompletedPayload = ts_utils_1.Validators.object({
60
+ response: ts_utils_1.Validators.object({ status: ts_utils_1.Validators.string.optional() }, { options: { optionalFields: ['status'] } })
61
+ });
62
+ const responsesErrorInner = ts_utils_1.Validators.object({ message: ts_utils_1.Validators.string.optional() }, { options: { optionalFields: ['message'] } });
63
+ const responsesErrorPayload = ts_utils_1.Validators.object({
64
+ error: responsesErrorInner.optional(),
65
+ message: ts_utils_1.Validators.string.optional()
66
+ }, { options: { optionalFields: ['error', 'message'] } });
67
+ // ============================================================================
68
+ // Stream translator
69
+ // ============================================================================
70
+ /**
71
+ * Translates an OpenAI Responses API SSE stream into unified events.
72
+ *
73
+ * @internal
74
+ */
75
+ function translateOpenAiResponsesStream(response) {
76
+ return __asyncGenerator(this, arguments, function* translateOpenAiResponsesStream_1() {
77
+ var _a, e_1, _b, _c;
78
+ var _d, _e, _f;
79
+ let fullText = '';
80
+ let truncated = false;
81
+ let completed = false;
82
+ try {
83
+ /* c8 ignore next - body is non-null at this point per openSseConnection */
84
+ if (!response.body)
85
+ return yield __await(void 0);
86
+ try {
87
+ for (var _g = true, _h = __asyncValues((0, sseParser_1.readSseEvents)(response.body)), _j; _j = yield __await(_h.next()), _a = _j.done, !_a; _g = true) {
88
+ _c = _j.value;
89
+ _g = false;
90
+ const message = _c;
91
+ const eventName = message.event;
92
+ if (eventName === 'response.output_text.delta') {
93
+ const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesDeltaPayload);
94
+ const delta = payload === null || payload === void 0 ? void 0 : payload.delta;
95
+ if (typeof delta === 'string' && delta.length > 0) {
96
+ fullText += delta;
97
+ yield yield __await({ type: 'text-delta', delta });
98
+ }
99
+ }
100
+ else if (eventName === 'response.web_search_call.in_progress') {
101
+ yield yield __await({ type: 'tool-event', toolType: 'web_search', phase: 'started' });
102
+ }
103
+ else if (eventName === 'response.web_search_call.completed') {
104
+ yield yield __await({ type: 'tool-event', toolType: 'web_search', phase: 'completed' });
105
+ }
106
+ else if (eventName === 'response.completed') {
107
+ const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesCompletedPayload);
108
+ truncated = (payload === null || payload === void 0 ? void 0 : payload.response.status) === 'incomplete';
109
+ completed = true;
110
+ }
111
+ else if (eventName === 'response.failed' || eventName === 'error') {
112
+ const payload = (0, common_1.validateEventPayload)((0, sseParser_1.parseSseEventJson)(message.data), responsesErrorPayload);
113
+ const errMsg = (_f = (_e = (_d = payload === null || payload === void 0 ? void 0 : payload.error) === null || _d === void 0 ? void 0 : _d.message) !== null && _e !== void 0 ? _e : payload === null || payload === void 0 ? void 0 : payload.message) !== null && _f !== void 0 ? _f : 'Responses API stream failed';
114
+ yield yield __await({ type: 'error', message: errMsg });
115
+ return yield __await(void 0);
116
+ }
117
+ }
118
+ }
119
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
120
+ finally {
121
+ try {
122
+ if (!_g && !_a && (_b = _h.return)) yield __await(_b.call(_h));
123
+ }
124
+ finally { if (e_1) throw e_1.error; }
125
+ }
126
+ }
127
+ catch (err) {
128
+ yield yield __await({ type: 'error', message: err instanceof Error ? err.message : String(err) });
129
+ return yield __await(void 0);
130
+ }
131
+ if (completed) {
132
+ yield yield __await({ type: 'done', truncated, fullText });
133
+ }
134
+ else {
135
+ yield yield __await({ type: 'error', message: 'Responses API stream ended without a completed event' });
136
+ }
137
+ });
138
+ }
139
+ // ============================================================================
140
+ // Per-format request caller
141
+ // ============================================================================
142
+ /**
143
+ * Issues a streaming Responses API request (with tools) and returns the
144
+ * unified-event iterable on success.
145
+ *
146
+ * @internal
147
+ */
148
+ async function callOpenAiResponsesStream(config, prompt, tools, messagesBefore, temperature, logger, signal) {
149
+ const url = `${config.baseUrl}/responses`;
150
+ const input = (0, chatRequestBuilders_1.buildMessages)(prompt.system, (0, chatRequestBuilders_1.buildOpenAiResponsesUserContent)(prompt), {
151
+ head: messagesBefore
152
+ });
153
+ const body = {
154
+ model: config.model,
155
+ input,
156
+ tools: (0, toolFormats_1.toResponsesApiTools)(tools),
157
+ temperature,
158
+ stream: true
159
+ };
160
+ const headers = { Authorization: `Bearer ${config.apiKey}` };
161
+ /* c8 ignore next 1 - optional logger */
162
+ logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI Responses streaming: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`);
163
+ const conn = await (0, common_1.openSseConnection)(url, headers, body, logger, signal);
164
+ return conn.onSuccess((response) => (0, ts_utils_1.succeed)(translateOpenAiResponsesStream(response)));
165
+ }
166
+ //# sourceMappingURL=openaiResponses.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Streaming adapter for a caller-provided proxy server. Unlike the
3
+ * provider-specific adapters, the proxy speaks our own unified vocabulary
4
+ * directly: each `data:` line is a JSON-serialized {@link AiAssist.IAiStreamEvent},
5
+ * so this adapter only validates the event-type discriminator and forwards.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ import { Result } from '@fgv/ts-utils';
10
+ import { type IAiStreamEvent } from '../model';
11
+ import { IProviderCompletionStreamParams } from './common';
12
+ /**
13
+ * Calls the streaming chat endpoint on a proxy server instead of calling
14
+ * the provider directly from the browser.
15
+ *
16
+ * @remarks
17
+ * Proxy contract:
18
+ * - Endpoint: `POST ${proxyUrl}/api/ai/completion-stream`
19
+ * - Request body: same JSON as `/api/ai/completion` plus `"stream": true`
20
+ * - Response: `Content-Type: text/event-stream`; body is the unified
21
+ * {@link AiAssist.IAiStreamEvent} JSON-serialized one event per SSE `data:` line
22
+ * (no `event:` line needed since the type discriminator is in the JSON).
23
+ * - Error response (when the proxy can't even start): JSON `{error: string}`
24
+ * with a non-2xx status, surfaced as `proxy: ${error}`.
25
+ *
26
+ * The proxy server is responsible for opening the upstream SSE connection,
27
+ * translating provider-native events to the unified vocabulary, and
28
+ * forwarding events as they arrive (no buffering). The library does not
29
+ * ship a proxy implementation.
30
+ *
31
+ * @public
32
+ */
33
+ export declare function callProxiedCompletionStream(proxyUrl: string, params: IProviderCompletionStreamParams): Promise<Result<AsyncIterable<IAiStreamEvent>>>;
34
+ //# sourceMappingURL=proxy.d.ts.map