@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,353 @@
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 { LLMError, classifyHttpError, wrapFetchError } from '../provider.js';
14
+ import { parseSSEStream } from '../stream-utils.js';
15
+ import { consumeStream } from '../stream-utils.js';
16
+ // ---------------------------------------------------------------------------
17
+ // Constants
18
+ // ---------------------------------------------------------------------------
19
+ const DEFAULT_BASE_URL = 'https://api.openai.com/v1';
20
+ const DEFAULT_MAX_TOKENS = 16384;
21
+ // Model ID patterns to filter out non-chat models
22
+ const NON_CHAT_PATTERNS = [
23
+ /^text-embedding/i,
24
+ /^text-moderation/i,
25
+ /^text-search/i,
26
+ /^text-similarity/i,
27
+ /^code-search/i,
28
+ /^whisper/i,
29
+ /^tts/i,
30
+ /^dall-e/i,
31
+ /^davinci/i,
32
+ /^curie/i,
33
+ /^babbage/i,
34
+ /^ada(?![-])/i,
35
+ /embedding/i,
36
+ /moderation/i,
37
+ ];
38
+ function isChatModel(id) {
39
+ return !NON_CHAT_PATTERNS.some((pat) => pat.test(id));
40
+ }
41
+ // ---------------------------------------------------------------------------
42
+ // OpenAI provider
43
+ // ---------------------------------------------------------------------------
44
+ export class OpenAIProvider {
45
+ id = 'openai';
46
+ displayName = 'OpenAI';
47
+ defaultBaseUrl = DEFAULT_BASE_URL;
48
+ // -----------------------------------------------------------------------
49
+ // stream
50
+ // -----------------------------------------------------------------------
51
+ async *stream(request) {
52
+ const baseUrl = (request.baseUrl || this.defaultBaseUrl).replace(/\/+$/, '');
53
+ const url = `${baseUrl}/chat/completions`;
54
+ const body = buildRequestBody(request);
55
+ let response;
56
+ try {
57
+ response = await fetch(url, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/json',
61
+ 'Authorization': `Bearer ${request.apiKey}`,
62
+ },
63
+ body: JSON.stringify(body),
64
+ signal: request.signal,
65
+ });
66
+ }
67
+ catch (err) {
68
+ yield { type: 'error', error: wrapFetchError(err, this.id) };
69
+ return;
70
+ }
71
+ if (!response.ok) {
72
+ const text = await response.text().catch(() => '');
73
+ yield { type: 'error', error: classifyHttpError(response.status, text, this.id) };
74
+ return;
75
+ }
76
+ if (!response.body) {
77
+ yield { type: 'error', error: new LLMError('Response body is null', this.id, 'network_error', true) };
78
+ return;
79
+ }
80
+ // -- Parse SSE events --
81
+ const usage = { inputTokens: 0, outputTokens: 0 };
82
+ const contentBlocks = [];
83
+ let textAccumulator = '';
84
+ let stopReason;
85
+ // Accumulate tool calls by index
86
+ const toolAccumulators = new Map();
87
+ try {
88
+ for await (const sse of parseSSEStream(response.body, request.signal)) {
89
+ const raw = sse.data.trim();
90
+ // [DONE] sentinel
91
+ if (raw === '[DONE]')
92
+ break;
93
+ let data;
94
+ try {
95
+ data = JSON.parse(raw);
96
+ }
97
+ catch {
98
+ continue;
99
+ }
100
+ // Extract usage if present (some providers include it in the final chunk)
101
+ const usageData = data.usage;
102
+ if (usageData) {
103
+ usage.inputTokens = usageData.prompt_tokens ?? usage.inputTokens;
104
+ usage.outputTokens = usageData.completion_tokens ?? usage.outputTokens;
105
+ }
106
+ const choices = data.choices;
107
+ if (!choices || choices.length === 0)
108
+ continue;
109
+ const choice = choices[0];
110
+ const delta = choice.delta;
111
+ const finishReason = choice.finish_reason;
112
+ if (delta) {
113
+ // Text content
114
+ const content = delta.content;
115
+ if (content) {
116
+ textAccumulator += content;
117
+ yield { type: 'text_delta', delta: content };
118
+ }
119
+ // Tool calls
120
+ const toolCalls = delta.tool_calls;
121
+ if (toolCalls) {
122
+ for (const tc of toolCalls) {
123
+ const idx = tc.index ?? 0;
124
+ const fn = tc.function;
125
+ let acc = toolAccumulators.get(idx);
126
+ if (!acc) {
127
+ acc = {
128
+ index: idx,
129
+ toolCallId: tc.id || `call_${idx}`,
130
+ toolName: fn?.name || '',
131
+ argsJson: '',
132
+ started: false,
133
+ };
134
+ toolAccumulators.set(idx, acc);
135
+ }
136
+ // Update name if present in this chunk
137
+ if (fn?.name) {
138
+ acc.toolName = fn.name;
139
+ }
140
+ // Emit tool_call_start on first encounter
141
+ if (!acc.started && acc.toolName) {
142
+ acc.started = true;
143
+ yield { type: 'tool_call_start', toolCallId: acc.toolCallId, toolName: acc.toolName };
144
+ }
145
+ // Accumulate argument fragments
146
+ if (fn?.arguments) {
147
+ const argChunk = fn.arguments;
148
+ acc.argsJson += argChunk;
149
+ if (acc.started) {
150
+ yield { type: 'tool_call_delta', toolCallId: acc.toolCallId, argsDelta: argChunk };
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ // Finish reason handling
157
+ if (finishReason) {
158
+ stopReason = mapFinishReason(finishReason);
159
+ }
160
+ }
161
+ // Flush text block
162
+ if (textAccumulator) {
163
+ contentBlocks.push({ type: 'text', text: textAccumulator });
164
+ }
165
+ // Flush tool calls — parse accumulated args JSON
166
+ for (const acc of toolAccumulators.values()) {
167
+ let args = {};
168
+ try {
169
+ args = acc.argsJson ? JSON.parse(acc.argsJson) : {};
170
+ }
171
+ catch {
172
+ args = { __raw: acc.argsJson };
173
+ }
174
+ contentBlocks.push({
175
+ type: 'tool_call',
176
+ toolCallId: acc.toolCallId,
177
+ toolName: acc.toolName,
178
+ args,
179
+ });
180
+ yield { type: 'tool_call_end', toolCallId: acc.toolCallId, toolName: acc.toolName, args };
181
+ }
182
+ const message = {
183
+ role: 'assistant',
184
+ content: contentBlocks,
185
+ usage,
186
+ stopReason,
187
+ };
188
+ yield { type: 'done', message, usage };
189
+ }
190
+ catch (err) {
191
+ yield { type: 'error', error: wrapFetchError(err, this.id) };
192
+ }
193
+ }
194
+ // -----------------------------------------------------------------------
195
+ // complete
196
+ // -----------------------------------------------------------------------
197
+ async complete(request) {
198
+ return consumeStream(this.stream(request));
199
+ }
200
+ // -----------------------------------------------------------------------
201
+ // listModels
202
+ // -----------------------------------------------------------------------
203
+ async listModels(apiKey, baseUrl) {
204
+ const base = (baseUrl || this.defaultBaseUrl).replace(/\/+$/, '');
205
+ const url = `${base}/models`;
206
+ try {
207
+ const res = await fetch(url, {
208
+ method: 'GET',
209
+ headers: {
210
+ 'Authorization': `Bearer ${apiKey}`,
211
+ },
212
+ signal: AbortSignal.timeout(15_000),
213
+ });
214
+ if (!res.ok)
215
+ return [];
216
+ const data = await res.json();
217
+ return (data.data || [])
218
+ .filter((m) => isChatModel(String(m.id || '')))
219
+ .map((m) => ({
220
+ id: String(m.id || ''),
221
+ name: String(m.id || ''),
222
+ provider: this.id,
223
+ contextWindow: 128_000,
224
+ maxOutputTokens: 16_384,
225
+ supportsThinking: false,
226
+ supportsTools: true,
227
+ supportsImages: /gpt-4|o1|o3/i.test(String(m.id)),
228
+ cost: { input: 0, output: 0 },
229
+ }));
230
+ }
231
+ catch {
232
+ return [];
233
+ }
234
+ }
235
+ }
236
+ // ---------------------------------------------------------------------------
237
+ // Internal: request body builder
238
+ // ---------------------------------------------------------------------------
239
+ function buildRequestBody(request) {
240
+ const messages = convertMessages(request.messages, request.systemPrompt);
241
+ const body = {
242
+ model: request.model,
243
+ messages,
244
+ stream: true,
245
+ stream_options: { include_usage: true },
246
+ };
247
+ if (request.maxTokens) {
248
+ body.max_tokens = request.maxTokens;
249
+ }
250
+ if (request.temperature !== undefined) {
251
+ body.temperature = request.temperature;
252
+ }
253
+ if (request.stopSequences && request.stopSequences.length > 0) {
254
+ body.stop = request.stopSequences;
255
+ }
256
+ if (request.tools && request.tools.length > 0) {
257
+ body.tools = request.tools.map(convertToolDefinition);
258
+ }
259
+ return body;
260
+ }
261
+ // ---------------------------------------------------------------------------
262
+ // Internal: message format conversion
263
+ // ---------------------------------------------------------------------------
264
+ function convertMessages(messages, systemPrompt) {
265
+ const result = [];
266
+ // System prompt as the first message
267
+ if (systemPrompt) {
268
+ result.push({ role: 'system', content: systemPrompt });
269
+ }
270
+ for (const msg of messages) {
271
+ switch (msg.role) {
272
+ case 'user':
273
+ result.push({
274
+ role: 'user',
275
+ content: typeof msg.content === 'string'
276
+ ? msg.content
277
+ : convertContentParts(msg.content),
278
+ });
279
+ break;
280
+ case 'assistant':
281
+ result.push(convertAssistantMessage(msg));
282
+ break;
283
+ case 'tool_result':
284
+ result.push({
285
+ role: 'tool',
286
+ tool_call_id: msg.toolCallId,
287
+ content: typeof msg.content === 'string'
288
+ ? msg.content
289
+ : JSON.stringify(msg.content),
290
+ });
291
+ break;
292
+ }
293
+ }
294
+ return result;
295
+ }
296
+ function convertContentParts(parts) {
297
+ return parts.map((part) => {
298
+ if (part.type === 'text') {
299
+ return { type: 'text', text: part.text };
300
+ }
301
+ return {
302
+ type: 'image_url',
303
+ image_url: {
304
+ url: `data:${part.mediaType};base64,${part.data}`,
305
+ },
306
+ };
307
+ });
308
+ }
309
+ function convertAssistantMessage(msg) {
310
+ const result = { role: 'assistant' };
311
+ // Extract text content
312
+ const textParts = msg.content.filter((b) => b.type === 'text');
313
+ if (textParts.length > 0) {
314
+ result.content = textParts.map((b) => b.text).join('');
315
+ }
316
+ else {
317
+ result.content = null;
318
+ }
319
+ // Extract tool calls
320
+ const toolCalls = msg.content.filter((b) => b.type === 'tool_call');
321
+ if (toolCalls.length > 0) {
322
+ result.tool_calls = toolCalls.map((tc) => ({
323
+ id: tc.toolCallId,
324
+ type: 'function',
325
+ function: {
326
+ name: tc.toolName,
327
+ arguments: JSON.stringify(tc.args),
328
+ },
329
+ }));
330
+ }
331
+ return result;
332
+ }
333
+ function convertToolDefinition(tool) {
334
+ return {
335
+ type: 'function',
336
+ function: {
337
+ name: tool.name,
338
+ description: tool.description,
339
+ parameters: tool.parameters,
340
+ },
341
+ };
342
+ }
343
+ // ---------------------------------------------------------------------------
344
+ // Internal: helpers
345
+ // ---------------------------------------------------------------------------
346
+ function mapFinishReason(reason) {
347
+ switch (reason) {
348
+ case 'stop': return 'end_turn';
349
+ case 'tool_calls': return 'tool_use';
350
+ case 'length': return 'max_tokens';
351
+ default: return 'end_turn';
352
+ }
353
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * SSE stream parser and convenience helpers for LLM streaming.
3
+ */
4
+ import type { AssistantMessage, StreamEvent } from './types.js';
5
+ export interface SSEEvent {
6
+ /** The `event:` field value. Undefined when omitted by the server. */
7
+ event?: string;
8
+ /** The concatenated `data:` field value (may span multiple lines). */
9
+ data: string;
10
+ /** The `id:` field value. */
11
+ id?: string;
12
+ }
13
+ /**
14
+ * Parse a `ReadableStream<Uint8Array>` (typically `Response.body`) into an
15
+ * async iterator of {@link SSEEvent} objects.
16
+ *
17
+ * Handles:
18
+ * - Multi-line `data:` fields (concatenated with `\n`)
19
+ * - `event:` and `id:` fields
20
+ * - Proper `\n\n` event boundary detection
21
+ * - Graceful abort via `signal`
22
+ *
23
+ * The iterator ends when the stream closes or `signal` fires.
24
+ */
25
+ export declare function parseSSEStream(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncIterableIterator<SSEEvent>;
26
+ /**
27
+ * Consume a StreamEvent iterator and return the final AssistantMessage.
28
+ *
29
+ * This is the generic "complete" implementation that any provider can use:
30
+ * it iterates the stream, discards incremental deltas, and returns the
31
+ * assembled message from the `done` event.
32
+ */
33
+ export declare function consumeStream(stream: AsyncIterableIterator<StreamEvent>): Promise<AssistantMessage>;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * SSE stream parser and convenience helpers for LLM streaming.
3
+ */
4
+ // ---------------------------------------------------------------------------
5
+ // Generic SSE parser
6
+ // ---------------------------------------------------------------------------
7
+ /**
8
+ * Parse a `ReadableStream<Uint8Array>` (typically `Response.body`) into an
9
+ * async iterator of {@link SSEEvent} objects.
10
+ *
11
+ * Handles:
12
+ * - Multi-line `data:` fields (concatenated with `\n`)
13
+ * - `event:` and `id:` fields
14
+ * - Proper `\n\n` event boundary detection
15
+ * - Graceful abort via `signal`
16
+ *
17
+ * The iterator ends when the stream closes or `signal` fires.
18
+ */
19
+ export async function* parseSSEStream(body, signal) {
20
+ const reader = body.getReader();
21
+ const decoder = new TextDecoder();
22
+ let buffer = '';
23
+ // Set up abort listener to release the reader
24
+ const onAbort = () => {
25
+ reader.cancel().catch(() => { });
26
+ };
27
+ signal?.addEventListener('abort', onAbort, { once: true });
28
+ try {
29
+ while (true) {
30
+ if (signal?.aborted)
31
+ break;
32
+ const { done, value } = await reader.read();
33
+ if (done)
34
+ break;
35
+ buffer += decoder.decode(value, { stream: true });
36
+ // Process complete events separated by \n\n
37
+ let boundary;
38
+ while ((boundary = buffer.indexOf('\n\n')) !== -1) {
39
+ const rawEvent = buffer.slice(0, boundary);
40
+ buffer = buffer.slice(boundary + 2);
41
+ const parsed = parseSSEBlock(rawEvent);
42
+ if (parsed) {
43
+ yield parsed;
44
+ }
45
+ }
46
+ }
47
+ // Flush any trailing data that wasn't terminated by \n\n
48
+ if (buffer.trim().length > 0) {
49
+ const parsed = parseSSEBlock(buffer);
50
+ if (parsed) {
51
+ yield parsed;
52
+ }
53
+ }
54
+ }
55
+ finally {
56
+ signal?.removeEventListener('abort', onAbort);
57
+ reader.releaseLock();
58
+ }
59
+ }
60
+ /**
61
+ * Parse a single SSE block (the text between two `\n\n` boundaries) into an
62
+ * {@link SSEEvent}. Returns `undefined` for comment-only or empty blocks.
63
+ */
64
+ function parseSSEBlock(block) {
65
+ let event;
66
+ let id;
67
+ const dataLines = [];
68
+ for (const line of block.split('\n')) {
69
+ // Comment lines start with ':'
70
+ if (line.startsWith(':'))
71
+ continue;
72
+ const colonIdx = line.indexOf(':');
73
+ if (colonIdx === -1) {
74
+ // Field with no value — treat field name as the value (per SSE spec)
75
+ continue;
76
+ }
77
+ const field = line.slice(0, colonIdx);
78
+ // Value starts after ':' — strip a single leading space if present
79
+ let value = line.slice(colonIdx + 1);
80
+ if (value.startsWith(' ')) {
81
+ value = value.slice(1);
82
+ }
83
+ switch (field) {
84
+ case 'event':
85
+ event = value;
86
+ break;
87
+ case 'data':
88
+ dataLines.push(value);
89
+ break;
90
+ case 'id':
91
+ id = value;
92
+ break;
93
+ // 'retry' and unknown fields are ignored
94
+ }
95
+ }
96
+ if (dataLines.length === 0 && event === undefined) {
97
+ return undefined;
98
+ }
99
+ return {
100
+ event,
101
+ data: dataLines.join('\n'),
102
+ id,
103
+ };
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // Convenience: streamLLM / completeLLM
107
+ // ---------------------------------------------------------------------------
108
+ // These are re-exported from providers/index.ts where the ProviderRegistry
109
+ // singleton is available. Declared here as standalone signatures for the
110
+ // module's public API documentation.
111
+ /**
112
+ * Consume a StreamEvent iterator and return the final AssistantMessage.
113
+ *
114
+ * This is the generic "complete" implementation that any provider can use:
115
+ * it iterates the stream, discards incremental deltas, and returns the
116
+ * assembled message from the `done` event.
117
+ */
118
+ export async function consumeStream(stream) {
119
+ let result;
120
+ for await (const event of stream) {
121
+ if (event.type === 'done') {
122
+ result = event.message;
123
+ }
124
+ else if (event.type === 'error') {
125
+ throw event.error;
126
+ }
127
+ }
128
+ if (!result) {
129
+ throw new Error('Stream ended without a done event');
130
+ }
131
+ return result;
132
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Core LLM types — provider-agnostic message, content, and streaming types.
3
+ *
4
+ * These types form the universal contract between the provider adapters and
5
+ * the agent engine. Every provider adapter converts its native wire format
6
+ * into these types so the rest of the system never touches provider-specific
7
+ * structures.
8
+ */
9
+ export interface TextContentPart {
10
+ type: 'text';
11
+ text: string;
12
+ }
13
+ export interface ImageContentPart {
14
+ type: 'image';
15
+ mediaType: string;
16
+ data: string;
17
+ }
18
+ export type ContentPart = TextContentPart | ImageContentPart;
19
+ export interface TextBlock {
20
+ type: 'text';
21
+ text: string;
22
+ }
23
+ export interface ThinkingBlock {
24
+ type: 'thinking';
25
+ text: string;
26
+ /** Anthropic thinking block signature — required when sending back in conversation history. */
27
+ signature?: string;
28
+ }
29
+ export interface ToolCallBlock {
30
+ type: 'tool_call';
31
+ toolCallId: string;
32
+ toolName: string;
33
+ args: Record<string, unknown>;
34
+ }
35
+ export type ContentBlock = TextBlock | ThinkingBlock | ToolCallBlock;
36
+ export interface UserMessage {
37
+ role: 'user';
38
+ content: string | ContentPart[];
39
+ timestamp?: number;
40
+ }
41
+ export interface AssistantMessage {
42
+ role: 'assistant';
43
+ content: ContentBlock[];
44
+ usage?: TokenUsage;
45
+ stopReason?: StopReason;
46
+ }
47
+ export interface ToolResultMessage {
48
+ role: 'tool_result';
49
+ toolCallId: string;
50
+ content: string | ContentPart[];
51
+ isError?: boolean;
52
+ }
53
+ export type Message = UserMessage | AssistantMessage | ToolResultMessage;
54
+ export type StopReason = 'end_turn' | 'tool_use' | 'max_tokens' | 'stop_sequence';
55
+ export interface TokenUsage {
56
+ inputTokens: number;
57
+ outputTokens: number;
58
+ cacheReadTokens?: number;
59
+ cacheWriteTokens?: number;
60
+ cost?: number;
61
+ }
62
+ export type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
63
+ /** Map ThinkingLevel to Anthropic budget_tokens values. */
64
+ export declare const THINKING_BUDGET: Record<ThinkingLevel, number>;
65
+ export interface TextDeltaEvent {
66
+ type: 'text_delta';
67
+ delta: string;
68
+ }
69
+ export interface ThinkingStartEvent {
70
+ type: 'thinking_start';
71
+ }
72
+ export interface ThinkingDeltaEvent {
73
+ type: 'thinking_delta';
74
+ delta: string;
75
+ }
76
+ export interface ToolCallStartEvent {
77
+ type: 'tool_call_start';
78
+ toolCallId: string;
79
+ toolName: string;
80
+ }
81
+ export interface ToolCallDeltaEvent {
82
+ type: 'tool_call_delta';
83
+ toolCallId: string;
84
+ argsDelta: string;
85
+ }
86
+ export interface ToolCallEndEvent {
87
+ type: 'tool_call_end';
88
+ toolCallId: string;
89
+ toolName: string;
90
+ args: Record<string, unknown>;
91
+ }
92
+ export interface DoneEvent {
93
+ type: 'done';
94
+ message: AssistantMessage;
95
+ usage: TokenUsage;
96
+ }
97
+ export interface ErrorEvent {
98
+ type: 'error';
99
+ error: Error;
100
+ }
101
+ export type StreamEvent = TextDeltaEvent | ThinkingStartEvent | ThinkingDeltaEvent | ToolCallStartEvent | ToolCallDeltaEvent | ToolCallEndEvent | DoneEvent | ErrorEvent;
102
+ /** JSON Schema object — kept deliberately loose to avoid coupling to a schema lib. */
103
+ export type JSONSchema = Record<string, unknown>;
104
+ export interface ToolDefinition {
105
+ name: string;
106
+ description: string;
107
+ parameters: JSONSchema;
108
+ }
109
+ export interface ModelCost {
110
+ /** Cost per 1M input tokens (USD). */
111
+ input: number;
112
+ /** Cost per 1M output tokens (USD). */
113
+ output: number;
114
+ /** Cost per 1M cache-read tokens (USD). */
115
+ cacheRead?: number;
116
+ /** Cost per 1M cache-write tokens (USD). */
117
+ cacheWrite?: number;
118
+ }
119
+ export interface ModelInfo {
120
+ id: string;
121
+ name: string;
122
+ provider: string;
123
+ contextWindow: number;
124
+ maxOutputTokens: number;
125
+ supportsThinking: boolean;
126
+ supportsTools: boolean;
127
+ supportsImages: boolean;
128
+ cost: ModelCost;
129
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Core LLM types — provider-agnostic message, content, and streaming types.
3
+ *
4
+ * These types form the universal contract between the provider adapters and
5
+ * the agent engine. Every provider adapter converts its native wire format
6
+ * into these types so the rest of the system never touches provider-specific
7
+ * structures.
8
+ */
9
+ /** Map ThinkingLevel to Anthropic budget_tokens values. */
10
+ export const THINKING_BUDGET = {
11
+ off: 0,
12
+ minimal: 1024,
13
+ low: 4096,
14
+ medium: 10240,
15
+ high: 32768,
16
+ xhigh: 65536,
17
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * CodeAct Sandbox — Console Capture
3
+ *
4
+ * Captures console.log/warn/error/info calls from inside the isolated-vm
5
+ * sandbox and buffers them on the host side. Uses synchronous ivm.Reference
6
+ * callbacks (applySync) so console calls behave naturally in sandbox code.
7
+ */
8
+ import ivm from 'isolated-vm';
9
+ import type { ConsoleEntry } from './types.js';
10
+ /**
11
+ * ConsoleCapture intercepts console method calls inside an isolated-vm
12
+ * Context and stores them as ConsoleEntry records on the host.
13
+ */
14
+ export declare class ConsoleCapture {
15
+ private entries;
16
+ /**
17
+ * Inject console overrides into the given isolated-vm context.
18
+ *
19
+ * Replaces globalThis.console with an object whose log/warn/error/info
20
+ * methods forward arguments (stringified) to the host via a synchronous
21
+ * ivm.Reference callback.
22
+ */
23
+ inject(context: ivm.Context): Promise<void>;
24
+ /**
25
+ * Return all captured console entries and clear the internal buffer.
26
+ * Typically called after each execute() to collect output.
27
+ */
28
+ drain(): ConsoleEntry[];
29
+ }