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/state.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { readFileSync, writeFileSync } = require('fs');
|
|
4
4
|
const { EventEmitter } = require('events');
|
|
5
5
|
|
|
6
|
+
/** @type {Record<string, Record<string, string>>} */
|
|
6
7
|
const TRANSITIONS = {
|
|
7
8
|
pending: { start: 'running', cancel: 'cancelled' },
|
|
8
9
|
running: { complete: 'done', fail: 'failed', pause: 'waiting_for_input', cancel: 'cancelled' },
|
|
@@ -11,10 +12,20 @@ const TRANSITIONS = {
|
|
|
11
12
|
// done and cancelled are terminal
|
|
12
13
|
};
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} Task
|
|
17
|
+
* @property {string} status
|
|
18
|
+
* @property {*} data
|
|
19
|
+
* @property {*} error
|
|
20
|
+
* @property {string} updatedAt
|
|
21
|
+
*/
|
|
22
|
+
|
|
14
23
|
class StateMachine extends EventEmitter {
|
|
24
|
+
/** @param {{ file?: string|null }} [options={}] */
|
|
15
25
|
constructor(options = {}) {
|
|
16
26
|
super();
|
|
17
27
|
this.file = options.file || null;
|
|
28
|
+
/** @type {Map<string, Task>} */
|
|
18
29
|
this.tasks = new Map();
|
|
19
30
|
if (this.file) this._load();
|
|
20
31
|
}
|
|
@@ -51,35 +62,40 @@ class StateMachine extends EventEmitter {
|
|
|
51
62
|
return task.status;
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
/** @param {string} taskId */
|
|
54
66
|
getStatus(taskId) {
|
|
55
67
|
return this.tasks.get(taskId) || null;
|
|
56
68
|
}
|
|
57
69
|
|
|
70
|
+
/** @param {(event: { taskId: string, from: string, to: string, event: string, data: * }) => void} callback */
|
|
58
71
|
onTransition(callback) {
|
|
59
72
|
this.on('transition', callback);
|
|
60
73
|
return () => this.off('transition', callback);
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
getAll() {
|
|
77
|
+
/** @type {Record<string, Task>} */
|
|
64
78
|
const result = {};
|
|
65
79
|
for (const [id, task] of this.tasks) result[id] = { ...task };
|
|
66
80
|
return result;
|
|
67
81
|
}
|
|
68
82
|
|
|
69
83
|
_load() {
|
|
84
|
+
if (!this.file) return;
|
|
70
85
|
try {
|
|
71
86
|
const raw = readFileSync(this.file, 'utf8');
|
|
72
87
|
const data = JSON.parse(raw);
|
|
73
|
-
for (const [id, task] of Object.entries(data)) this.tasks.set(id, task);
|
|
88
|
+
for (const [id, task] of Object.entries(data)) this.tasks.set(id, /** @type {Task} */ (task));
|
|
74
89
|
} catch (e) {
|
|
75
90
|
if (e.code !== 'ENOENT') throw e;
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
_save() {
|
|
95
|
+
/** @type {Record<string, Task>} */
|
|
80
96
|
const obj = {};
|
|
81
97
|
for (const [id, task] of this.tasks) obj[id] = task;
|
|
82
|
-
writeFileSync(this.file, JSON.stringify(obj, null, 2) + '\n');
|
|
98
|
+
if (this.file) writeFileSync(this.file, JSON.stringify(obj, null, 2) + '\n');
|
|
83
99
|
}
|
|
84
100
|
}
|
|
85
101
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON file memory store. Zero deps, case-insensitive substring search.
|
|
3
|
+
*
|
|
4
|
+
* SCALING: intended for small memory sets. `search()` is an O(n) substring scan
|
|
5
|
+
* (no index), and every `store()`/`delete()` re-serializes and rewrites the entire
|
|
6
|
+
* file synchronously. Fine for hundreds–low-thousands of entries; for larger or
|
|
7
|
+
* write-heavy memory use SQLiteStore (FTS5 index, parameterized, incremental writes).
|
|
8
|
+
*
|
|
9
|
+
* Interface (implements Memory store contract):
|
|
10
|
+
* store(content, metadata) → id
|
|
11
|
+
* search(query, options) → [{ id, content, metadata, score }]
|
|
12
|
+
* get(id) → { content, metadata }
|
|
13
|
+
* delete(id) → void
|
|
14
|
+
*/
|
|
15
|
+
export type JsonRecord = {
|
|
16
|
+
id: number;
|
|
17
|
+
content: any;
|
|
18
|
+
metadata: Record<string, any>;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* JSON file memory store. Zero deps, case-insensitive substring search.
|
|
23
|
+
*
|
|
24
|
+
* SCALING: intended for small memory sets. `search()` is an O(n) substring scan
|
|
25
|
+
* (no index), and every `store()`/`delete()` re-serializes and rewrites the entire
|
|
26
|
+
* file synchronously. Fine for hundreds–low-thousands of entries; for larger or
|
|
27
|
+
* write-heavy memory use SQLiteStore (FTS5 index, parameterized, incremental writes).
|
|
28
|
+
*
|
|
29
|
+
* Interface (implements Memory store contract):
|
|
30
|
+
* store(content, metadata) → id
|
|
31
|
+
* search(query, options) → [{ id, content, metadata, score }]
|
|
32
|
+
* get(id) → { content, metadata }
|
|
33
|
+
* delete(id) → void
|
|
34
|
+
*
|
|
35
|
+
* @typedef {object} JsonRecord
|
|
36
|
+
* @property {number} id
|
|
37
|
+
* @property {any} content
|
|
38
|
+
* @property {Record<string, any>} metadata
|
|
39
|
+
* @property {string} createdAt
|
|
40
|
+
*/
|
|
41
|
+
export class JsonFileStore {
|
|
42
|
+
/**
|
|
43
|
+
* @param {{ path?: string }} [options]
|
|
44
|
+
* @throws {Error} `[JsonFileStore] requires options.path` — when path is missing.
|
|
45
|
+
*/
|
|
46
|
+
constructor(options?: {
|
|
47
|
+
path?: string;
|
|
48
|
+
});
|
|
49
|
+
_path: string;
|
|
50
|
+
/** @type {JsonRecord[]} */
|
|
51
|
+
_data: JsonRecord[];
|
|
52
|
+
_nextId: number;
|
|
53
|
+
_warnedLarge: boolean;
|
|
54
|
+
_save(): void;
|
|
55
|
+
/**
|
|
56
|
+
* @param {any} content
|
|
57
|
+
* @param {Record<string, any>} [metadata]
|
|
58
|
+
* @returns {number} id
|
|
59
|
+
*/
|
|
60
|
+
store(content: any, metadata?: Record<string, any>): number;
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} query
|
|
63
|
+
* @param {{ limit?: number }} [options]
|
|
64
|
+
* @returns {Array<JsonRecord & { score: number }>}
|
|
65
|
+
*/
|
|
66
|
+
search(query: string, options?: {
|
|
67
|
+
limit?: number;
|
|
68
|
+
}): Array<JsonRecord & {
|
|
69
|
+
score: number;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* @param {number} id
|
|
73
|
+
* @returns {{ id: number, content: any, metadata: Record<string, any> } | null}
|
|
74
|
+
*/
|
|
75
|
+
get(id: number): {
|
|
76
|
+
id: number;
|
|
77
|
+
content: any;
|
|
78
|
+
metadata: Record<string, any>;
|
|
79
|
+
} | null;
|
|
80
|
+
/**
|
|
81
|
+
* @param {number} id
|
|
82
|
+
* @returns {void}
|
|
83
|
+
*/
|
|
84
|
+
delete(id: number): void;
|
|
85
|
+
}
|
package/src/store-jsonfile.js
CHANGED
|
@@ -2,60 +2,102 @@
|
|
|
2
2
|
|
|
3
3
|
const { readFileSync, writeFileSync, existsSync } = require('node:fs');
|
|
4
4
|
|
|
5
|
+
// Past this many entries, the O(n) scan + whole-file rewrite per write becomes a
|
|
6
|
+
// real latency/availability concern. Warn once (per instance) and point at SQLiteStore.
|
|
7
|
+
const LARGE_STORE_THRESHOLD = 10000;
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* JSON file memory store. Zero deps, case-insensitive substring search.
|
|
7
11
|
*
|
|
12
|
+
* SCALING: intended for small memory sets. `search()` is an O(n) substring scan
|
|
13
|
+
* (no index), and every `store()`/`delete()` re-serializes and rewrites the entire
|
|
14
|
+
* file synchronously. Fine for hundreds–low-thousands of entries; for larger or
|
|
15
|
+
* write-heavy memory use SQLiteStore (FTS5 index, parameterized, incremental writes).
|
|
16
|
+
*
|
|
8
17
|
* Interface (implements Memory store contract):
|
|
9
18
|
* store(content, metadata) → id
|
|
10
19
|
* search(query, options) → [{ id, content, metadata, score }]
|
|
11
20
|
* get(id) → { content, metadata }
|
|
12
21
|
* delete(id) → void
|
|
22
|
+
*
|
|
23
|
+
* @typedef {object} JsonRecord
|
|
24
|
+
* @property {number} id
|
|
25
|
+
* @property {any} content
|
|
26
|
+
* @property {Record<string, any>} metadata
|
|
27
|
+
* @property {string} createdAt
|
|
13
28
|
*/
|
|
29
|
+
|
|
14
30
|
class JsonFileStore {
|
|
15
31
|
/**
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {string} options.path - Path to JSON file (required).
|
|
32
|
+
* @param {{ path?: string }} [options]
|
|
18
33
|
* @throws {Error} `[JsonFileStore] requires options.path` — when path is missing.
|
|
19
34
|
*/
|
|
20
35
|
constructor(options = {}) {
|
|
21
36
|
if (!options.path) throw new Error('[JsonFileStore] requires options.path');
|
|
22
37
|
this._path = options.path;
|
|
38
|
+
/** @type {JsonRecord[]} */
|
|
23
39
|
this._data = existsSync(this._path)
|
|
24
40
|
? JSON.parse(readFileSync(this._path, 'utf8'))
|
|
25
41
|
: [];
|
|
26
42
|
this._nextId = this._data.length
|
|
27
|
-
? this._data.reduce((max, d) => Math.max(max, d.id), 0) + 1
|
|
43
|
+
? this._data.reduce((/** @type {number} */ max, /** @type {JsonRecord} */ d) => Math.max(max, d.id), 0) + 1
|
|
28
44
|
: 1;
|
|
45
|
+
this._warnedLarge = false;
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
_save() {
|
|
32
49
|
writeFileSync(this._path, JSON.stringify(this._data, null, 2));
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @param {any} content
|
|
54
|
+
* @param {Record<string, any>} [metadata]
|
|
55
|
+
* @returns {number} id
|
|
56
|
+
*/
|
|
35
57
|
store(content, metadata = {}) {
|
|
36
58
|
const id = this._nextId++;
|
|
37
59
|
this._data.push({ id, content, metadata, createdAt: new Date().toISOString() });
|
|
38
60
|
this._save();
|
|
61
|
+
if (!this._warnedLarge && this._data.length > LARGE_STORE_THRESHOLD) {
|
|
62
|
+
this._warnedLarge = true;
|
|
63
|
+
console.warn(
|
|
64
|
+
`[JsonFileStore] ${this._data.length} entries — search is O(n) and every write rewrites ` +
|
|
65
|
+
`the whole file. Switch to SQLiteStore for memory this size.`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
39
68
|
return id;
|
|
40
69
|
}
|
|
41
70
|
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} query
|
|
73
|
+
* @param {{ limit?: number }} [options]
|
|
74
|
+
* @returns {Array<JsonRecord & { score: number }>}
|
|
75
|
+
*/
|
|
42
76
|
search(query, options = {}) {
|
|
43
77
|
const limit = options.limit || 10;
|
|
44
78
|
const q = (query || '').toLowerCase();
|
|
45
|
-
if (!q) return this._data.slice(0, limit).map(d => ({ ...d, score: 1 }));
|
|
79
|
+
if (!q) return this._data.slice(0, limit).map((/** @type {JsonRecord} */ d) => ({ ...d, score: 1 }));
|
|
46
80
|
return this._data
|
|
47
|
-
.filter(d => d.content.toLowerCase().includes(q))
|
|
81
|
+
.filter((/** @type {JsonRecord} */ d) => d.content.toLowerCase().includes(q))
|
|
48
82
|
.slice(0, limit)
|
|
49
|
-
.map(d => ({ ...d, score: 1 }));
|
|
83
|
+
.map((/** @type {JsonRecord} */ d) => ({ ...d, score: 1 }));
|
|
50
84
|
}
|
|
51
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {number} id
|
|
88
|
+
* @returns {{ id: number, content: any, metadata: Record<string, any> } | null}
|
|
89
|
+
*/
|
|
52
90
|
get(id) {
|
|
53
|
-
const item = this._data.find(d => d.id === id);
|
|
91
|
+
const item = this._data.find((/** @type {JsonRecord} */ d) => d.id === id);
|
|
54
92
|
return item ? { id: item.id, content: item.content, metadata: item.metadata } : null;
|
|
55
93
|
}
|
|
56
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {number} id
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
57
99
|
delete(id) {
|
|
58
|
-
this._data = this._data.filter(d => d.id !== id);
|
|
100
|
+
this._data = this._data.filter((/** @type {JsonRecord} */ d) => d.id !== id);
|
|
59
101
|
this._save();
|
|
60
102
|
}
|
|
61
103
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite FTS5 memory store. Full-text search with BM25 ranking.
|
|
3
|
+
*
|
|
4
|
+
* Interface (implements Memory store contract):
|
|
5
|
+
* store(content, metadata) → id
|
|
6
|
+
* search(query, options) → [{ id, content, metadata, score }]
|
|
7
|
+
* get(id) → { content, metadata }
|
|
8
|
+
* delete(id) → void
|
|
9
|
+
*
|
|
10
|
+
* Requires peer dep: better-sqlite3
|
|
11
|
+
*/
|
|
12
|
+
export type ChunkRow = {
|
|
13
|
+
id: number;
|
|
14
|
+
content: string;
|
|
15
|
+
/**
|
|
16
|
+
* - JSON-serialized metadata.
|
|
17
|
+
*/
|
|
18
|
+
metadata: string;
|
|
19
|
+
/**
|
|
20
|
+
* - FTS5 rank (search rows only).
|
|
21
|
+
*/
|
|
22
|
+
rank?: number | undefined;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* SQLite FTS5 memory store. Full-text search with BM25 ranking.
|
|
26
|
+
*
|
|
27
|
+
* Interface (implements Memory store contract):
|
|
28
|
+
* store(content, metadata) → id
|
|
29
|
+
* search(query, options) → [{ id, content, metadata, score }]
|
|
30
|
+
* get(id) → { content, metadata }
|
|
31
|
+
* delete(id) → void
|
|
32
|
+
*
|
|
33
|
+
* Requires peer dep: better-sqlite3
|
|
34
|
+
*
|
|
35
|
+
* @typedef {object} ChunkRow
|
|
36
|
+
* @property {number} id
|
|
37
|
+
* @property {string} content
|
|
38
|
+
* @property {string} metadata - JSON-serialized metadata.
|
|
39
|
+
* @property {number} [rank] - FTS5 rank (search rows only).
|
|
40
|
+
*/
|
|
41
|
+
export class SQLiteStore {
|
|
42
|
+
/**
|
|
43
|
+
* @param {{ path?: string }} [options]
|
|
44
|
+
* @throws {Error} `[SQLiteStore] requires options.path` — when path is missing.
|
|
45
|
+
* @throws {Error} `[SQLiteStore] requires better-sqlite3` — when peer dep is not installed.
|
|
46
|
+
*/
|
|
47
|
+
constructor(options?: {
|
|
48
|
+
path?: string;
|
|
49
|
+
});
|
|
50
|
+
_db: any;
|
|
51
|
+
_insertStmt: any;
|
|
52
|
+
_getStmt: any;
|
|
53
|
+
_deleteStmt: any;
|
|
54
|
+
_searchStmt: any;
|
|
55
|
+
_allStmt: any;
|
|
56
|
+
/**
|
|
57
|
+
* @param {any} content
|
|
58
|
+
* @param {Record<string, any>} [metadata]
|
|
59
|
+
* @returns {number} id
|
|
60
|
+
*/
|
|
61
|
+
store(content: any, metadata?: Record<string, any>): number;
|
|
62
|
+
/**
|
|
63
|
+
* @param {string} query
|
|
64
|
+
* @param {{ limit?: number }} [options]
|
|
65
|
+
* @returns {Array<{ id: number, content: string, metadata: Record<string, any>, score: number }>}
|
|
66
|
+
*/
|
|
67
|
+
search(query: string, options?: {
|
|
68
|
+
limit?: number;
|
|
69
|
+
}): Array<{
|
|
70
|
+
id: number;
|
|
71
|
+
content: string;
|
|
72
|
+
metadata: Record<string, any>;
|
|
73
|
+
score: number;
|
|
74
|
+
}>;
|
|
75
|
+
/**
|
|
76
|
+
* @param {number} id
|
|
77
|
+
* @returns {{ id: number, content: string, metadata: Record<string, any> } | null}
|
|
78
|
+
*/
|
|
79
|
+
get(id: number): {
|
|
80
|
+
id: number;
|
|
81
|
+
content: string;
|
|
82
|
+
metadata: Record<string, any>;
|
|
83
|
+
} | null;
|
|
84
|
+
/**
|
|
85
|
+
* @param {number} id
|
|
86
|
+
* @returns {void}
|
|
87
|
+
*/
|
|
88
|
+
delete(id: number): void;
|
|
89
|
+
close(): void;
|
|
90
|
+
}
|
package/src/store-sqlite.js
CHANGED
|
@@ -10,11 +10,17 @@
|
|
|
10
10
|
* delete(id) → void
|
|
11
11
|
*
|
|
12
12
|
* Requires peer dep: better-sqlite3
|
|
13
|
+
*
|
|
14
|
+
* @typedef {object} ChunkRow
|
|
15
|
+
* @property {number} id
|
|
16
|
+
* @property {string} content
|
|
17
|
+
* @property {string} metadata - JSON-serialized metadata.
|
|
18
|
+
* @property {number} [rank] - FTS5 rank (search rows only).
|
|
13
19
|
*/
|
|
20
|
+
|
|
14
21
|
class SQLiteStore {
|
|
15
22
|
/**
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {string} options.path - Path to SQLite database file (required).
|
|
23
|
+
* @param {{ path?: string }} [options]
|
|
18
24
|
* @throws {Error} `[SQLiteStore] requires options.path` — when path is missing.
|
|
19
25
|
* @throws {Error} `[SQLiteStore] requires better-sqlite3` — when peer dep is not installed.
|
|
20
26
|
*/
|
|
@@ -77,6 +83,11 @@ class SQLiteStore {
|
|
|
77
83
|
);
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @param {any} content
|
|
88
|
+
* @param {Record<string, any>} [metadata]
|
|
89
|
+
* @returns {number} id
|
|
90
|
+
*/
|
|
80
91
|
store(content, metadata = {}) {
|
|
81
92
|
const result = this._insertStmt.run(
|
|
82
93
|
content,
|
|
@@ -86,10 +97,15 @@ class SQLiteStore {
|
|
|
86
97
|
return Number(result.lastInsertRowid);
|
|
87
98
|
}
|
|
88
99
|
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} query
|
|
102
|
+
* @param {{ limit?: number }} [options]
|
|
103
|
+
* @returns {Array<{ id: number, content: string, metadata: Record<string, any>, score: number }>}
|
|
104
|
+
*/
|
|
89
105
|
search(query, options = {}) {
|
|
90
106
|
const limit = options.limit || 10;
|
|
91
107
|
if (!query) {
|
|
92
|
-
return this._allStmt.all(limit).map(row => ({
|
|
108
|
+
return this._allStmt.all(limit).map((/** @type {ChunkRow} */ row) => ({
|
|
93
109
|
id: row.id,
|
|
94
110
|
content: row.content,
|
|
95
111
|
metadata: JSON.parse(row.metadata),
|
|
@@ -100,14 +116,14 @@ class SQLiteStore {
|
|
|
100
116
|
const ftsQuery = query
|
|
101
117
|
.split(/\s+/)
|
|
102
118
|
.filter(Boolean)
|
|
103
|
-
.map(w => `"${w.replace(/"/g, '""')}"`)
|
|
119
|
+
.map((/** @type {string} */ w) => `"${w.replace(/"/g, '""')}"`)
|
|
104
120
|
.join(' OR ');
|
|
105
121
|
try {
|
|
106
|
-
return this._searchStmt.all(ftsQuery, limit).map(row => ({
|
|
122
|
+
return this._searchStmt.all(ftsQuery, limit).map((/** @type {ChunkRow} */ row) => ({
|
|
107
123
|
id: row.id,
|
|
108
124
|
content: row.content,
|
|
109
125
|
metadata: JSON.parse(row.metadata),
|
|
110
|
-
score: -row.rank, // FTS5 rank is negative (closer to 0 = better)
|
|
126
|
+
score: -(row.rank ?? 0), // FTS5 rank is negative (closer to 0 = better)
|
|
111
127
|
}));
|
|
112
128
|
} catch {
|
|
113
129
|
// If FTS query fails (e.g. special characters), return empty
|
|
@@ -115,12 +131,20 @@ class SQLiteStore {
|
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
/**
|
|
135
|
+
* @param {number} id
|
|
136
|
+
* @returns {{ id: number, content: string, metadata: Record<string, any> } | null}
|
|
137
|
+
*/
|
|
118
138
|
get(id) {
|
|
119
|
-
const row = this._getStmt.get(id);
|
|
139
|
+
const row = /** @type {ChunkRow | undefined} */ (this._getStmt.get(id));
|
|
120
140
|
if (!row) return null;
|
|
121
141
|
return { id: row.id, content: row.content, metadata: JSON.parse(row.metadata) };
|
|
122
142
|
}
|
|
123
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @param {number} id
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
124
148
|
delete(id) {
|
|
125
149
|
this._deleteStmt.run(id);
|
|
126
150
|
}
|
package/src/stores.d.ts
ADDED
package/src/stream.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type StreamEvent = {
|
|
2
|
+
/**
|
|
3
|
+
* - Event type identifier.
|
|
4
|
+
*/
|
|
5
|
+
type: string;
|
|
6
|
+
/**
|
|
7
|
+
* - Associated task identifier.
|
|
8
|
+
*/
|
|
9
|
+
taskId?: string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* - Arbitrary event payload.
|
|
12
|
+
*/
|
|
13
|
+
data?: any;
|
|
14
|
+
/**
|
|
15
|
+
* - ISO timestamp; auto-filled on emit if absent.
|
|
16
|
+
*/
|
|
17
|
+
ts?: string | undefined;
|
|
18
|
+
};
|
|
19
|
+
export type Transport = {
|
|
20
|
+
/**
|
|
21
|
+
* - Sink for emitted events.
|
|
22
|
+
*/
|
|
23
|
+
write: (event: StreamEvent) => void;
|
|
24
|
+
};
|
|
25
|
+
export type StreamOptions = {
|
|
26
|
+
/**
|
|
27
|
+
* - Optional transport sink for emitted events.
|
|
28
|
+
*/
|
|
29
|
+
transport?: Transport | null | undefined;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Structured event streaming for observability and cross-process communication.
|
|
33
|
+
*
|
|
34
|
+
* Interface:
|
|
35
|
+
* emit(event) → void
|
|
36
|
+
* subscribe(callback) → unsubscribe function
|
|
37
|
+
*
|
|
38
|
+
* Event shape:
|
|
39
|
+
* { type, taskId, data, ts }
|
|
40
|
+
*
|
|
41
|
+
* Transport:
|
|
42
|
+
* Object with write(event) method — e.g. JsonlTransport
|
|
43
|
+
* null — disabled (subscribers only)
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {object} StreamEvent
|
|
47
|
+
* @property {string} type - Event type identifier.
|
|
48
|
+
* @property {string} [taskId] - Associated task identifier.
|
|
49
|
+
* @property {*} [data] - Arbitrary event payload.
|
|
50
|
+
* @property {string} [ts] - ISO timestamp; auto-filled on emit if absent.
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {object} Transport
|
|
54
|
+
* @property {(event: StreamEvent) => void} write - Sink for emitted events.
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {object} StreamOptions
|
|
58
|
+
* @property {Transport|null} [transport] - Optional transport sink for emitted events.
|
|
59
|
+
*/
|
|
60
|
+
export class Stream {
|
|
61
|
+
/**
|
|
62
|
+
* @param {StreamOptions} [options={}]
|
|
63
|
+
*/
|
|
64
|
+
constructor(options?: StreamOptions);
|
|
65
|
+
/** @type {Transport|null} */
|
|
66
|
+
_transport: Transport | null;
|
|
67
|
+
/** @type {Array<(e: StreamEvent) => void>} */
|
|
68
|
+
_subscribers: Array<(e: StreamEvent) => void>;
|
|
69
|
+
/**
|
|
70
|
+
* @param {StreamEvent} event - Event to broadcast to subscribers and transport.
|
|
71
|
+
* @returns {void}
|
|
72
|
+
*/
|
|
73
|
+
emit(event: StreamEvent): void;
|
|
74
|
+
/**
|
|
75
|
+
* @param {(e: StreamEvent) => void} callback - Invoked with each emitted event.
|
|
76
|
+
* @returns {() => void} Unsubscribe function.
|
|
77
|
+
*/
|
|
78
|
+
subscribe(callback: (e: StreamEvent) => void): () => void;
|
|
79
|
+
}
|
package/src/stream.js
CHANGED
|
@@ -14,12 +14,40 @@
|
|
|
14
14
|
* Object with write(event) method — e.g. JsonlTransport
|
|
15
15
|
* null — disabled (subscribers only)
|
|
16
16
|
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} StreamEvent
|
|
20
|
+
* @property {string} type - Event type identifier.
|
|
21
|
+
* @property {string} [taskId] - Associated task identifier.
|
|
22
|
+
* @property {*} [data] - Arbitrary event payload.
|
|
23
|
+
* @property {string} [ts] - ISO timestamp; auto-filled on emit if absent.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} Transport
|
|
28
|
+
* @property {(event: StreamEvent) => void} write - Sink for emitted events.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {object} StreamOptions
|
|
33
|
+
* @property {Transport|null} [transport] - Optional transport sink for emitted events.
|
|
34
|
+
*/
|
|
35
|
+
|
|
17
36
|
class Stream {
|
|
37
|
+
/**
|
|
38
|
+
* @param {StreamOptions} [options={}]
|
|
39
|
+
*/
|
|
18
40
|
constructor(options = {}) {
|
|
41
|
+
/** @type {Transport|null} */
|
|
19
42
|
this._transport = options.transport || null;
|
|
43
|
+
/** @type {Array<(e: StreamEvent) => void>} */
|
|
20
44
|
this._subscribers = [];
|
|
21
45
|
}
|
|
22
46
|
|
|
47
|
+
/**
|
|
48
|
+
* @param {StreamEvent} event - Event to broadcast to subscribers and transport.
|
|
49
|
+
* @returns {void}
|
|
50
|
+
*/
|
|
23
51
|
emit(event) {
|
|
24
52
|
const full = { ...event, ts: event.ts || new Date().toISOString() };
|
|
25
53
|
for (const fn of this._subscribers) {
|
|
@@ -30,6 +58,10 @@ class Stream {
|
|
|
30
58
|
}
|
|
31
59
|
}
|
|
32
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @param {(e: StreamEvent) => void} callback - Invoked with each emitted event.
|
|
63
|
+
* @returns {() => void} Unsubscribe function.
|
|
64
|
+
*/
|
|
33
65
|
subscribe(callback) {
|
|
34
66
|
this._subscribers.push(callback);
|
|
35
67
|
return () => {
|
package/src/tools.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createBrowsingTools } from "../tools/browse";
|
|
2
|
+
import { createMobileTools } from "../tools/mobile";
|
|
3
|
+
import { createShellTools } from "../tools/shell";
|
|
4
|
+
import { createSpawnTool } from "../tools/spawn";
|
|
5
|
+
import { spawnChild } from "../tools/spawn";
|
|
6
|
+
import { createDeferTool } from "../tools/defer";
|
|
7
|
+
import { readQueue as readDeferQueue } from "../tools/defer";
|
|
8
|
+
export { createBrowsingTools, createMobileTools, createShellTools, createSpawnTool, spawnChild, createDeferTool, readDeferQueue };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type JsonlTransportOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* - Writable stream to write JSONL lines to. Defaults to process.stdout.
|
|
4
|
+
*/
|
|
5
|
+
output?: NodeJS.WritableStream | undefined;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* JSONL transport: one JSON object per line to a writable stream.
|
|
9
|
+
* Default: process.stdout. Pipe-friendly, parseable by any language.
|
|
10
|
+
*
|
|
11
|
+
* Debug output goes to stderr (never pollutes stdout).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {object} JsonlTransportOptions
|
|
15
|
+
* @property {NodeJS.WritableStream} [output] - Writable stream to write JSONL lines to. Defaults to process.stdout.
|
|
16
|
+
*/
|
|
17
|
+
export class JsonlTransport {
|
|
18
|
+
/**
|
|
19
|
+
* @param {JsonlTransportOptions} [options={}]
|
|
20
|
+
*/
|
|
21
|
+
constructor(options?: JsonlTransportOptions);
|
|
22
|
+
_output: NodeJS.WritableStream | (NodeJS.WriteStream & {
|
|
23
|
+
fd: 1;
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* @param {*} event - Event object to serialize as one JSON line.
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
write(event: any): void;
|
|
30
|
+
}
|
package/src/transport-jsonl.js
CHANGED
|
@@ -6,11 +6,24 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Debug output goes to stderr (never pollutes stdout).
|
|
8
8
|
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} JsonlTransportOptions
|
|
12
|
+
* @property {NodeJS.WritableStream} [output] - Writable stream to write JSONL lines to. Defaults to process.stdout.
|
|
13
|
+
*/
|
|
14
|
+
|
|
9
15
|
class JsonlTransport {
|
|
16
|
+
/**
|
|
17
|
+
* @param {JsonlTransportOptions} [options={}]
|
|
18
|
+
*/
|
|
10
19
|
constructor(options = {}) {
|
|
11
20
|
this._output = options.output || process.stdout;
|
|
12
21
|
}
|
|
13
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @param {*} event - Event object to serialize as one JSON line.
|
|
25
|
+
* @returns {void}
|
|
26
|
+
*/
|
|
14
27
|
write(event) {
|
|
15
28
|
this._output.write(JSON.stringify(event) + '\n');
|
|
16
29
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates browsing tools via barebrowse (optional dep).
|
|
3
|
+
* Returns { tools, close } or null if barebrowse is not installed.
|
|
4
|
+
* @param {object} [opts] - Options passed to barebrowse createBrowseTools
|
|
5
|
+
* @returns {Promise<{tools: Array, close: Function}|null>}
|
|
6
|
+
*/
|
|
7
|
+
export function createBrowsingTools(opts?: object): Promise<{
|
|
8
|
+
tools: any[];
|
|
9
|
+
close: Function;
|
|
10
|
+
} | null>;
|
package/tools/browse.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
async function createBrowsingTools(opts = {}) {
|
|
10
10
|
try {
|
|
11
|
+
// barebrowse is an optional dep; the subpath is declared as an ambient `any`
|
|
12
|
+
// module in types/shims.d.ts and resolves to `any` at runtime.
|
|
11
13
|
const { createBrowseTools } = await import('barebrowse/bareagent');
|
|
12
14
|
return createBrowseTools(opts);
|
|
13
15
|
} catch {
|