bare-agent 0.11.0 β†’ 0.12.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.
Files changed (65) hide show
  1. package/bin/cli.d.ts +4 -0
  2. package/bin/cli.js +40 -10
  3. package/bin/test-provider.d.ts +2 -0
  4. package/bin/test-provider.js +5 -1
  5. package/index.d.ts +20 -0
  6. package/package.json +44 -10
  7. package/src/bareguard-adapter.d.ts +118 -0
  8. package/src/bareguard-adapter.js +75 -3
  9. package/src/checkpoint.d.ts +61 -0
  10. package/src/checkpoint.js +17 -8
  11. package/src/circuit-breaker.d.ts +70 -0
  12. package/src/circuit-breaker.js +20 -4
  13. package/src/errors.d.ts +106 -0
  14. package/src/errors.js +50 -1
  15. package/src/loop.d.ts +135 -0
  16. package/src/loop.js +73 -17
  17. package/src/mcp-bridge.d.ts +133 -0
  18. package/src/mcp-bridge.js +179 -27
  19. package/src/mcp.d.ts +4 -0
  20. package/src/memory.d.ts +50 -0
  21. package/src/memory.js +22 -2
  22. package/src/planner.d.ts +62 -0
  23. package/src/planner.js +26 -7
  24. package/src/provider-anthropic.d.ts +55 -0
  25. package/src/provider-anthropic.js +32 -11
  26. package/src/provider-clipipe.d.ts +86 -0
  27. package/src/provider-clipipe.js +28 -18
  28. package/src/provider-fallback.d.ts +44 -0
  29. package/src/provider-fallback.js +18 -8
  30. package/src/provider-ollama.d.ts +41 -0
  31. package/src/provider-ollama.js +27 -7
  32. package/src/provider-openai.d.ts +57 -0
  33. package/src/provider-openai.js +31 -16
  34. package/src/providers.d.ts +6 -0
  35. package/src/retry.d.ts +44 -0
  36. package/src/retry.js +15 -1
  37. package/src/run-plan.d.ts +126 -0
  38. package/src/run-plan.js +46 -13
  39. package/src/scheduler.d.ts +102 -0
  40. package/src/scheduler.js +32 -4
  41. package/src/state.d.ts +45 -0
  42. package/src/state.js +18 -2
  43. package/src/store-jsonfile.d.ts +85 -0
  44. package/src/store-jsonfile.js +33 -8
  45. package/src/store-sqlite.d.ts +90 -0
  46. package/src/store-sqlite.js +31 -7
  47. package/src/stores.d.ts +3 -0
  48. package/src/stream.d.ts +79 -0
  49. package/src/stream.js +32 -0
  50. package/src/tools.d.ts +8 -0
  51. package/src/transport-jsonl.d.ts +30 -0
  52. package/src/transport-jsonl.js +13 -0
  53. package/src/transports.d.ts +2 -0
  54. package/tools/browse.d.ts +10 -0
  55. package/tools/browse.js +2 -0
  56. package/tools/defer.d.ts +33 -0
  57. package/tools/defer.js +12 -3
  58. package/tools/mobile.d.ts +34 -0
  59. package/tools/mobile.js +28 -15
  60. package/tools/shell.d.ts +31 -0
  61. package/tools/shell.js +55 -6
  62. package/tools/spawn.d.ts +107 -0
  63. package/tools/spawn.js +24 -5
  64. package/types/index.d.ts +66 -0
  65. package/types/shims.d.ts +16 -0
