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 CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  # bare-agent
15
15
 
16
- **Agent orchestration in ~800 lines. Zero required deps. MIT license.**
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: ~820
220
+ total lines: ~1500
221
221
  ```
222
222
 
223
223
  ## Status
224
224
 
225
- Early development. Core components built and validated through POCs. See [project plan](docs/01-product/prd.md) for the full design.
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.0",
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"
@@ -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
- const prompt = this._formatPrompt(messages);
38
- const text = await this._spawn(prompt);
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;
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+ const { JsonlTransport } = require('./transport-jsonl');
3
+ module.exports = { JsonlTransport };