bare-agent 0.10.4 → 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.
- package/bin/cli.d.ts +4 -0
- package/bin/cli.js +70 -12
- package/bin/test-provider.d.ts +2 -0
- package/bin/test-provider.js +5 -1
- package/index.d.ts +20 -0
- package/package.json +44 -10
- package/src/bareguard-adapter.d.ts +118 -0
- package/src/bareguard-adapter.js +75 -3
- package/src/checkpoint.d.ts +61 -0
- package/src/checkpoint.js +17 -8
- package/src/circuit-breaker.d.ts +70 -0
- package/src/circuit-breaker.js +20 -4
- package/src/errors.d.ts +106 -0
- package/src/errors.js +50 -1
- package/src/loop.d.ts +135 -0
- package/src/loop.js +80 -18
- package/src/mcp-bridge.d.ts +133 -0
- package/src/mcp-bridge.js +199 -26
- package/src/mcp.d.ts +4 -0
- package/src/memory.d.ts +50 -0
- package/src/memory.js +22 -2
- package/src/planner.d.ts +62 -0
- package/src/planner.js +26 -7
- package/src/provider-anthropic.d.ts +55 -0
- package/src/provider-anthropic.js +34 -10
- package/src/provider-clipipe.d.ts +86 -0
- package/src/provider-clipipe.js +28 -18
- package/src/provider-fallback.d.ts +44 -0
- package/src/provider-fallback.js +18 -8
- package/src/provider-ollama.d.ts +41 -0
- package/src/provider-ollama.js +29 -7
- package/src/provider-openai.d.ts +57 -0
- package/src/provider-openai.js +34 -7
- package/src/providers.d.ts +6 -0
- package/src/retry.d.ts +44 -0
- package/src/retry.js +15 -1
- package/src/run-plan.d.ts +126 -0
- package/src/run-plan.js +46 -13
- package/src/scheduler.d.ts +102 -0
- package/src/scheduler.js +32 -4
- package/src/state.d.ts +45 -0
- package/src/state.js +18 -2
- package/src/store-jsonfile.d.ts +85 -0
- package/src/store-jsonfile.js +50 -8
- package/src/store-sqlite.d.ts +90 -0
- package/src/store-sqlite.js +31 -7
- package/src/stores.d.ts +3 -0
- package/src/stream.d.ts +79 -0
- package/src/stream.js +32 -0
- package/src/tools.d.ts +8 -0
- package/src/transport-jsonl.d.ts +30 -0
- package/src/transport-jsonl.js +13 -0
- package/src/transports.d.ts +2 -0
- package/tools/browse.d.ts +10 -0
- package/tools/browse.js +2 -0
- package/tools/defer.d.ts +33 -0
- package/tools/defer.js +12 -3
- package/tools/mobile.d.ts +34 -0
- package/tools/mobile.js +28 -15
- package/tools/shell.d.ts +31 -0
- package/tools/shell.js +83 -6
- package/tools/spawn.d.ts +107 -0
- package/tools/spawn.js +24 -5
- package/types/index.d.ts +66 -0
- package/types/shims.d.ts +16 -0
package/src/checkpoint.js
CHANGED
|
@@ -4,16 +4,20 @@ const { TimeoutError } = require('./errors');
|
|
|
4
4
|
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} CheckpointOptions
|
|
9
|
+
* @property {Array<string>} [tools] - Tool names that require approval (exact match).
|
|
10
|
+
* @property {(toolName: string, args: any) => boolean} [shouldAsk] - Custom predicate — overrides tools list if set.
|
|
11
|
+
* @property {(question: string, context: any) => any} [send] - Async `(question, context) => void` to deliver the question.
|
|
12
|
+
* @property {(context: any) => any} [waitForReply] - Async `(context) => string` that resolves with the user's reply.
|
|
13
|
+
* @property {number} [timeout=300000] - Ms to wait before auto-denying. 0 disables.
|
|
14
|
+
*/
|
|
15
|
+
|
|
7
16
|
class Checkpoint {
|
|
8
17
|
/**
|
|
9
|
-
* @param {
|
|
10
|
-
* @param {Array<string>} [options.tools] - Tool names that require approval (exact match).
|
|
11
|
-
* @param {Function} [options.shouldAsk] - Custom predicate `(toolName, args) => bool` — overrides tools list if set.
|
|
12
|
-
* @param {Function} options.send - Async `(question, context) => void` to deliver the question.
|
|
13
|
-
* @param {Function} options.waitForReply - Async `(context) => string` that resolves with the user's reply.
|
|
14
|
-
* @param {number} [options.timeout=300000] - Ms to wait before auto-denying. 0 disables.
|
|
18
|
+
* @param {CheckpointOptions} [options={}]
|
|
15
19
|
*/
|
|
16
|
-
constructor(options = {}) {
|
|
20
|
+
constructor(options = /** @type {CheckpointOptions} */ ({})) {
|
|
17
21
|
this.tools = new Set(options.tools || []);
|
|
18
22
|
this.send = options.send || null;
|
|
19
23
|
this.waitForReply = options.waitForReply || null;
|
|
@@ -21,6 +25,11 @@ class Checkpoint {
|
|
|
21
25
|
this.timeout = options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS;
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} toolName - Name of the tool being invoked.
|
|
30
|
+
* @param {any} args - Arguments passed to the tool.
|
|
31
|
+
* @returns {boolean} Whether approval should be requested.
|
|
32
|
+
*/
|
|
24
33
|
shouldAsk(toolName, args) {
|
|
25
34
|
if (this.shouldAskFn) return this.shouldAskFn(toolName, args);
|
|
26
35
|
return this.tools.has(toolName);
|
|
@@ -31,7 +40,7 @@ class Checkpoint {
|
|
|
31
40
|
* without a reply — the Loop catches this, auto-denies the tool call, and routes the
|
|
32
41
|
* error through loop:error + onError. No silent hangs.
|
|
33
42
|
* @param {string} question - The approval question to send.
|
|
34
|
-
* @param {
|
|
43
|
+
* @param {{tool?: string, [key: string]: any}} [context={}] - Context passed to send and waitForReply.
|
|
35
44
|
* @returns {Promise<string|null>} The user's reply, or null.
|
|
36
45
|
* @throws {Error} `[Checkpoint] send and waitForReply callbacks required` — when callbacks are missing.
|
|
37
46
|
* @throws {TimeoutError} When no reply arrives within `timeout` ms.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export type CircuitState = "closed" | "open" | "half-open";
|
|
2
|
+
export type CircuitEntry = {
|
|
3
|
+
state: CircuitState;
|
|
4
|
+
failures: number;
|
|
5
|
+
openedAt: number;
|
|
6
|
+
generation: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {'closed'|'open'|'half-open'} CircuitState
|
|
10
|
+
* @typedef {{ state: CircuitState, failures: number, openedAt: number, generation: number }} CircuitEntry
|
|
11
|
+
*/
|
|
12
|
+
export class CircuitBreaker {
|
|
13
|
+
/**
|
|
14
|
+
* @param {object} [options={}]
|
|
15
|
+
* @param {number} [options.threshold=5] - Failures before opening.
|
|
16
|
+
* @param {number} [options.resetAfter=60000] - Ms before half-open probe.
|
|
17
|
+
* @param {((key: string, from: CircuitState, to: CircuitState) => void)} [options.onStateChange] - Callback(key, from, to).
|
|
18
|
+
*/
|
|
19
|
+
constructor(options?: {
|
|
20
|
+
threshold?: number | undefined;
|
|
21
|
+
resetAfter?: number | undefined;
|
|
22
|
+
onStateChange?: ((key: string, from: CircuitState, to: CircuitState) => void) | undefined;
|
|
23
|
+
});
|
|
24
|
+
threshold: number;
|
|
25
|
+
resetAfter: number;
|
|
26
|
+
onStateChange: ((key: string, from: CircuitState, to: CircuitState) => void) | null;
|
|
27
|
+
/** @type {Map<string, CircuitEntry>} */
|
|
28
|
+
_keys: Map<string, CircuitEntry>;
|
|
29
|
+
/**
|
|
30
|
+
* @param {string} key
|
|
31
|
+
* @returns {CircuitEntry}
|
|
32
|
+
*/
|
|
33
|
+
_getEntry(key: string): CircuitEntry;
|
|
34
|
+
/**
|
|
35
|
+
* @param {CircuitEntry} entry
|
|
36
|
+
* @param {string} key
|
|
37
|
+
* @param {CircuitState} newState
|
|
38
|
+
*/
|
|
39
|
+
_setState(entry: CircuitEntry, key: string, newState: CircuitState): void;
|
|
40
|
+
/**
|
|
41
|
+
* Execute fn through the circuit breaker.
|
|
42
|
+
* @param {function} fn - Async function to call.
|
|
43
|
+
* @param {string} [key='default'] - Circuit key for per-key isolation.
|
|
44
|
+
* @returns {Promise<*>}
|
|
45
|
+
* @throws {CircuitOpenError} When circuit is open.
|
|
46
|
+
*/
|
|
47
|
+
call(fn: Function, key?: string): Promise<any>;
|
|
48
|
+
/**
|
|
49
|
+
* Get current state for a key.
|
|
50
|
+
* @param {string} [key='default']
|
|
51
|
+
* @returns {'closed'|'open'|'half-open'}
|
|
52
|
+
*/
|
|
53
|
+
getState(key?: string): "closed" | "open" | "half-open";
|
|
54
|
+
/**
|
|
55
|
+
* Force reset a key to closed.
|
|
56
|
+
* @param {string} [key='default']
|
|
57
|
+
*/
|
|
58
|
+
reset(key?: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Wrap a provider so generate() goes through the circuit breaker.
|
|
61
|
+
* @param {{ generate: (...args: any[]) => Promise<any> }} provider - Provider with generate().
|
|
62
|
+
* @param {string} [key] - Circuit key.
|
|
63
|
+
* @returns {{ generate: (...args: any[]) => Promise<any> }} Wrapped provider with generate().
|
|
64
|
+
*/
|
|
65
|
+
wrapProvider(provider: {
|
|
66
|
+
generate: (...args: any[]) => Promise<any>;
|
|
67
|
+
}, key?: string): {
|
|
68
|
+
generate: (...args: any[]) => Promise<any>;
|
|
69
|
+
};
|
|
70
|
+
}
|
package/src/circuit-breaker.js
CHANGED
|
@@ -2,27 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
const { CircuitOpenError } = require('./errors');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {'closed'|'open'|'half-open'} CircuitState
|
|
7
|
+
* @typedef {{ state: CircuitState, failures: number, openedAt: number, generation: number }} CircuitEntry
|
|
8
|
+
*/
|
|
9
|
+
|
|
5
10
|
class CircuitBreaker {
|
|
6
11
|
/**
|
|
7
12
|
* @param {object} [options={}]
|
|
8
13
|
* @param {number} [options.threshold=5] - Failures before opening.
|
|
9
14
|
* @param {number} [options.resetAfter=60000] - Ms before half-open probe.
|
|
10
|
-
* @param {
|
|
15
|
+
* @param {((key: string, from: CircuitState, to: CircuitState) => void)} [options.onStateChange] - Callback(key, from, to).
|
|
11
16
|
*/
|
|
12
17
|
constructor(options = {}) {
|
|
13
18
|
this.threshold = options.threshold || 5;
|
|
14
19
|
this.resetAfter = options.resetAfter || 60000;
|
|
15
20
|
this.onStateChange = options.onStateChange || null;
|
|
21
|
+
/** @type {Map<string, CircuitEntry>} */
|
|
16
22
|
this._keys = new Map();
|
|
17
23
|
}
|
|
18
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} key
|
|
27
|
+
* @returns {CircuitEntry}
|
|
28
|
+
*/
|
|
19
29
|
_getEntry(key) {
|
|
20
30
|
if (!this._keys.has(key)) {
|
|
21
31
|
this._keys.set(key, { state: 'closed', failures: 0, openedAt: 0, generation: 0 });
|
|
22
32
|
}
|
|
23
|
-
return this._keys.get(key);
|
|
33
|
+
return /** @type {CircuitEntry} */ (this._keys.get(key));
|
|
24
34
|
}
|
|
25
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @param {CircuitEntry} entry
|
|
38
|
+
* @param {string} key
|
|
39
|
+
* @param {CircuitState} newState
|
|
40
|
+
*/
|
|
26
41
|
_setState(entry, key, newState) {
|
|
27
42
|
const from = entry.state;
|
|
28
43
|
if (from === newState) return;
|
|
@@ -98,12 +113,13 @@ class CircuitBreaker {
|
|
|
98
113
|
|
|
99
114
|
/**
|
|
100
115
|
* Wrap a provider so generate() goes through the circuit breaker.
|
|
101
|
-
* @param {
|
|
116
|
+
* @param {{ generate: (...args: any[]) => Promise<any> }} provider - Provider with generate().
|
|
102
117
|
* @param {string} [key] - Circuit key.
|
|
103
|
-
* @returns {
|
|
118
|
+
* @returns {{ generate: (...args: any[]) => Promise<any> }} Wrapped provider with generate().
|
|
104
119
|
*/
|
|
105
120
|
wrapProvider(provider, key) {
|
|
106
121
|
return {
|
|
122
|
+
/** @param {...any} args */
|
|
107
123
|
generate: (...args) => this.call(() => provider.generate(...args), key),
|
|
108
124
|
};
|
|
109
125
|
}
|
package/src/errors.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export type BareAgentErrorOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* - Stable error code (e.g. 'PROVIDER_ERROR').
|
|
4
|
+
*/
|
|
5
|
+
code?: string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* - Whether the operation may be safely retried.
|
|
8
|
+
*/
|
|
9
|
+
retryable?: boolean | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* - Arbitrary structured context.
|
|
12
|
+
*/
|
|
13
|
+
context?: Record<string, any> | undefined;
|
|
14
|
+
};
|
|
15
|
+
export type ProviderErrorOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* - HTTP status from the provider.
|
|
18
|
+
*/
|
|
19
|
+
status?: number | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* - Raw response body.
|
|
22
|
+
*/
|
|
23
|
+
body?: any;
|
|
24
|
+
/**
|
|
25
|
+
* - Arbitrary structured context.
|
|
26
|
+
*/
|
|
27
|
+
context?: Record<string, any> | undefined;
|
|
28
|
+
};
|
|
29
|
+
export type HaltErrorOptions = {
|
|
30
|
+
/**
|
|
31
|
+
* - The bareguard rule that triggered the halt.
|
|
32
|
+
*/
|
|
33
|
+
rule?: string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* - The full bareguard decision object.
|
|
36
|
+
*/
|
|
37
|
+
decision?: any;
|
|
38
|
+
/**
|
|
39
|
+
* - Arbitrary structured context.
|
|
40
|
+
*/
|
|
41
|
+
context?: Record<string, any> | undefined;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {object} BareAgentErrorOptions
|
|
45
|
+
* @property {string} [code] - Stable error code (e.g. 'PROVIDER_ERROR').
|
|
46
|
+
* @property {boolean} [retryable] - Whether the operation may be safely retried.
|
|
47
|
+
* @property {Record<string, any>} [context] - Arbitrary structured context.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {object} ProviderErrorOptions
|
|
51
|
+
* @property {number} [status] - HTTP status from the provider.
|
|
52
|
+
* @property {any} [body] - Raw response body.
|
|
53
|
+
* @property {Record<string, any>} [context] - Arbitrary structured context.
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* @typedef {object} HaltErrorOptions
|
|
57
|
+
* @property {string} [rule] - The bareguard rule that triggered the halt.
|
|
58
|
+
* @property {any} [decision] - The full bareguard decision object.
|
|
59
|
+
* @property {Record<string, any>} [context] - Arbitrary structured context.
|
|
60
|
+
*/
|
|
61
|
+
export class BareAgentError extends Error {
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} message
|
|
64
|
+
* @param {BareAgentErrorOptions} [options]
|
|
65
|
+
*/
|
|
66
|
+
constructor(message: string, { code, retryable, context }?: BareAgentErrorOptions);
|
|
67
|
+
code: string | undefined;
|
|
68
|
+
retryable: boolean;
|
|
69
|
+
context: Record<string, any>;
|
|
70
|
+
}
|
|
71
|
+
export class ProviderError extends BareAgentError {
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} message
|
|
74
|
+
* @param {ProviderErrorOptions} [options]
|
|
75
|
+
*/
|
|
76
|
+
constructor(message: string, { status, body, context }?: ProviderErrorOptions);
|
|
77
|
+
status: number | undefined;
|
|
78
|
+
body: any;
|
|
79
|
+
}
|
|
80
|
+
export class ToolError extends BareAgentError {
|
|
81
|
+
}
|
|
82
|
+
export class TimeoutError extends BareAgentError {
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} [message]
|
|
85
|
+
* @param {BareAgentErrorOptions} [opts]
|
|
86
|
+
*/
|
|
87
|
+
constructor(message?: string, opts?: BareAgentErrorOptions);
|
|
88
|
+
}
|
|
89
|
+
export class ValidationError extends BareAgentError {
|
|
90
|
+
}
|
|
91
|
+
export class CircuitOpenError extends BareAgentError {
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} [message]
|
|
94
|
+
* @param {BareAgentErrorOptions} [opts]
|
|
95
|
+
*/
|
|
96
|
+
constructor(message?: string, opts?: BareAgentErrorOptions);
|
|
97
|
+
}
|
|
98
|
+
export class HaltError extends BareAgentError {
|
|
99
|
+
/**
|
|
100
|
+
* @param {string} [message]
|
|
101
|
+
* @param {HaltErrorOptions} [options]
|
|
102
|
+
*/
|
|
103
|
+
constructor(message?: string, { rule, decision, context }?: HaltErrorOptions);
|
|
104
|
+
rule: string | null;
|
|
105
|
+
decision: any;
|
|
106
|
+
}
|
package/src/errors.js
CHANGED
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {object} BareAgentErrorOptions
|
|
5
|
+
* @property {string} [code] - Stable error code (e.g. 'PROVIDER_ERROR').
|
|
6
|
+
* @property {boolean} [retryable] - Whether the operation may be safely retried.
|
|
7
|
+
* @property {Record<string, any>} [context] - Arbitrary structured context.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} ProviderErrorOptions
|
|
12
|
+
* @property {number} [status] - HTTP status from the provider.
|
|
13
|
+
* @property {any} [body] - Raw response body.
|
|
14
|
+
* @property {Record<string, any>} [context] - Arbitrary structured context.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {object} HaltErrorOptions
|
|
19
|
+
* @property {string} [rule] - The bareguard rule that triggered the halt.
|
|
20
|
+
* @property {any} [decision] - The full bareguard decision object.
|
|
21
|
+
* @property {Record<string, any>} [context] - Arbitrary structured context.
|
|
22
|
+
*/
|
|
23
|
+
|
|
3
24
|
class BareAgentError extends Error {
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} message
|
|
27
|
+
* @param {BareAgentErrorOptions} [options]
|
|
28
|
+
*/
|
|
4
29
|
constructor(message, { code, retryable = false, context = {} } = {}) {
|
|
5
30
|
super(message);
|
|
6
31
|
this.name = this.constructor.name;
|
|
@@ -11,8 +36,12 @@ class BareAgentError extends Error {
|
|
|
11
36
|
}
|
|
12
37
|
|
|
13
38
|
class ProviderError extends BareAgentError {
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} message
|
|
41
|
+
* @param {ProviderErrorOptions} [options]
|
|
42
|
+
*/
|
|
14
43
|
constructor(message, { status, body, context = {} } = {}) {
|
|
15
|
-
const retryable = status === 429 || (status >= 500 && status <= 504);
|
|
44
|
+
const retryable = status === 429 || (status != null && status >= 500 && status <= 504);
|
|
16
45
|
super(message, { code: 'PROVIDER_ERROR', retryable, context });
|
|
17
46
|
this.status = status;
|
|
18
47
|
this.body = body;
|
|
@@ -20,24 +49,40 @@ class ProviderError extends BareAgentError {
|
|
|
20
49
|
}
|
|
21
50
|
|
|
22
51
|
class ToolError extends BareAgentError {
|
|
52
|
+
/**
|
|
53
|
+
* @param {string} message
|
|
54
|
+
* @param {BareAgentErrorOptions} [opts]
|
|
55
|
+
*/
|
|
23
56
|
constructor(message, opts = {}) {
|
|
24
57
|
super(message, { code: 'TOOL_ERROR', retryable: false, ...opts });
|
|
25
58
|
}
|
|
26
59
|
}
|
|
27
60
|
|
|
28
61
|
class TimeoutError extends BareAgentError {
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} [message]
|
|
64
|
+
* @param {BareAgentErrorOptions} [opts]
|
|
65
|
+
*/
|
|
29
66
|
constructor(message, opts = {}) {
|
|
30
67
|
super(message || 'Operation timed out', { code: 'ETIMEDOUT', retryable: true, ...opts });
|
|
31
68
|
}
|
|
32
69
|
}
|
|
33
70
|
|
|
34
71
|
class ValidationError extends BareAgentError {
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} message
|
|
74
|
+
* @param {BareAgentErrorOptions} [opts]
|
|
75
|
+
*/
|
|
35
76
|
constructor(message, opts = {}) {
|
|
36
77
|
super(message, { code: 'VALIDATION_ERROR', retryable: false, ...opts });
|
|
37
78
|
}
|
|
38
79
|
}
|
|
39
80
|
|
|
40
81
|
class CircuitOpenError extends BareAgentError {
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} [message]
|
|
84
|
+
* @param {BareAgentErrorOptions} [opts]
|
|
85
|
+
*/
|
|
41
86
|
constructor(message, opts = {}) {
|
|
42
87
|
super(message || 'Circuit breaker is open', { code: 'CIRCUIT_OPEN', retryable: true, ...opts });
|
|
43
88
|
}
|
|
@@ -48,6 +93,10 @@ class CircuitOpenError extends BareAgentError {
|
|
|
48
93
|
// Loop's outer handler — does NOT propagate to the LLM as a tool result.
|
|
49
94
|
// Loop exits cleanly: emits loop:error{source:'halt'} + loop:done, calls onError.
|
|
50
95
|
class HaltError extends BareAgentError {
|
|
96
|
+
/**
|
|
97
|
+
* @param {string} [message]
|
|
98
|
+
* @param {HaltErrorOptions} [options]
|
|
99
|
+
*/
|
|
51
100
|
constructor(message, { rule, decision, context = {} } = {}) {
|
|
52
101
|
super(message || `[HALT: ${rule || 'unknown'}]`, {
|
|
53
102
|
code: 'HALT',
|
package/src/loop.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export type Provider = import("../types").Provider;
|
|
2
|
+
export type Message = import("../types").Message;
|
|
3
|
+
export type ToolDef = import("../types").ToolDef;
|
|
4
|
+
export type ToolCall = import("../types").ToolCall;
|
|
5
|
+
export type Usage = import("../types").Usage;
|
|
6
|
+
export type GenerateResult = import("../types").GenerateResult;
|
|
7
|
+
export type Store = import("../types").Store;
|
|
8
|
+
export type Checkpoint = import("./checkpoint").Checkpoint;
|
|
9
|
+
export type Retry = import("./retry").Retry;
|
|
10
|
+
export type Stream = import("./stream").Stream;
|
|
11
|
+
export type LoopOptions = {
|
|
12
|
+
provider: Provider;
|
|
13
|
+
system?: string | undefined;
|
|
14
|
+
checkpoint?: import("./checkpoint").Checkpoint | undefined;
|
|
15
|
+
retry?: import("./retry").Retry | undefined;
|
|
16
|
+
stream?: import("./stream").Stream | undefined;
|
|
17
|
+
store?: import("../types").Store | undefined;
|
|
18
|
+
onToolCall?: Function | undefined;
|
|
19
|
+
onText?: Function | undefined;
|
|
20
|
+
onError?: Function | undefined;
|
|
21
|
+
throwOnError?: boolean | undefined;
|
|
22
|
+
policy?: Function | undefined;
|
|
23
|
+
onLlmResult?: Function | undefined;
|
|
24
|
+
onToolResult?: Function | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* - Removed in v0.8; presence throws a migration error.
|
|
27
|
+
*/
|
|
28
|
+
maxRounds?: number | undefined;
|
|
29
|
+
};
|
|
30
|
+
export class Loop {
|
|
31
|
+
/**
|
|
32
|
+
* `policy` is async `(toolName, args, ctx) => true | string`. Recommended wiring: a closure
|
|
33
|
+
* that delegates to a bareguard Gate (`require('bare-agent/bareguard').wireGate(gate).policy`).
|
|
34
|
+
* Anything other than `true` denies; a string is fed to the LLM verbatim as the deny reason.
|
|
35
|
+
* A throw of `HaltError` exits the loop cleanly. `onLlmResult`/`onToolResult` forward usage and
|
|
36
|
+
* tool outcomes to `gate.record` (via wireGate) and never kill the loop on error.
|
|
37
|
+
* @param {LoopOptions} options
|
|
38
|
+
* @throws {Error} `[Loop] requires a provider` — when options.provider is missing.
|
|
39
|
+
*/
|
|
40
|
+
constructor(options?: LoopOptions);
|
|
41
|
+
provider: import("../types").Provider;
|
|
42
|
+
system: string | null;
|
|
43
|
+
checkpoint: import("./checkpoint").Checkpoint | null;
|
|
44
|
+
retry: import("./retry").Retry | null;
|
|
45
|
+
stream: import("./stream").Stream | null;
|
|
46
|
+
onToolCall: Function | null;
|
|
47
|
+
onText: Function | null;
|
|
48
|
+
onError: Function | null;
|
|
49
|
+
throwOnError: boolean;
|
|
50
|
+
store: import("../types").Store | null;
|
|
51
|
+
policy: Function | null;
|
|
52
|
+
onLlmResult: Function | null;
|
|
53
|
+
onToolResult: Function | null;
|
|
54
|
+
_stopped: boolean;
|
|
55
|
+
/** @type {Message[]} */
|
|
56
|
+
_history: Message[];
|
|
57
|
+
/**
|
|
58
|
+
* @param {string} source
|
|
59
|
+
* @param {any} err
|
|
60
|
+
* @param {Record<string, any>} [extra]
|
|
61
|
+
*/
|
|
62
|
+
_reportError(source: string, err: any, extra?: Record<string, any>): void;
|
|
63
|
+
/** @param {{type: string, data?: any, ts?: string}} event */
|
|
64
|
+
_safeEmit(event: {
|
|
65
|
+
type: string;
|
|
66
|
+
data?: any;
|
|
67
|
+
ts?: string;
|
|
68
|
+
}): void;
|
|
69
|
+
/**
|
|
70
|
+
* @param {string} name
|
|
71
|
+
* @param {Function|null} fn
|
|
72
|
+
* @param {...any} args
|
|
73
|
+
*/
|
|
74
|
+
_safeCall(name: string, fn: Function | null, ...args: any[]): void;
|
|
75
|
+
/**
|
|
76
|
+
* Run the think/act/observe loop.
|
|
77
|
+
* @param {Message[]} messages - Conversation messages in OpenAI format.
|
|
78
|
+
* @param {ToolDef[]} [tools=[]] - Tool definitions with name, execute, description, parameters.
|
|
79
|
+
* @param {Record<string, any>} [options={}] - Per-run overrides (system, temperature, ctx, etc.).
|
|
80
|
+
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, msgs: Message[]}>}
|
|
81
|
+
* On halt the returned `error` is `halt:<rule>` (or `halt:unknown` if the
|
|
82
|
+
* thrown HaltError carried no `rule`), and `msgs` is sanitized so any
|
|
83
|
+
* dangling assistant `tool_calls` from the halted round are paired with
|
|
84
|
+
* synthetic `[halted]` tool replies — safe to feed back into another
|
|
85
|
+
* provider call without violating OpenAI's tool-call/tool-result pairing.
|
|
86
|
+
* @throws {Error} `[Loop] Tool is missing a name` — when a tool has no name or a non-string name.
|
|
87
|
+
* @throws {Error} `[Loop] Tool "X" is missing an execute() function` — when execute is not a function.
|
|
88
|
+
* @throws {Error} `[Loop] Tool "X" has invalid parameters` — when parameters is not an object.
|
|
89
|
+
*/
|
|
90
|
+
run(messages: Message[], tools?: ToolDef[], options?: Record<string, any>): Promise<{
|
|
91
|
+
text: string;
|
|
92
|
+
toolCalls: ToolCall[];
|
|
93
|
+
usage: Usage;
|
|
94
|
+
cost: number;
|
|
95
|
+
error: string | null;
|
|
96
|
+
msgs: Message[];
|
|
97
|
+
}>;
|
|
98
|
+
/**
|
|
99
|
+
* Health check — validates provider, store, and tools without throwing.
|
|
100
|
+
* @param {ToolDef[]} [tools=[]] - Tool definitions to validate.
|
|
101
|
+
* @returns {Promise<{provider: {ok: boolean, error?: string}, store: {ok: boolean, error?: string, skipped: boolean}, tools: {ok: boolean, errors?: string[]}}>}
|
|
102
|
+
* Never throws — all failures captured in return value.
|
|
103
|
+
*/
|
|
104
|
+
validate(tools?: ToolDef[]): Promise<{
|
|
105
|
+
provider: {
|
|
106
|
+
ok: boolean;
|
|
107
|
+
error?: string;
|
|
108
|
+
};
|
|
109
|
+
store: {
|
|
110
|
+
ok: boolean;
|
|
111
|
+
error?: string;
|
|
112
|
+
skipped: boolean;
|
|
113
|
+
};
|
|
114
|
+
tools: {
|
|
115
|
+
ok: boolean;
|
|
116
|
+
errors?: string[];
|
|
117
|
+
};
|
|
118
|
+
}>;
|
|
119
|
+
/**
|
|
120
|
+
* Stateful single-turn chat that maintains conversation history across calls.
|
|
121
|
+
* @param {string} text - User message.
|
|
122
|
+
* @param {ToolDef[]} [tools=[]] - Tool definitions.
|
|
123
|
+
* @param {Record<string, any>} [options={}] - Per-run overrides.
|
|
124
|
+
* @returns {Promise<{text: string, toolCalls: ToolCall[], usage: Usage, cost: number, error: string|null, msgs: Message[]}>}
|
|
125
|
+
*/
|
|
126
|
+
chat(text: string, tools?: ToolDef[], options?: Record<string, any>): Promise<{
|
|
127
|
+
text: string;
|
|
128
|
+
toolCalls: ToolCall[];
|
|
129
|
+
usage: Usage;
|
|
130
|
+
cost: number;
|
|
131
|
+
error: string | null;
|
|
132
|
+
msgs: Message[];
|
|
133
|
+
}>;
|
|
134
|
+
stop(): void;
|
|
135
|
+
}
|