@@ -0,0 +1,62 @@
1
+ export type Provider = import("../types").Provider;
2
+ export type Step = {
3
+ /**
4
+ * - Unique step identifier.
5
+ */
6
+ id: string;
7
+ /**
8
+ * - Description of the step to execute.
9
+ */
10
+ action: string;
11
+ /**
12
+ * - Ids of steps that must complete first.
13
+ */
14
+ dependsOn: string[];
15
+ /**
16
+ * - Lifecycle status (e.g. 'pending').
17
+ */
18
+ status: string;
19
+ };
20
+ export type PlannerOptions = {
21
+ /**
22
+ * - LLM provider (must implement generate()).
23
+ */
24
+ provider: Provider;
25
+ /**
26
+ * - Custom planning prompt override.
27
+ */
28
+ prompt?: string | undefined;
29
+ /**
30
+ * - Cache time-to-live in ms. 0 disables caching.
31
+ */
32
+ cacheTTL?: number | undefined;
33
+ };
34
+ export class Planner {
35
+ /**
36
+ * @param {PlannerOptions} options
37
+ * @throws {Error} `[Planner] requires a provider` β€” when options.provider is missing.
38
+ */
39
+ constructor(options?: PlannerOptions);
40
+ provider: import("../types").Provider;
41
+ prompt: string;
42
+ _cacheTTL: number;
43
+ _cache: Map<any, any>;
44
+ /**
45
+ * Generate a step DAG from a goal.
46
+ * @param {string} goal - The user's goal to decompose.
47
+ * @param {{info?: string}} [context={}] - Optional context with info field.
48
+ * @returns {Promise<Step[]>}
49
+ * @throws {Error} `[Planner] could not parse plan` β€” when LLM output is not parseable JSON.
50
+ * @throws {Error} `[Planner] expected JSON array` β€” when parsed result is not an array.
51
+ * @throws {Error} `[Planner] step missing id or action` β€” when a step lacks required fields.
52
+ */
53
+ plan(goal: string, context?: {
54
+ info?: string;
55
+ }): Promise<Step[]>;
56
+ clearCache(): void;
57
+ /**
58
+ * @param {string} text - Raw LLM output to parse into steps.
59
+ * @returns {Step[]}
60
+ */
61
+ _parse(text: string): Step[];
62
+ }
package/src/planner.js CHANGED
@@ -1,5 +1,22 @@
1
1
  'use strict';
2
2
 
