@aragon-agent/core 0.1.3

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 (74) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/LICENSE +21 -0
  3. package/README.md +147 -0
  4. package/dist/engine/agent-loop.d.ts +52 -0
  5. package/dist/engine/agent-loop.js +260 -0
  6. package/dist/engine/agent.d.ts +129 -0
  7. package/dist/engine/agent.js +283 -0
  8. package/dist/engine/message-manager.d.ts +26 -0
  9. package/dist/engine/message-manager.js +39 -0
  10. package/dist/engine/steering.d.ts +27 -0
  11. package/dist/engine/steering.js +51 -0
  12. package/dist/engine/watchdog.d.ts +24 -0
  13. package/dist/engine/watchdog.js +50 -0
  14. package/dist/index.d.ts +46 -0
  15. package/dist/index.js +63 -0
  16. package/dist/llm/model-registry.d.ts +51 -0
  17. package/dist/llm/model-registry.js +277 -0
  18. package/dist/llm/provider.d.ts +72 -0
  19. package/dist/llm/provider.js +77 -0
  20. package/dist/llm/providers/anthropic.d.ts +23 -0
  21. package/dist/llm/providers/anthropic.js +420 -0
  22. package/dist/llm/providers/google.d.ts +22 -0
  23. package/dist/llm/providers/google.js +336 -0
  24. package/dist/llm/providers/index.d.ts +67 -0
  25. package/dist/llm/providers/index.js +127 -0
  26. package/dist/llm/providers/openai.d.ts +22 -0
  27. package/dist/llm/providers/openai.js +353 -0
  28. package/dist/llm/stream-utils.d.ts +33 -0
  29. package/dist/llm/stream-utils.js +132 -0
  30. package/dist/llm/types.d.ts +129 -0
  31. package/dist/llm/types.js +17 -0
  32. package/dist/sandbox/console-capture.d.ts +29 -0
  33. package/dist/sandbox/console-capture.js +56 -0
  34. package/dist/sandbox/http-bridge.d.ts +47 -0
  35. package/dist/sandbox/http-bridge.js +143 -0
  36. package/dist/sandbox/isolated-vm-sandbox.d.ts +50 -0
  37. package/dist/sandbox/isolated-vm-sandbox.js +226 -0
  38. package/dist/sandbox/tool-bridge.d.ts +42 -0
  39. package/dist/sandbox/tool-bridge.js +98 -0
  40. package/dist/sandbox/types.d.ts +84 -0
  41. package/dist/sandbox/types.js +8 -0
  42. package/dist/skills/constants.d.ts +122 -0
  43. package/dist/skills/constants.js +179 -0
  44. package/dist/skills/disclosure.d.ts +146 -0
  45. package/dist/skills/disclosure.js +788 -0
  46. package/dist/skills/frontmatter.d.ts +27 -0
  47. package/dist/skills/frontmatter.js +177 -0
  48. package/dist/skills/index.d.ts +21 -0
  49. package/dist/skills/index.js +18 -0
  50. package/dist/skills/skill-find-tool.d.ts +41 -0
  51. package/dist/skills/skill-find-tool.js +117 -0
  52. package/dist/skills/skill-registry.d.ts +96 -0
  53. package/dist/skills/skill-registry.js +208 -0
  54. package/dist/skills/skill-tool.d.ts +44 -0
  55. package/dist/skills/skill-tool.js +96 -0
  56. package/dist/skills/tool-policy.d.ts +91 -0
  57. package/dist/skills/tool-policy.js +272 -0
  58. package/dist/skills/types.d.ts +271 -0
  59. package/dist/skills/types.js +8 -0
  60. package/dist/skills/validate.d.ts +37 -0
  61. package/dist/skills/validate.js +264 -0
  62. package/dist/tools/executor.d.ts +54 -0
  63. package/dist/tools/executor.js +206 -0
  64. package/dist/tools/helpers.d.ts +69 -0
  65. package/dist/tools/helpers.js +87 -0
  66. package/dist/tools/registry.d.ts +58 -0
  67. package/dist/tools/registry.js +119 -0
  68. package/dist/tools/types.d.ts +140 -0
  69. package/dist/tools/types.js +7 -0
  70. package/dist/tools/validator.d.ts +46 -0
  71. package/dist/tools/validator.js +210 -0
  72. package/dist/types.d.ts +56 -0
  73. package/dist/types.js +7 -0
  74. package/package.json +115 -0
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Google Gemini (Generative AI) adapter.
3
+ *
4
+ * Uses `POST /v1beta/models/{model}:streamGenerateContent?alt=sse` for
5
+ * streaming and maps the chunked response into unified StreamEvents.
6
+ *
7
+ * Key differences from Anthropic/OpenAI:
8
+ * - Tool calls (functionCall) arrive as complete objects in a single chunk,
9
+ * not incrementally — so we emit tool_call_start + tool_call_end together.
10
+ * - System instructions use a dedicated `system_instruction` field.
11
+ * - The API key is passed as a query parameter, not a header.
12
+ */
13
+ import { randomUUID } from 'node:crypto';
14
+ import { LLMError, classifyHttpError, wrapFetchError } from '../provider.js';
15
+ import { parseSSEStream } from '../stream-utils.js';
16
+ import { consumeStream } from '../stream-utils.js';
17
+ // ---------------------------------------------------------------------------
18
+ // Constants
19
+ // ---------------------------------------------------------------------------
20
+ const DEFAULT_BASE_URL = 'https://generativelanguage.googleapis.com';
21
+ // ---------------------------------------------------------------------------
22
+ // Google provider
23
+ // ---------------------------------------------------------------------------
24
+ export class GoogleProvider {
25
+ id = 'google';
26
+ displayName = 'Google';
27
+ defaultBaseUrl = DEFAULT_BASE_URL;
28
+ // -----------------------------------------------------------------------
29
+ // stream
30
+ // -----------------------------------------------------------------------
31
+ async *stream(request) {
32
+ const baseUrl = (request.baseUrl || this.defaultBaseUrl).replace(/\/+$/, '');
33
+ const url = `${baseUrl}/v1beta/models/${request.model}:streamGenerateContent?alt=sse&key=${encodeURIComponent(request.apiKey)}`;
34
+ const body = buildRequestBody(request);
35
+ let response;
36
+ try {
37
+ response = await fetch(url, {
38
+ method: 'POST',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ body: JSON.stringify(body),
41
+ signal: request.signal,
42
+ });
43
+ }
44
+ catch (err) {
45
+ yield { type: 'error', error: wrapFetchError(err, this.id) };
46
+ return;
47
+ }
48
+ if (!response.ok) {
49
+ const text = await response.text().catch(() => '');
50
+ yield { type: 'error', error: classifyHttpError(response.status, text, this.id) };
51
+ return;
52
+ }
53
+ if (!response.body) {
54
+ yield { type: 'error', error: new LLMError('Response body is null', this.id, 'network_error', true) };
55
+ return;
56
+ }
57
+ // -- Parse SSE events --
58
+ const usage = { inputTokens: 0, outputTokens: 0 };
59
+ const contentBlocks = [];
60
+ let textAccumulator = '';
61
+ let stopReason;
62
+ try {
63
+ for await (const sse of parseSSEStream(response.body, request.signal)) {
64
+ const raw = sse.data.trim();
65
+ if (!raw)
66
+ continue;
67
+ let data;
68
+ try {
69
+ data = JSON.parse(raw);
70
+ }
71
+ catch {
72
+ continue;
73
+ }
74
+ // Usage metadata
75
+ const usageMeta = data.usageMetadata;
76
+ if (usageMeta) {
77
+ usage.inputTokens = usageMeta.promptTokenCount ?? usage.inputTokens;
78
+ usage.outputTokens = usageMeta.candidatesTokenCount ?? usage.outputTokens;
79
+ }
80
+ // Candidates
81
+ const candidates = data.candidates;
82
+ if (!candidates || candidates.length === 0)
83
+ continue;
84
+ const candidate = candidates[0];
85
+ // Finish reason
86
+ const finishReason = candidate.finishReason;
87
+ if (finishReason) {
88
+ stopReason = mapFinishReason(finishReason);
89
+ }
90
+ const content = candidate.content;
91
+ if (!content)
92
+ continue;
93
+ const parts = content.parts;
94
+ if (!parts)
95
+ continue;
96
+ for (const part of parts) {
97
+ // Text part
98
+ if (typeof part.text === 'string') {
99
+ textAccumulator += part.text;
100
+ yield { type: 'text_delta', delta: part.text };
101
+ }
102
+ // Function call — arrives complete in a single chunk
103
+ const functionCall = part.functionCall;
104
+ if (functionCall) {
105
+ const toolName = functionCall.name || '';
106
+ const args = functionCall.args || {};
107
+ const toolCallId = `call_${randomUUID().slice(0, 8)}`;
108
+ yield { type: 'tool_call_start', toolCallId, toolName };
109
+ yield { type: 'tool_call_end', toolCallId, toolName, args };
110
+ contentBlocks.push({
111
+ type: 'tool_call',
112
+ toolCallId,
113
+ toolName,
114
+ args,
115
+ });
116
+ }
117
+ }
118
+ }
119
+ // Flush text
120
+ if (textAccumulator) {
121
+ contentBlocks.push({ type: 'text', text: textAccumulator });
122
+ }
123
+ const message = {
124
+ role: 'assistant',
125
+ content: contentBlocks,
126
+ usage,
127
+ stopReason,
128
+ };
129
+ yield { type: 'done', message, usage };
130
+ }
131
+ catch (err) {
132
+ yield { type: 'error', error: wrapFetchError(err, this.id) };
133
+ }
134
+ }
135
+ // -----------------------------------------------------------------------
136
+ // complete
137
+ // -----------------------------------------------------------------------
138
+ async complete(request) {
139
+ return consumeStream(this.stream(request));
140
+ }
141
+ // -----------------------------------------------------------------------
142
+ // listModels
143
+ // -----------------------------------------------------------------------
144
+ async listModels(apiKey, baseUrl) {
145
+ const base = (baseUrl || this.defaultBaseUrl).replace(/\/+$/, '');
146
+ const url = `${base}/v1beta/models?key=${encodeURIComponent(apiKey)}`;
147
+ try {
148
+ const res = await fetch(url, {
149
+ method: 'GET',
150
+ signal: AbortSignal.timeout(15_000),
151
+ });
152
+ if (!res.ok)
153
+ return [];
154
+ const data = await res.json();
155
+ return (data.models || [])
156
+ .filter((m) => {
157
+ const methods = (m.supportedGenerationMethods || []);
158
+ return methods.includes('generateContent') || methods.includes('streamGenerateContent');
159
+ })
160
+ .map((m) => {
161
+ const id = String(m.name || '').replace(/^models\//, '');
162
+ return {
163
+ id,
164
+ name: String(m.displayName || id),
165
+ provider: this.id,
166
+ contextWindow: m.inputTokenLimit || 128_000,
167
+ maxOutputTokens: m.outputTokenLimit || 8192,
168
+ supportsThinking: false,
169
+ supportsTools: true,
170
+ supportsImages: true,
171
+ cost: { input: 0, output: 0 },
172
+ };
173
+ });
174
+ }
175
+ catch {
176
+ return [];
177
+ }
178
+ }
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // Internal: request body builder
182
+ // ---------------------------------------------------------------------------
183
+ function buildRequestBody(request) {
184
+ const body = {
185
+ contents: convertMessages(request.messages),
186
+ };
187
+ if (request.systemPrompt) {
188
+ body.system_instruction = {
189
+ parts: [{ text: request.systemPrompt }],
190
+ };
191
+ }
192
+ if (request.tools && request.tools.length > 0) {
193
+ body.tools = [
194
+ {
195
+ functionDeclarations: request.tools.map(convertToolDefinition),
196
+ },
197
+ ];
198
+ }
199
+ // Generation config
200
+ const genConfig = {};
201
+ if (request.maxTokens) {
202
+ genConfig.maxOutputTokens = request.maxTokens;
203
+ }
204
+ if (request.temperature !== undefined) {
205
+ genConfig.temperature = request.temperature;
206
+ }
207
+ if (request.stopSequences && request.stopSequences.length > 0) {
208
+ genConfig.stopSequences = request.stopSequences;
209
+ }
210
+ if (Object.keys(genConfig).length > 0) {
211
+ body.generationConfig = genConfig;
212
+ }
213
+ return body;
214
+ }
215
+ // ---------------------------------------------------------------------------
216
+ // Internal: message format conversion
217
+ // ---------------------------------------------------------------------------
218
+ function convertMessages(messages) {
219
+ const result = [];
220
+ for (const msg of messages) {
221
+ switch (msg.role) {
222
+ case 'user':
223
+ result.push({
224
+ role: 'user',
225
+ parts: typeof msg.content === 'string'
226
+ ? [{ text: msg.content }]
227
+ : convertContentParts(msg.content),
228
+ });
229
+ break;
230
+ case 'assistant':
231
+ result.push({
232
+ role: 'model',
233
+ parts: convertAssistantParts(msg.content),
234
+ });
235
+ break;
236
+ case 'tool_result': {
237
+ const textContent = typeof msg.content === 'string'
238
+ ? msg.content
239
+ : msg.content.map((p) => p.type === 'text' ? p.text : '').join('');
240
+ // Gemini requires the function *name* here, not the call ID.
241
+ // Look backwards through the conversation for the matching
242
+ // tool_call block so we can resolve the actual name.
243
+ const toolName = resolveToolName(messages, msg.toolCallId);
244
+ result.push({
245
+ role: 'function',
246
+ parts: [
247
+ {
248
+ functionResponse: {
249
+ name: toolName,
250
+ response: {
251
+ content: textContent,
252
+ },
253
+ },
254
+ },
255
+ ],
256
+ });
257
+ break;
258
+ }
259
+ }
260
+ }
261
+ return result;
262
+ }
263
+ function convertContentParts(parts) {
264
+ return parts.map((part) => {
265
+ if (part.type === 'text') {
266
+ return { text: part.text };
267
+ }
268
+ return {
269
+ inlineData: {
270
+ mimeType: part.mediaType,
271
+ data: part.data,
272
+ },
273
+ };
274
+ });
275
+ }
276
+ function convertAssistantParts(blocks) {
277
+ const parts = [];
278
+ for (const block of blocks) {
279
+ switch (block.type) {
280
+ case 'text':
281
+ parts.push({ text: block.text });
282
+ break;
283
+ case 'thinking':
284
+ // Gemini doesn't have thinking blocks — emit as text
285
+ parts.push({ text: block.text });
286
+ break;
287
+ case 'tool_call':
288
+ parts.push({
289
+ functionCall: {
290
+ name: block.toolName,
291
+ args: block.args,
292
+ },
293
+ });
294
+ break;
295
+ }
296
+ }
297
+ return parts;
298
+ }
299
+ function convertToolDefinition(tool) {
300
+ return {
301
+ name: tool.name,
302
+ description: tool.description,
303
+ parameters: tool.parameters,
304
+ };
305
+ }
306
+ // ---------------------------------------------------------------------------
307
+ // Internal: helpers
308
+ // ---------------------------------------------------------------------------
309
+ /**
310
+ * Resolve a toolCallId back to the function name by scanning the conversation
311
+ * history for a matching tool_call block in an assistant message.
312
+ *
313
+ * Falls back to the toolCallId itself if no match is found (best-effort).
314
+ */
315
+ function resolveToolName(messages, toolCallId) {
316
+ for (let i = messages.length - 1; i >= 0; i--) {
317
+ const m = messages[i];
318
+ if (m.role === 'assistant') {
319
+ for (const block of m.content) {
320
+ if (block.type === 'tool_call' && block.toolCallId === toolCallId) {
321
+ return block.toolName;
322
+ }
323
+ }
324
+ }
325
+ }
326
+ return toolCallId;
327
+ }
328
+ function mapFinishReason(reason) {
329
+ switch (reason) {
330
+ case 'STOP': return 'end_turn';
331
+ case 'MAX_TOKENS': return 'max_tokens';
332
+ case 'SAFETY': return 'end_turn';
333
+ case 'RECITATION': return 'end_turn';
334
+ default: return 'end_turn';
335
+ }
336
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Provider registry — manages all registered LLM providers and provides
3
+ * convenience methods for streaming and completion.
4
+ */
5
+ import type { AssistantMessage, ModelInfo, StreamEvent } from '../types.js';
6
+ import type { LLMProvider, LLMRequest } from '../provider.js';
7
+ export declare class ProviderRegistry {
8
+ private readonly providers;
9
+ /** Register a provider. Overwrites any existing registration with the same id. */
10
+ register(provider: LLMProvider): void;
11
+ /** Retrieve a provider by id. */
12
+ get(id: string): LLMProvider | undefined;
13
+ /** Return all registered providers. */
14
+ getAll(): LLMProvider[];
15
+ /** Check whether a provider is registered. */
16
+ has(id: string): boolean;
17
+ /**
18
+ * Convenience: stream a completion through the named provider.
19
+ *
20
+ * @throws {LLMError} if the provider is not registered.
21
+ */
22
+ stream(providerId: string, request: LLMRequest): AsyncIterableIterator<StreamEvent>;
23
+ /**
24
+ * Convenience: non-streaming completion through the named provider.
25
+ *
26
+ * @throws {LLMError} if the provider is not registered.
27
+ */
28
+ complete(providerId: string, request: LLMRequest): Promise<AssistantMessage>;
29
+ /**
30
+ * Convenience: list models from the named provider.
31
+ *
32
+ * @throws {LLMError} if the provider is not registered.
33
+ */
34
+ listModels(providerId: string, apiKey: string, baseUrl?: string): Promise<ModelInfo[]>;
35
+ /** Get the default base URL for a provider. */
36
+ getDefaultBaseUrl(providerId: string): string;
37
+ private resolveProvider;
38
+ }
39
+ /**
40
+ * Create and populate a {@link ProviderRegistry} with all built-in providers.
41
+ * Call this once at server startup.
42
+ */
43
+ export declare function initProviders(): ProviderRegistry;
44
+ /**
45
+ * Get (or lazily create) the global singleton provider registry.
46
+ *
47
+ * Prefer calling `initProviders()` explicitly at startup and passing the
48
+ * registry around. This singleton exists as a convenience for modules that
49
+ * cannot easily receive the registry through dependency injection.
50
+ */
51
+ export declare function getProviderRegistry(): ProviderRegistry;
52
+ /**
53
+ * Replace the global singleton (useful for testing).
54
+ */
55
+ export declare function setProviderRegistry(registry: ProviderRegistry): void;
56
+ /**
57
+ * Stream a completion — drop-in replacement for `pi-ai` `streamSimple`.
58
+ *
59
+ * Uses the global singleton registry.
60
+ */
61
+ export declare function streamLLM(providerId: string, request: LLMRequest): AsyncIterableIterator<StreamEvent>;
62
+ /**
63
+ * Non-streaming completion — drop-in replacement for `pi-ai` `completeSimple`.
64
+ *
65
+ * Uses the global singleton registry.
66
+ */
67
+ export declare function completeLLM(providerId: string, request: LLMRequest): Promise<AssistantMessage>;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Provider registry — manages all registered LLM providers and provides
3
+ * convenience methods for streaming and completion.
4
+ */
5
+ import { LLMError } from '../provider.js';
6
+ import { AnthropicProvider } from './anthropic.js';
7
+ import { OpenAIProvider } from './openai.js';
8
+ import { GoogleProvider } from './google.js';
9
+ // ---------------------------------------------------------------------------
10
+ // ProviderRegistry
11
+ // ---------------------------------------------------------------------------
12
+ export class ProviderRegistry {
13
+ providers = new Map();
14
+ /** Register a provider. Overwrites any existing registration with the same id. */
15
+ register(provider) {
16
+ this.providers.set(provider.id, provider);
17
+ }
18
+ /** Retrieve a provider by id. */
19
+ get(id) {
20
+ return this.providers.get(id);
21
+ }
22
+ /** Return all registered providers. */
23
+ getAll() {
24
+ return Array.from(this.providers.values());
25
+ }
26
+ /** Check whether a provider is registered. */
27
+ has(id) {
28
+ return this.providers.has(id);
29
+ }
30
+ /**
31
+ * Convenience: stream a completion through the named provider.
32
+ *
33
+ * @throws {LLMError} if the provider is not registered.
34
+ */
35
+ stream(providerId, request) {
36
+ const provider = this.resolveProvider(providerId);
37
+ return provider.stream(request);
38
+ }
39
+ /**
40
+ * Convenience: non-streaming completion through the named provider.
41
+ *
42
+ * @throws {LLMError} if the provider is not registered.
43
+ */
44
+ async complete(providerId, request) {
45
+ const provider = this.resolveProvider(providerId);
46
+ return provider.complete(request);
47
+ }
48
+ /**
49
+ * Convenience: list models from the named provider.
50
+ *
51
+ * @throws {LLMError} if the provider is not registered.
52
+ */
53
+ async listModels(providerId, apiKey, baseUrl) {
54
+ const provider = this.resolveProvider(providerId);
55
+ return provider.listModels(apiKey, baseUrl);
56
+ }
57
+ /** Get the default base URL for a provider. */
58
+ getDefaultBaseUrl(providerId) {
59
+ return this.providers.get(providerId)?.defaultBaseUrl ?? '';
60
+ }
61
+ // -----------------------------------------------------------------------
62
+ // Internal
63
+ // -----------------------------------------------------------------------
64
+ resolveProvider(id) {
65
+ const provider = this.providers.get(id);
66
+ if (!provider) {
67
+ throw new LLMError(`Unknown LLM provider: "${id}". Registered providers: ${Array.from(this.providers.keys()).join(', ')}`, id, 'invalid_request', false);
68
+ }
69
+ return provider;
70
+ }
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Factory
74
+ // ---------------------------------------------------------------------------
75
+ /**
76
+ * Create and populate a {@link ProviderRegistry} with all built-in providers.
77
+ * Call this once at server startup.
78
+ */
79
+ export function initProviders() {
80
+ const registry = new ProviderRegistry();
81
+ registry.register(new AnthropicProvider());
82
+ registry.register(new OpenAIProvider());
83
+ registry.register(new GoogleProvider());
84
+ return registry;
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // Singleton
88
+ // ---------------------------------------------------------------------------
89
+ let _singleton;
90
+ /**
91
+ * Get (or lazily create) the global singleton provider registry.
92
+ *
93
+ * Prefer calling `initProviders()` explicitly at startup and passing the
94
+ * registry around. This singleton exists as a convenience for modules that
95
+ * cannot easily receive the registry through dependency injection.
96
+ */
97
+ export function getProviderRegistry() {
98
+ if (!_singleton) {
99
+ _singleton = initProviders();
100
+ }
101
+ return _singleton;
102
+ }
103
+ /**
104
+ * Replace the global singleton (useful for testing).
105
+ */
106
+ export function setProviderRegistry(registry) {
107
+ _singleton = registry;
108
+ }
109
+ // ---------------------------------------------------------------------------
110
+ // Top-level convenience functions (replacements for pi-ai helpers)
111
+ // ---------------------------------------------------------------------------
112
+ /**
113
+ * Stream a completion — drop-in replacement for `pi-ai` `streamSimple`.
114
+ *
115
+ * Uses the global singleton registry.
116
+ */
117
+ export function streamLLM(providerId, request) {
118
+ return getProviderRegistry().stream(providerId, request);
119
+ }
120
+ /**
121
+ * Non-streaming completion — drop-in replacement for `pi-ai` `completeSimple`.
122
+ *
123
+ * Uses the global singleton registry.
124
+ */
125
+ export async function completeLLM(providerId, request) {
126
+ return getProviderRegistry().complete(providerId, request);
127
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * OpenAI Chat Completions adapter.
3
+ *
4
+ * Handles all OpenAI-compatible APIs: OpenAI, xAI, Groq, DeepSeek, Cerebras,
5
+ * Mistral, and any custom endpoint that speaks the same wire format.
6
+ *
7
+ * Key challenges this adapter solves:
8
+ * - Tool call argument chunks arrive incrementally — we accumulate and parse
9
+ * the full JSON only when the tool call finishes.
10
+ * - Multiple tool calls can be in-flight in a single completion chunk.
11
+ * - The `[DONE]` sentinel terminates the SSE stream.
12
+ */
13
+ import type { AssistantMessage, ModelInfo, StreamEvent } from '../types.js';
14
+ import type { LLMProvider, LLMRequest } from '../provider.js';
15
+ export declare class OpenAIProvider implements LLMProvider {
16
+ readonly id = "openai";
17
+ readonly displayName = "OpenAI";
18
+ readonly defaultBaseUrl = "https://api.openai.com/v1";
19
+ stream(request: LLMRequest): AsyncIterableIterator<StreamEvent>;
20
+ complete(request: LLMRequest): Promise<AssistantMessage>;
21
+ listModels(apiKey: string, baseUrl?: string): Promise<ModelInfo[]>;
22
+ }