bare-agent 0.4.2 → 0.4.3

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 (3) hide show
  1. package/README.md +4 -4
  2. package/package.json +2 -2
  3. package/src/loop.js +36 -5
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  ```
13
13
 
14
- **Agent orchestration in ~1700 lines. Zero required deps. MIT license.**
14
+ **Agent orchestration in ~1800 lines. Zero required deps. MIT license.**
15
15
 
16
16
  Lightweight enough to understand completely. Complete enough to not reinvent wheels. Not a framework, not 50,000 lines of opinions — just composable building blocks for agents.
17
17
 
@@ -60,7 +60,7 @@ Every piece works alone — take what you need, ignore the rest.
60
60
 
61
61
  | Component | What it does |
62
62
  |---|---|
63
- | **Loop** | Think → act → observe → repeat. Calls any LLM, executes your tools, loops until done. Throws on error by default |
63
+ | **Loop** | Think → act → observe → repeat. Calls any LLM, executes your tools, loops until done. Throws on error by default. Returns estimated USD cost per run |
64
64
  | **Planner** | Break a goal into a step DAG via LLM. Built-in caching (`cacheTTL`) |
65
65
  | **runPlan** | Execute steps in parallel waves. Dependency-aware, failure propagation, per-step retry |
66
66
  | **Retry** | Exponential/linear backoff with jitter. Respects `err.retryable` |
@@ -72,7 +72,7 @@ Every piece works alone — take what you need, ignore the rest.
72
72
  | **Scheduler** | Cron (`0 9 * * 1-5`) or relative (`2h`, `30m`). Persisted jobs survive restarts |
73
73
  | **Stream** | Structured event emitter. Pipe as JSONL, subscribe in-process, or custom transport |
74
74
  | **Errors** | Typed hierarchy — `ProviderError`, `ToolError`, `TimeoutError`, `MaxRoundsError`, `CircuitOpenError` |
75
- | **Browsing** | Web navigation, clicking, typing, reading via `barebrowse`. Two modes: library tools (inline snapshots, pass to Loop) or CLI session (disk-based snapshots, token-efficient for multi-step flows) |
75
+ | **Browsing** | Web navigation, clicking, typing, reading via `barebrowse` (17 tools). Two modes: library tools (inline snapshots, pass to Loop) or CLI session (disk-based snapshots, token-efficient for multi-step flows). Optional `assess` tool (privacy scan) when `wearehere` is installed |
76
76
  | **Mobile** | Android + iOS device control via `baremobile`. Same two modes: library tools (`createMobileTools` — action tools auto-return snapshots) or CLI session (`baremobile` CLI — disk-based snapshots) |
77
77
 
78
78
  **Providers:** OpenAI-compatible (OpenAI, OpenRouter, Groq, vLLM, LM Studio), Anthropic, Ollama, CLIPipe (any CLI tool via stdin/stdout with real-time streaming), Fallback, or bring your own (one method: `generate`). All return the same shape — swap freely.
@@ -81,7 +81,7 @@ Every piece works alone — take what you need, ignore the rest.
81
81
 
82
82
  **Cross-language:** Runs as a subprocess. Communicate via JSONL on stdin/stdout from Python, Go, Rust, Ruby, Java, or anything that can spawn a process. Ready-made wrappers in [`contrib/`](contrib/README.md).
83
83
 
84
- **Deps:** 0 required. Optional: `cron-parser` (cron expressions), `better-sqlite3` (SQLite store), `barebrowse` (web browsing), `baremobile` (Android + iOS device control).
84
+ **Deps:** 0 required. Optional: `cron-parser` (cron expressions), `better-sqlite3` (SQLite store), `barebrowse` (web browsing), `baremobile` (Android + iOS device control), `wearehere` (privacy assessment via barebrowse).
85
85
 
86
86
  ---
87
87
 
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "bare-agent",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "files": [
5
5
  "index.js",
6
6
  "src/",
7
7
  "bin/",
8
8
  "tools/"
9
9
  ],
10
- "description": "Lightweight, composable agent orchestration. ~1700 lines, 0 required deps.",
10
+ "description": "Lightweight, composable agent orchestration. ~1800 lines, 0 required deps.",
11
11
  "license": "MIT",
12
12
  "author": "hamr0",
13
13
  "repository": {
package/src/loop.js CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  const { ToolError, MaxRoundsError } = require('./errors');
4
4
 
5
+ // Average pricing per 1K tokens (USD). Adjust these to match your provider's rates.
6
+ // Last updated: 2026-03-18. Source: public provider pricing pages.
7
+ const COST_PER_1K = {
8
+ // OpenAI
9
+ 'gpt-4o': { in: 0.0025, out: 0.01 },
10
+ 'gpt-4o-mini': { in: 0.00015, out: 0.0006 },
11
+ 'gpt-4.1': { in: 0.002, out: 0.008 },
12
+ 'gpt-4.1-mini': { in: 0.0004, out: 0.0016 },
13
+ 'gpt-4.1-nano': { in: 0.0001, out: 0.0004 },
14
+ 'o3-mini': { in: 0.0011, out: 0.0044 },
15
+ // Anthropic
16
+ 'claude-sonnet-4-20250514': { in: 0.003, out: 0.015 },
17
+ 'claude-haiku-4-5-20251001': { in: 0.0008, out: 0.004 },
18
+ 'claude-opus-4-20250514': { in: 0.015, out: 0.075 },
19
+ // Fallback average across popular models (~$0.002 in, ~$0.008 out per 1K)
20
+ '_default': { in: 0.002, out: 0.008 },
21
+ };
22
+
23
+ function estimateCost(model, usage) {
24
+ if (!usage || !model) return null;
25
+ const rates = COST_PER_1K[model] || COST_PER_1K['_default'];
26
+ return (
27
+ ((usage.inputTokens || 0) * rates.in +
28
+ (usage.outputTokens || 0) * rates.out) / 1000
29
+ );
30
+ }
31
+
5
32
  class Loop {
6
33
  /**
7
34
  * @param {object} options
@@ -68,6 +95,7 @@ class Loop {
68
95
  this.stream?.emit({ type: 'loop:start', data: { messageCount: msgs.length } });
69
96
 
70
97
  let lastUsage = { inputTokens: 0, outputTokens: 0 };
98
+ let totalCost = 0;
71
99
 
72
100
  for (let round = 0; round < this.maxRounds; round++) {
73
101
  if (this._stopped) break;
@@ -80,17 +108,20 @@ class Loop {
80
108
  this.stream?.emit({ type: 'loop:error', data: { error: err.message, round } });
81
109
  this.onError?.(err);
82
110
  if (this.throwOnError) throw err;
83
- return { text: '', toolCalls: [], usage: lastUsage, error: err.message };
111
+ return { text: '', toolCalls: [], usage: lastUsage, cost: totalCost, error: err.message };
84
112
  }
85
113
 
86
114
  lastUsage = result.usage || lastUsage;
115
+ const model = this.provider.model || null;
116
+ const roundCost = estimateCost(model, lastUsage);
117
+ if (roundCost !== null) totalCost += roundCost;
87
118
 
88
119
  // No tool calls — LLM gave a final text response
89
120
  if (!result.toolCalls || result.toolCalls.length === 0) {
90
121
  this.stream?.emit({ type: 'loop:text', data: { text: result.text } });
91
122
  this.onText?.(result.text);
92
- this.stream?.emit({ type: 'loop:done', data: { text: result.text, usage: lastUsage } });
93
- return { text: result.text, toolCalls: [], usage: lastUsage, error: null };
123
+ this.stream?.emit({ type: 'loop:done', data: { text: result.text, usage: lastUsage, cost: totalCost } });
124
+ return { text: result.text, toolCalls: [], usage: lastUsage, cost: totalCost, error: null };
94
125
  }
95
126
 
96
127
  // Execute tool calls
@@ -149,9 +180,9 @@ class Loop {
149
180
 
150
181
  // maxRounds exceeded
151
182
  const warning = `[Loop] ended after ${this.maxRounds} rounds without final response`;
152
- this.stream?.emit({ type: 'loop:done', data: { text: '', warning } });
183
+ this.stream?.emit({ type: 'loop:done', data: { text: '', warning, cost: totalCost } });
153
184
  if (this.throwOnError) throw new MaxRoundsError(warning);
154
- return { text: '', toolCalls: [], usage: lastUsage, error: warning };
185
+ return { text: '', toolCalls: [], usage: lastUsage, cost: totalCost, error: warning };
155
186
  }
156
187
 
157
188
  /**