3
+ /** @typedef {import('../types').Provider} Provider */
4
+
5
+ /**
6
+ * @typedef {object} Step
7
+ * @property {string} id - Unique step identifier.
8
+ * @property {string} action - Description of the step to execute.
9
+ * @property {string[]} dependsOn - Ids of steps that must complete first.
10
+ * @property {string} status - Lifecycle status (e.g. 'pending').
11
+ */
12
+
13
+ /**
14
+ * @typedef {object} PlannerOptions
15
+ * @property {Provider} provider - LLM provider (must implement generate()).
16
+ * @property {string} [prompt] - Custom planning prompt override.
17
+ * @property {number} [cacheTTL] - Cache time-to-live in ms. 0 disables caching.
18
+ */
19
+
3
20
  const PLAN_PROMPT = `You are a planning agent. Break the user's goal into concrete steps.
4
21
 
5
22
  Rules:
@@ -16,12 +33,10 @@ Output format:
16
33
 
17
34
  class Planner {
18
35
  /**
19
- * @param {object} options
20
- * @param {object} options.provider - LLM provider (must implement generate()).
21
- * @param {string} [options.prompt] - Custom planning prompt override.
36
+ * @param {PlannerOptions} options
22
37
  * @throws {Error} `[Planner] requires a provider` β€” when options.provider is missing.
23
38
  */
24
- constructor(options = {}) {
39
+ constructor(options = /** @type {PlannerOptions} */ ({})) {
25
40
  if (!options.provider) throw new Error('[Planner] requires a provider');
26
41
  this.provider = options.provider;
27
42
  this.prompt = options.prompt || PLAN_PROMPT;
@@ -32,8 +47,8 @@ class Planner {
32
47
  /**
33
48
  * Generate a step DAG from a goal.
34
49
  * @param {string} goal - The user's goal to decompose.
35
- * @param {object} [context={}] - Optional context with info field.
36
- * @returns {Promise<Array<{id: string, action: string, dependsOn: string[], status: string}>>}
50
+ * @param {{info?: string}} [context={}] - Optional context with info field.
51
+ * @returns {Promise<Step[]>}
37
52
  * @throws {Error} `[Planner] could not parse plan` β€” when LLM output is not parseable JSON.
38
53
  * @throws {Error} `[Planner] expected JSON array` β€” when parsed result is not an array.
39
54
  * @throws {Error} `[Planner] step missing id or action` β€” when a step lacks required fields.
@@ -74,6 +89,10 @@ class Planner {
74
89
  this._cache.clear();
75
90
  }
76
91
 
92
+ /**
93
+ * @param {string} text - Raw LLM output to parse into steps.
94
+ * @returns {Step[]}
95
+ */
77
96
  _parse(text) {
78
97
  // Extract JSON array from response (handle markdown code blocks)
79
98
  const cleaned = text.replace(/^```(?:json)?\s*\n?/m, '').replace(/\n?```\s*$/m, '').trim();
@@ -93,7 +112,7 @@ class Planner {
93
112
  const ids = new Set(steps.map(s => s.id));
94
113
  return steps.map(s => {
95
114
  if (!s.id || !s.action) throw new Error(`[Planner] step missing id or action: ${JSON.stringify(s)}`);
96
- const deps = (s.dependsOn || []).filter(d => ids.has(d));
115
+ const deps = (s.dependsOn || []).filter(/** @param {string} d */ d => ids.has(d));
97
116
  return { id: s.id, action: s.action, dependsOn: deps, status: 'pending' };
98
117
  });
99
118
  }
@@ -0,0 +1,55 @@
1
+ export type Message = import("../types").Message;
2
+ export type ToolDef = import("../types").ToolDef;
3
+ export type GenerateResult = import("../types").GenerateResult;
4
+ export type AnthropicOptions = {
5
+ /**
6
+ * - Anthropic API key (required).
7
+ */
8
+ apiKey?: string | undefined;
9
+ /**
10
+ * - Model ID.
11
+ */
12
+ model?: string | undefined;
13
+ /**
14
+ * - Attach the full upstream response to `err.body` on HTTP errors (off by default to avoid leaking unexpected fields through error logs; `err.message` still carries the API error).
15
+ */
16
+ exposeErrorBody?: boolean | undefined;
17
+ };
18
+ /** @typedef {import('../types').Message} Message */
19
+ /** @typedef {import('../types').ToolDef} ToolDef */
20
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
21
+ /**
22
+ * @typedef {object} AnthropicOptions
23
+ * @property {string} [apiKey] - Anthropic API key (required).
24
+ * @property {string} [model='claude-haiku-4-5-20251001'] - Model ID.
25
+ * @property {boolean} [exposeErrorBody=false] - Attach the full upstream response to `err.body` on HTTP errors (off by default to avoid leaking unexpected fields through error logs; `err.message` still carries the API error).
26
+ */
27
+ export class AnthropicProvider {
28
+ /**
29
+ * @param {AnthropicOptions} [options]
30
+ * @throws {Error} `[AnthropicProvider] requires apiKey` β€” when apiKey is missing.
31
+ */
32
+ constructor(options?: AnthropicOptions);
33
+ apiKey: string;
34
+ model: string;
35
+ exposeErrorBody: boolean;
36
+ /**
37
+ * Generate a response from the Anthropic API.
38
+ * @param {Message[]} messages - Conversation messages (OpenAI format, auto-converted).
39
+ * @param {ToolDef[]} [tools=[]] - Tool definitions.
40
+ * @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, system).
41
+ * @returns {Promise<GenerateResult>}
42
+ * @throws {Error} `[AnthropicProvider] ...` β€” on HTTP errors (4xx/5xx) or invalid JSON response.
43
+ */
44
+ generate(messages: Message[], tools?: ToolDef[], options?: Record<string, any>): Promise<GenerateResult>;
45
+ /**
46
+ * @param {Message} msg
47
+ * @returns {any}
48
+ */
49
+ _toAnthropicMessage(msg: Message): any;
50
+ /**
51
+ * @param {Record<string, any>} body
52
+ * @returns {Promise<any>}
53
+ */
54
+ _request(body: Record<string, any>): Promise<any>;
55
+ }
@@ -3,12 +3,20 @@
3
3
  const https = require('https');
4
4
  const { ProviderError } = require('./errors');
5
5
 
6
+ /** @typedef {import('../types').Message} Message */
7
+ /** @typedef {import('../types').ToolDef} ToolDef */
8
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
9
+
10
+ /**
11
+ * @typedef {object} AnthropicOptions
12
+ * @property {string} [apiKey] - Anthropic API key (required).
13
+ * @property {string} [model='claude-haiku-4-5-20251001'] - Model ID.
14
+ * @property {boolean} [exposeErrorBody=false] - Attach the full upstream response to `err.body` on HTTP errors (off by default to avoid leaking unexpected fields through error logs; `err.message` still carries the API error).
15
+ */
16
+
6
17
  class AnthropicProvider {
7
18
  /**
8
- * @param {object} options
9
- * @param {string} options.apiKey - Anthropic API key (required).
10
- * @param {string} [options.model='claude-haiku-4-5-20251001'] - Model ID.
11
- * @param {boolean} [options.exposeErrorBody=false] - Attach the full upstream response to `err.body` on HTTP errors (off by default to avoid leaking unexpected fields through error logs; `err.message` still carries the API error).
19
+ * @param {AnthropicOptions} [options]
12
20
  * @throws {Error} `[AnthropicProvider] requires apiKey` β€” when apiKey is missing.
13
21
  */
14
22
  constructor(options = {}) {
@@ -21,15 +29,17 @@ class AnthropicProvider {
21
29
 
22
30
  /**
23
31
  * Generate a response from the Anthropic API.
24
- * @param {Array<object>} messages - Conversation messages (OpenAI format, auto-converted).
25
- * @param {Array<object>} [tools=[]] - Tool definitions.
26
- * @param {object} [options={}] - Options (temperature, maxTokens, system).
27
- * @returns {Promise<{text: string, toolCalls: Array, usage: object}>}
32
+ * @param {Message[]} messages - Conversation messages (OpenAI format, auto-converted).
33
+ * @param {ToolDef[]} [tools=[]] - Tool definitions.
34
+ * @param {Record<string, any>} [options={}] - Options (temperature, maxTokens, system).
35
+ * @returns {Promise<GenerateResult>}
28
36
  * @throws {Error} `[AnthropicProvider] ...` β€” on HTTP errors (4xx/5xx) or invalid JSON response.
29
37
  */
30
38
  async generate(messages, tools = [], options = {}) {
31
39
  // Separate system message from conversation messages
40
+ /** @type {any} */
32
41
  let system;
42
+ /** @type {any[]} */
33
43
  const msgs = [];
34
44
  for (const m of messages) {
35
45
  if (m.role === 'system') {
@@ -42,6 +52,7 @@ class AnthropicProvider {
42
52
  // Override with options.system if provided
43
53
  if (options.system) system = options.system;
44
54
 
55
+ /** @type {Record<string, any>} */
45
56
  const body = {
46
57
  model: this.model,
47
58
  max_tokens: options.maxTokens || 4096,
@@ -60,6 +71,7 @@ class AnthropicProvider {
60
71
  const data = await this._request(body);
61
72
 
62
73
  let text = '';
74
+ /** @type {import('../types').ToolCall[]} */
63
75
  const toolCalls = [];
64
76
  for (const block of data.content) {
65
77
  if (block.type === 'text') text += block.text;
@@ -78,6 +90,10 @@ class AnthropicProvider {
78
90
  };
79
91
  }
80
92
 
93
+ /**
94
+ * @param {Message} msg
95
+ * @returns {any}
96
+ */
81
97
  _toAnthropicMessage(msg) {
82
98
  // Convert OpenAI-format tool results β†’ Anthropic tool_result blocks
83
99
  if (msg.role === 'tool') {
@@ -91,7 +107,8 @@ class AnthropicProvider {
91
107
  };
92
108
  }
93
109
  // Convert OpenAI-format assistant tool_calls β†’ Anthropic tool_use content blocks
94
- if (msg.role === 'assistant' && msg.tool_calls?.length > 0) {
110
+ if (msg.role === 'assistant' && msg.tool_calls && msg.tool_calls.length > 0) {
111
+ /** @type {any[]} */
95
112
  const content = [];
96
113
  if (msg.content) content.push({ type: 'text', text: msg.content });
97
114
  for (const tc of msg.tool_calls) {
@@ -109,6 +126,10 @@ class AnthropicProvider {
109
126
  return { role: msg.role, content: msg.content };
110
127
  }
111
128
 
129
+ /**
130
+ * @param {Record<string, any>} body
131
+ * @returns {Promise<any>}
132
+ */
112
133
  _request(body) {
113
134
  return new Promise((resolve, reject) => {
114
135
  const payload = JSON.stringify(body);
@@ -126,10 +147,10 @@ class AnthropicProvider {
126
147
  res.on('end', () => {
127
148
  try {
128
149
  const parsed = JSON.parse(chunks);
129
- if (res.statusCode >= 400) {
150
+ if ((res.statusCode ?? 0) >= 400) {
130
151
  return reject(new ProviderError(
131
152
  `[AnthropicProvider] ${parsed.error?.message || `HTTP ${res.statusCode}`}`,
132
- { status: res.statusCode, body: this.exposeErrorBody ? parsed : undefined }
153
+ /** @type {any} */ ({ status: res.statusCode, body: this.exposeErrorBody ? parsed : undefined })
133
154
  ));
134
155
  }
135
156
  resolve(parsed);
@@ -0,0 +1,86 @@
1
+ export type Message = import("../types").Message;
2
+ export type ToolDef = import("../types").ToolDef;
3
+ export type GenerateResult = import("../types").GenerateResult;
4
+ export type CLIPipeOptions = {
5
+ /**
6
+ * - CLI command to spawn (required).
7
+ */
8
+ command?: string | undefined;
9
+ /**
10
+ * - Arguments to pass to the command.
11
+ */
12
+ args?: string[] | undefined;
13
+ /**
14
+ * - Working directory for the child process.
15
+ */
16
+ cwd?: string | undefined;
17
+ /**
18
+ * - Environment variables for the child process.
19
+ */
20
+ env?: Record<string, string> | undefined;
21
+ /**
22
+ * - Timeout in milliseconds.
23
+ */
24
+ timeout?: number | undefined;
25
+ /**
26
+ * - CLI flag for system prompt (e.g. '--system'). When set, system messages are extracted and passed via this flag instead of stdin.
27
+ */
28
+ systemPromptFlag?: string | undefined;
29
+ /**
30
+ * - Called with each stdout chunk as it streams.
31
+ */
32
+ onChunk?: ((chunk: string) => void) | undefined;
33
+ };
34
+ /** @typedef {import('../types').Message} Message */
35
+ /** @typedef {import('../types').ToolDef} ToolDef */
36
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
37
+ /**
38
+ * @typedef {object} CLIPipeOptions
39
+ * @property {string} [command] - CLI command to spawn (required).
40
+ * @property {string[]} [args=[]] - Arguments to pass to the command.
41
+ * @property {string} [cwd] - Working directory for the child process.
42
+ * @property {Record<string, string>} [env] - Environment variables for the child process.
43
+ * @property {number} [timeout=30000] - Timeout in milliseconds.
44
+ * @property {string} [systemPromptFlag] - CLI flag for system prompt (e.g. '--system'). When set, system messages are extracted and passed via this flag instead of stdin.
45
+ * @property {(chunk: string) => void} [onChunk] - Called with each stdout chunk as it streams.
46
+ */
47
+ export class CLIPipeProvider {
48
+ /**
49
+ * Provider that pipes prompts to a CLI command via stdin and reads stdout.
50
+ * @param {CLIPipeOptions} [options]
51
+ * @throws {Error} `[CLIPipeProvider] requires command` β€” when options.command is missing.
52
+ */
53
+ constructor(options?: CLIPipeOptions);
54
+ command: string;
55
+ args: string[];
56
+ cwd: string | undefined;
57
+ env: Record<string, string> | undefined;
58
+ timeout: number;
59
+ systemPromptFlag: string | null;
60
+ onChunk: ((chunk: string) => void) | null;
61
+ /**
62
+ * Generate a response by piping messages to the CLI command.
63
+ * @param {Message[]} messages - Conversation messages in OpenAI format.
64
+ * @param {ToolDef[]} [tools=[]] - Unused (CLI commands don't support tools).
65
+ * @param {Record<string, any>} [options={}] - Unused.
66
+ * @returns {Promise<GenerateResult>}
67
+ * @throws {Error} `[CLIPipeProvider] failed to spawn "cmd": ...` β€” when the command cannot be found or executed.
68
+ * @throws {Error} `[CLIPipeProvider] process exited with code N: ...` β€” on non-zero exit.
69
+ * @throws {Error} `[CLIPipeProvider] timed out after Nms` β€” when the process exceeds timeout.
70
+ * @throws {Error} `[CLIPipeProvider] process produced no output` β€” when stdout is empty.
71
+ */
72
+ generate(messages: Message[], tools?: ToolDef[], options?: Record<string, any>): Promise<GenerateResult>;
73
+ /**
74
+ * Convert OpenAI-format messages to a plain text prompt.
75
+ * @param {Message[]} messages
76
+ * @returns {string}
77
+ */
78
+ _formatPrompt(messages: Message[]): string;
79
+ /**
80
+ * Spawn the CLI process, pipe prompt to stdin, collect stdout.
81
+ * @param {string} prompt
82
+ * @param {string[]} [extraArgs=[]] - Additional args appended after this.args.
83
+ * @returns {Promise<string>}
84
+ */
85
+ _spawn(prompt: string, extraArgs?: string[]): Promise<string>;
86
+ }
@@ -3,16 +3,25 @@
3
3
  const { spawn } = require('child_process');
4
4
  const { ProviderError } = require('./errors');
5
5
 
6
+ /** @typedef {import('../types').Message} Message */
7
+ /** @typedef {import('../types').ToolDef} ToolDef */
8
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
9
+
10
+ /**
11
+ * @typedef {object} CLIPipeOptions
12
+ * @property {string} [command] - CLI command to spawn (required).
13
+ * @property {string[]} [args=[]] - Arguments to pass to the command.
14
+ * @property {string} [cwd] - Working directory for the child process.
15
+ * @property {Record<string, string>} [env] - Environment variables for the child process.
16
+ * @property {number} [timeout=30000] - Timeout in milliseconds.
17
+ * @property {string} [systemPromptFlag] - CLI flag for system prompt (e.g. '--system'). When set, system messages are extracted and passed via this flag instead of stdin.
18
+ * @property {(chunk: string) => void} [onChunk] - Called with each stdout chunk as it streams.
19
+ */
20
+
6
21
  class CLIPipeProvider {
7
22
  /**
8
23
  * Provider that pipes prompts to a CLI command via stdin and reads stdout.
9
- * @param {object} options
10
- * @param {string} options.command - CLI command to spawn (required).
11
- * @param {string[]} [options.args=[]] - Arguments to pass to the command.
12
- * @param {string} [options.cwd] - Working directory for the child process.
13
- * @param {object} [options.env] - Environment variables for the child process.
14
- * @param {number} [options.timeout=30000] - Timeout in milliseconds.
15
- * @param {string} [options.systemPromptFlag] - CLI flag for system prompt (e.g. '--system'). When set, system messages are extracted and passed via this flag instead of stdin.
24
+ * @param {CLIPipeOptions} [options]
16
25
  * @throws {Error} `[CLIPipeProvider] requires command` β€” when options.command is missing.
17
26
  */
18
27
  constructor(options = {}) {
@@ -28,16 +37,17 @@ class CLIPipeProvider {
28
37
 
29
38
  /**
30
39
  * Generate a response by piping messages to the CLI command.
31
- * @param {Array<object>} messages - Conversation messages in OpenAI format.
32
- * @param {Array<object>} [tools=[]] - Unused (CLI commands don't support tools).
33
- * @param {object} [options={}] - Unused.
34
- * @returns {Promise<{text: string, toolCalls: Array, usage: object}>}
40
+ * @param {Message[]} messages - Conversation messages in OpenAI format.
41
+ * @param {ToolDef[]} [tools=[]] - Unused (CLI commands don't support tools).
42
+ * @param {Record<string, any>} [options={}] - Unused.
43
+ * @returns {Promise<GenerateResult>}
35
44
  * @throws {Error} `[CLIPipeProvider] failed to spawn "cmd": ...` β€” when the command cannot be found or executed.
36
45
  * @throws {Error} `[CLIPipeProvider] process exited with code N: ...` β€” on non-zero exit.
37
46
  * @throws {Error} `[CLIPipeProvider] timed out after Nms` β€” when the process exceeds timeout.
38
47
  * @throws {Error} `[CLIPipeProvider] process produced no output` β€” when stdout is empty.
39
48
  */
40
49
  async generate(messages, tools = [], options = {}) {
50
+ /** @type {string[]} */
41
51
  let extraArgs = [];
42
52
  let promptMessages = messages;
43
53
 
@@ -61,7 +71,7 @@ class CLIPipeProvider {
61
71
 
62
72
  /**
63
73
  * Convert OpenAI-format messages to a plain text prompt.
64
- * @param {Array<object>} messages
74
+ * @param {Message[]} messages
65
75
  * @returns {string}
66
76
  */
67
77
  _formatPrompt(messages) {
@@ -79,11 +89,11 @@ class CLIPipeProvider {
79
89
  */
80
90
  _spawn(prompt, extraArgs = []) {
81
91
  return new Promise((resolve, reject) => {
82
- const child = spawn(this.command, [...this.args, ...extraArgs], {
92
+ const child = spawn(this.command, [...this.args, ...extraArgs], /** @type {any} */ ({
83
93
  cwd: this.cwd,
84
94
  env: this.env,
85
95
  stdio: ['pipe', 'pipe', 'pipe'],
86
- });
96
+ }));
87
97
 
88
98
  let stdout = '';
89
99
  let stderr = '';
@@ -93,17 +103,17 @@ class CLIPipeProvider {
93
103
  child.stderr.on('data', d => { stderr += d; });
94
104
 
95
105
  child.on('error', err => {
96
- reject(new ProviderError(`[CLIPipeProvider] failed to spawn "${this.command}": ${err.message}`, { status: 0 }));
106
+ reject(new ProviderError(`[CLIPipeProvider] failed to spawn "${this.command}": ${err.message}`, /** @type {any} */ ({ status: 0 })));
97
107
  });
98
108
 
99
109
  child.on('close', code => {
100
110
  if (killed) return; // timeout already rejected
101
111
  if (code !== 0) {
102
- return reject(new ProviderError(`[CLIPipeProvider] process exited with code ${code}: ${stderr.trim()}`, { status: code }));
112
+ return reject(new ProviderError(`[CLIPipeProvider] process exited with code ${code}: ${stderr.trim()}`, /** @type {any} */ ({ status: code })));
103
113
  }
104
114
  const text = stdout.trim();
105
115
  if (!text) {
106
- return reject(new ProviderError('[CLIPipeProvider] process produced no output', { status: 0 }));
116
+ return reject(new ProviderError('[CLIPipeProvider] process produced no output', /** @type {any} */ ({ status: 0 })));
107
117
  }
108
118
  resolve(text);
109
119
  });
@@ -115,7 +125,7 @@ class CLIPipeProvider {
115
125
  setTimeout(() => {
116
126
  try { child.kill('SIGKILL'); } catch (_) {}
117
127
  }, 1000);
118
- reject(new ProviderError(`[CLIPipeProvider] timed out after ${this.timeout}ms`, { status: 0 }));
128
+ reject(new ProviderError(`[CLIPipeProvider] timed out after ${this.timeout}ms`, /** @type {any} */ ({ status: 0 })));
119
129
  }, this.timeout);
120
130
 
121
131
  child.on('close', () => clearTimeout(timer));
@@ -0,0 +1,44 @@
1
+ export type Provider = import("../types").Provider;
2
+ export type Message = import("../types").Message;
3
+ export type ToolDef = import("../types").ToolDef;
4
+ export type GenerateResult = import("../types").GenerateResult;
5
+ export type FallbackOptions = {
6
+ /**
7
+ * - Return false to stop.
8
+ */
9
+ shouldFallback?: ((error: any, index: number) => boolean) | undefined;
10
+ /**
11
+ * - Callback.
12
+ */
13
+ onFallback?: ((error: any, fromIndex: number, toIndex: number) => void) | undefined;
14
+ };
15
+ /** @typedef {import('../types').Provider} Provider */
16
+ /** @typedef {import('../types').Message} Message */
17
+ /** @typedef {import('../types').ToolDef} ToolDef */
18
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
19
+ /**
20
+ * @typedef {object} FallbackOptions
21
+ * @property {(error: any, index: number) => boolean} [shouldFallback] - Return false to stop.
22
+ * @property {(error: any, fromIndex: number, toIndex: number) => void} [onFallback] - Callback.
23
+ */
24
+ export class FallbackProvider {
25
+ /**
26
+ * Provider that tries multiple providers in order.
27
+ * @param {Provider[]} providers - Ordered list of providers with generate().
28
+ * @param {FallbackOptions} [options={}]
29
+ * @throws {Error} `[FallbackProvider] requires at least one provider` β€” when providers is empty.
30
+ */
31
+ constructor(providers: Provider[], options?: FallbackOptions);
32
+ providers: import("../types").Provider[];
33
+ shouldFallback: (error: any, index: number) => boolean;
34
+ onFallback: ((error: any, fromIndex: number, toIndex: number) => void) | null;
35
+ /**
36
+ * Generate using first available provider.
37
+ * @param {Message[]} messages
38
+ * @param {ToolDef[]} [tools=[]]
39
+ * @param {Record<string, any>} [options={}]
40
+ * @returns {Promise<GenerateResult>}
41
+ * @throws {AggregateError} When all providers fail.
42
+ */
43
+ generate(messages: Message[], tools?: ToolDef[], options?: Record<string, any>): Promise<GenerateResult>;
44
+ }
@@ -1,12 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ /** @typedef {import('../types').Provider} Provider */
4
+ /** @typedef {import('../types').Message} Message */
5
+ /** @typedef {import('../types').ToolDef} ToolDef */
6
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
7
+
8
+ /**
9
+ * @typedef {object} FallbackOptions
10
+ * @property {(error: any, index: number) => boolean} [shouldFallback] - Return false to stop.
11
+ * @property {(error: any, fromIndex: number, toIndex: number) => void} [onFallback] - Callback.
12
+ */
13
+
3
14
  class FallbackProvider {
4
15
  /**
5
16
  * Provider that tries multiple providers in order.
6
- * @param {Array<object>} providers - Ordered list of providers with generate().
7
- * @param {object} [options={}]
8
- * @param {function} [options.shouldFallback] - (error, index) => boolean. Return false to stop.
9
- * @param {function} [options.onFallback] - (error, fromIndex, toIndex) callback.
17
+ * @param {Provider[]} providers - Ordered list of providers with generate().
18
+ * @param {FallbackOptions} [options={}]
10
19
  * @throws {Error} `[FallbackProvider] requires at least one provider` β€” when providers is empty.
11
20
  */
12
21
  constructor(providers, options = {}) {
@@ -20,13 +29,14 @@ class FallbackProvider {
20
29
 
21
30
  /**
22
31
  * Generate using first available provider.
23
- * @param {Array<object>} messages
24
- * @param {Array<object>} [tools=[]]
25
- * @param {object} [options={}]
26
- * @returns {Promise<{text: string, toolCalls: Array, usage: object}>}
32
+ * @param {Message[]} messages
33
+ * @param {ToolDef[]} [tools=[]]
34
+ * @param {Record<string, any>} [options={}]
35
+ * @returns {Promise<GenerateResult>}
27
36
  * @throws {AggregateError} When all providers fail.
28
37
  */
29
38
  async generate(messages, tools = [], options = {}) {
39
+ /** @type {any[]} */
30
40
  const errors = [];
31
41
 
32
42
  for (let i = 0; i < this.providers.length; i++) {
@@ -0,0 +1,41 @@
1
+ export type Message = import("../types").Message;
2
+ export type ToolDef = import("../types").ToolDef;
3
+ export type GenerateResult = import("../types").GenerateResult;
4
+ export type OllamaOptions = {
5
+ model?: string | undefined;
6
+ url?: string | undefined;
7
+ exposeErrorBody?: boolean | undefined;
8
+ };
9
+ /** @typedef {import('../types').Message} Message */
10
+ /** @typedef {import('../types').ToolDef} ToolDef */
11
+ /** @typedef {import('../types').GenerateResult} GenerateResult */
12
+ /**
13
+ * @typedef {object} OllamaOptions
14
+ * @property {string} [model='llama3.2']
15
+ * @property {string} [url='http://localhost:11434']
16
+ * @property {boolean} [exposeErrorBody=false]
17
+ */
18
+ export class OllamaProvider {
19
+ /**
20
+ * @param {OllamaOptions} [options]
21
+ */
22
+ constructor(options?: OllamaOptions);
23
+ model: string;
24
+ url: string;
25
+ exposeErrorBody: boolean;
26
+ /**
27
+ * Generate a response from a local Ollama instance.
28
+ * @param {Message[]} messages - Conversation messages.
29
+ * @param {ToolDef[]} [tools=[]] - Tool definitions.
30
+ * @param {Record<string, any>} [options={}] - Options (temperature).
31
+ * @returns {Promise<GenerateResult>}
32
+ * @throws {Error} `[OllamaProvider] ...` β€” on HTTP errors or invalid JSON response.
33
+ */
34
+ generate(messages: Message[], tools?: ToolDef[], options?: Record<string, any>): Promise<GenerateResult>;
35
+ /**
36
+ * @param {string} path
37
+ * @param {Record<string, any>} body
38
+ * @returns {Promise<any>}
39
+ */
40
+ _request(path: string, body: Record<string, any>): Promise<any>;
41
+ }