bare-agent 0.2.0 → 0.2.1
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/README.md +10 -3
- package/package.json +3 -2
- package/src/provider-clipipe.js +19 -4
- package/src/run-plan.js +7 -1
- package/src/transports.js +3 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
# bare-agent
|
|
15
15
|
|
|
16
|
-
**Agent orchestration in ~
|
|
16
|
+
**Agent orchestration in ~1500 lines. Zero required deps. MIT license.**
|
|
17
17
|
|
|
18
18
|
Everything between "call the LLM" and "ship the agent" — loop, plan, remember, schedule, checkpoint. Each works alone. All compose together.
|
|
19
19
|
|
|
@@ -217,12 +217,19 @@ Same pattern works from Go, Rust, Java, Ruby — any language that can spawn a p
|
|
|
217
217
|
required: 0
|
|
218
218
|
optional: cron-parser (for cron expressions in scheduler)
|
|
219
219
|
peer: better-sqlite3 (for SQLite memory store)
|
|
220
|
-
total lines: ~
|
|
220
|
+
total lines: ~1500
|
|
221
221
|
```
|
|
222
222
|
|
|
223
223
|
## Status
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
**Production-validated.** bare-agent powers the SOAR2 pipeline in [Aurora](https://github.com/hamr0/aurora), replacing ~400 lines of hand-rolled agent orchestration with ~60 lines of bare-agent wiring. In production use, bare-agent eliminated:
|
|
226
|
+
|
|
227
|
+
- **Boilerplate** — Tool-calling loop, provider normalization, retry logic, and state tracking that every agent project reinvents. Aurora's SOAR2 pipeline dropped from custom loop + manual state management to `Loop + Planner + runPlan + StateMachine`.
|
|
228
|
+
- **Fragile glue code** — Manual wave execution, dependency resolution, and error propagation replaced by `runPlan` with built-in parallelism and failure cascading.
|
|
229
|
+
- **Provider lock-in** — Switching from OpenAI to Anthropic to CLIPipe required zero orchestration changes — just swap the provider constructor.
|
|
230
|
+
- **Debugging friction** — Structured `[ComponentName]` error prefixes and `Stream` events made failures traceable in minutes instead of hours.
|
|
231
|
+
|
|
232
|
+
See [project plan](docs/01-product/prd.md) for the full design. See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
226
233
|
|
|
227
234
|
## License
|
|
228
235
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"files": [
|
|
5
5
|
"index.js",
|
|
6
6
|
"src/",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"exports": {
|
|
21
21
|
".": "./index.js",
|
|
22
22
|
"./providers": "./src/providers.js",
|
|
23
|
-
"./stores": "./src/stores.js"
|
|
23
|
+
"./stores": "./src/stores.js",
|
|
24
|
+
"./transports": "./src/transports.js"
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": ">=18"
|
package/src/provider-clipipe.js
CHANGED
|
@@ -11,6 +11,7 @@ class CLIPipeProvider {
|
|
|
11
11
|
* @param {string} [options.cwd] - Working directory for the child process.
|
|
12
12
|
* @param {object} [options.env] - Environment variables for the child process.
|
|
13
13
|
* @param {number} [options.timeout=30000] - Timeout in milliseconds.
|
|
14
|
+
* @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.
|
|
14
15
|
* @throws {Error} `[CLIPipeProvider] requires command` — when options.command is missing.
|
|
15
16
|
*/
|
|
16
17
|
constructor(options = {}) {
|
|
@@ -20,6 +21,7 @@ class CLIPipeProvider {
|
|
|
20
21
|
this.cwd = options.cwd || undefined;
|
|
21
22
|
this.env = options.env || undefined;
|
|
22
23
|
this.timeout = options.timeout ?? 30000;
|
|
24
|
+
this.systemPromptFlag = options.systemPromptFlag || null;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/**
|
|
@@ -34,8 +36,20 @@ class CLIPipeProvider {
|
|
|
34
36
|
* @throws {Error} `[CLIPipeProvider] process produced no output` — when stdout is empty.
|
|
35
37
|
*/
|
|
36
38
|
async generate(messages, tools = [], options = {}) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
let extraArgs = [];
|
|
40
|
+
let promptMessages = messages;
|
|
41
|
+
|
|
42
|
+
if (this.systemPromptFlag) {
|
|
43
|
+
const systemMessages = messages.filter(m => m.role === 'system');
|
|
44
|
+
if (systemMessages.length > 0) {
|
|
45
|
+
const systemContent = systemMessages.map(m => m.content).join('\n\n');
|
|
46
|
+
extraArgs = [this.systemPromptFlag, systemContent];
|
|
47
|
+
promptMessages = messages.filter(m => m.role !== 'system');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const prompt = this._formatPrompt(promptMessages);
|
|
52
|
+
const text = await this._spawn(prompt, extraArgs);
|
|
39
53
|
return {
|
|
40
54
|
text,
|
|
41
55
|
toolCalls: [],
|
|
@@ -58,11 +72,12 @@ class CLIPipeProvider {
|
|
|
58
72
|
/**
|
|
59
73
|
* Spawn the CLI process, pipe prompt to stdin, collect stdout.
|
|
60
74
|
* @param {string} prompt
|
|
75
|
+
* @param {string[]} [extraArgs=[]] - Additional args prepended to this.args.
|
|
61
76
|
* @returns {Promise<string>}
|
|
62
77
|
*/
|
|
63
|
-
_spawn(prompt) {
|
|
78
|
+
_spawn(prompt, extraArgs = []) {
|
|
64
79
|
return new Promise((resolve, reject) => {
|
|
65
|
-
const child = spawn(this.command, this.args, {
|
|
80
|
+
const child = spawn(this.command, [...this.args, ...extraArgs], {
|
|
66
81
|
cwd: this.cwd,
|
|
67
82
|
env: this.env,
|
|
68
83
|
stdio: ['pipe', 'pipe', 'pipe'],
|
package/src/run-plan.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @param {function} [options.onStepStart] - Callback(step) fired when a step begins.
|
|
11
11
|
* @param {function} [options.onStepDone] - Callback(step, result) fired on success.
|
|
12
12
|
* @param {function} [options.onStepFail] - Callback(step, error) fired on failure.
|
|
13
|
+
* @param {function} [options.onWaveStart] - Callback(waveNumber, steps) fired before each wave executes.
|
|
13
14
|
* @returns {Promise<Array<{id: string, status: string, result?: *, error?: string}>>}
|
|
14
15
|
* @throws {Error} `[runPlan] steps must be a non-empty array` — when steps is not a non-empty array.
|
|
15
16
|
* @throws {Error} `[runPlan] executeFn must be a function` — when executeFn is not a function.
|
|
@@ -47,7 +48,9 @@ async function runPlan(steps, executeFn, options = {}) {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
const { concurrency = Infinity, stateMachine, onStepStart, onStepDone, onStepFail } = options;
|
|
51
|
+
const { concurrency = Infinity, stateMachine, onStepStart, onStepDone, onStepFail, onWaveStart } = options;
|
|
52
|
+
|
|
53
|
+
let waveNumber = 0;
|
|
51
54
|
|
|
52
55
|
// Wave loop
|
|
53
56
|
while (true) {
|
|
@@ -81,6 +84,9 @@ async function runPlan(steps, executeFn, options = {}) {
|
|
|
81
84
|
// Apply concurrency limit
|
|
82
85
|
const wave = ready.slice(0, concurrency);
|
|
83
86
|
|
|
87
|
+
waveNumber++;
|
|
88
|
+
onWaveStart?.(waveNumber, wave.map(e => e.step));
|
|
89
|
+
|
|
84
90
|
// Execute wave
|
|
85
91
|
await Promise.all(wave.map(async entry => {
|
|
86
92
|
const { step } = entry;
|