bare-agent 0.46.5 → 0.46.6
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 +49 -272
- package/bareagent.context.md +28 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,292 +17,62 @@
|
|
|
17
17
|
<img src="https://img.shields.io/badge/license-Apache%202.0-2a4f8c" alt="license: Apache 2.0">
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
|
-
**Lightweight agent
|
|
20
|
+
**Lightweight agent primitives. Zero required deps — optional [bareguard](https://npmjs.com/package/bareguard) peer for one-gate governance.**
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
bare-agent started as primitives for building agents by hand: your code is the deterministic body, an LLM call sits in wherever you need judgment — the LangChain / CrewAI / AutoGen job, provider-agnostic, without a framework.
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
LLMs got good at wiring things themselves, so the same primitives are now offered as tool calls too: the model wires them up, bare-agent runs the loop, retries, budgets, and governance underneath. It's what [bareloop](https://github.com/hamr0/bareloop) and [fwdloop](https://github.com/hamr0/fwdloop) run on.
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
npm install bare-agent
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**1. Give your AI assistant the integration guide**
|
|
26
|
+
Some things an LLM can't judge about its own work. Building a harness or an external arbiter? Every primitive is listed in `primitives.json` — when to use it, import, signature, failure modes, a runnable example — plus a decisive return-time `judge` and the Jev gut-check tier.
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
Read bareagent.context.md from node_modules/bare-agent/bareagent.context.md
|
|
34
|
-
```
|
|
28
|
+
## Start here
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
- **Building by hand** → `npm install bare-agent`, then hand your assistant `bareagent.context.md` (ships in the package — the complete contract).
|
|
31
|
+
- **AI agent / tool-calling** → read `primitives.json` first (`unpkg.com/bare-agent/primitives.json` before you install, or `require('bare-agent/primitives.json')`); generated from source, never drifts.
|
|
32
|
+
- **Harness / judge builder** → `primitives.json` + `judge` + Jev + wire bareguard.
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
## Fast gut check — Jev
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
I need an agent that:
|
|
42
|
-
- Takes a user goal and breaks it into steps
|
|
43
|
-
- Runs steps in parallel where possible
|
|
44
|
-
- Retries failed steps twice
|
|
45
|
-
- Streams progress as JSONL events
|
|
36
|
+
An LLM call is slow, deliberate thinking (System 2). Jev, from TypeSafe, is the fast gut check (System 1): a classifier that returns a typed decision with a calibrated probability — roughly 200× faster and 400× cheaper than an LLM, per TypeSafe. bare-agent's `JevProvider` covers all three shapes — **yes/no**, **pick-one**, **score** — so your automation calls it like any other step: act when it's confident, escalate when it isn't. Injection-hardened by default; also usable as the `jev` check in the Evaluator.
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
That's it. The context doc is structured for LLM consumption — your agent reads it once and knows how to wire every component.
|
|
51
|
-
|
|
52
|
-
**Not sure what you need?** Paste this into any AI assistant:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
I want to build an agent using bare-agent. Read the integration guide at
|
|
56
|
-
node_modules/bare-agent/bareagent.context.md, then ask me up to 5 questions
|
|
57
|
-
about what I need. Based on my answers, tell me which components to use
|
|
58
|
-
and show me the wiring code.
|
|
59
|
-
```
|
|
38
|
+
## Break big tasks down — RLM
|
|
60
39
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
## For AI agents — the menu
|
|
64
|
-
|
|
65
|
-
Building tool-calling automation on top of bare-agent? Read **`primitives.json`** first. It's a compact, machine-readable menu of every building block — the fast path to *using* bare-agent without reading the docs: load it, pick a primitive, wire it. Each entry carries `when` to reach for it, its `import`, `signature`, `fails`, and a runnable `example`:
|
|
66
|
-
|
|
67
|
-
```jsonc
|
|
68
|
-
{
|
|
69
|
-
"name": "AnthropicProvider",
|
|
70
|
-
"category": "providers",
|
|
71
|
-
"when": "you want Claude models as the Loop's provider — native Messages API with opt-in prompt caching and thinking-block passthrough",
|
|
72
|
-
"import": "import { AnthropicProvider } from 'bare-agent/providers'",
|
|
73
|
-
"signature": "new AnthropicProvider(options?: AnthropicOptions)",
|
|
74
|
-
"fails": "…",
|
|
75
|
-
"example": "…"
|
|
76
|
-
}
|
|
77
|
-
// 51 entries across: providers · routing · retrieval · mcp · loop · memory · tools · skills · evaluation · hitl · resilience · governance · orchestration · …
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Browse it on unpkg (`unpkg.com/bare-agent/primitives.json`) before you install, `require` it (`const menu = require('bare-agent/primitives.json')`), or point a tool at it. Generated from the source, so it never drifts.
|
|
81
|
-
|
|
82
|
-
**Then go deeper:** `bareagent.context.md` (the complete contract — every option and the full API) and the rest of this README for the recipes.
|
|
40
|
+
`recurse` brings Recursive Language Models (RLM) to your code in one call: split a hard task into smaller ones, run each in a fresh context window, check the result, and stitch it back together. The model decides when to split (or you force a fixed count), and over a large set of documents it picks the path by question shape — scan everything for "how many", search for a needle. Totals are counted by code, and a dead worker comes back `incomplete`, never a faked pass. Cost is open by design: run it under a budget cap (bareguard) or set `maxDepth: 1`.
|
|
83
41
|
|
|
84
42
|
## What's inside
|
|
85
43
|
|
|
86
|
-
Every piece works alone — take what you need, ignore the rest.
|
|
87
|
-
|
|
88
|
-
### Act — get work done
|
|
89
|
-
|
|
90
|
-
| Component | What it does |
|
|
91
|
-
|---|---|
|
|
92
|
-
| **Loop** | Think → act → observe until done. Any LLM, your tools, per-run cost. Opt-in seams: `policy` (govern), `assemble` (context engineering), `trim` (bound the transcript) |
|
|
93
|
-
| **Planner** | Break a goal into a step DAG. Cached |
|
|
94
|
-
| **assessComplexity** | Rate a goal `simple`→`critical` from its text — no LLM. Gates whether to plan |
|
|
95
|
-
| **runPlan** | Run plan steps in parallel waves. Dependency-aware, per-step retry |
|
|
96
|
-
| **recurse** | RLM decompose→fan-out→verify→synthesize in one call. Model-driven (`spawn_child`) or forced fan-out (`count`); `retrieval:'scan'` answers "how many / all" over a corpus by scanning every slice and code-counting — never a faked pass (honest `{incomplete, missingSlices}`) |
|
|
97
|
-
| **Memory** | Persist + recall across sessions via a swappable `Store` — zero-dep JSON, SQLite, or [litectx](https://npmjs.com/package/litectx) in a one-line swap |
|
|
98
|
-
| **StateMachine** | Task lifecycle: `pending → running → done / failed / waiting / cancelled` |
|
|
99
|
-
| **Scheduler** | Cron or relative triggers. Jobs survive restarts |
|
|
100
|
-
| **Checkpoint** | Human approval gate — bring your own transport |
|
|
101
|
-
| **Spawn** | Fork a child agent. One shared audit log + budget |
|
|
102
|
-
| **Defer** | Queue an action for a waker to fire later. Governed when emitted *and* when it fires |
|
|
103
|
-
| **Retry · CircuitBreaker · Fallback** | Resilience: backoff with jitter, fail-fast, provider failover |
|
|
104
|
-
| **Stream · Errors** | Structured JSONL events; typed error hierarchy |
|
|
105
|
-
| **Browsing · Mobile · Shell** | Hands: web ([barebrowse](https://npmjs.com/package/barebrowse)), Android + iOS ([baremobile](https://npmjs.com/package/baremobile)), cross-platform shell — library tools or token-thrifty CLI sessions |
|
|
106
|
-
| **MCP Bridge** | Auto-discover MCP servers from your IDE configs; expose as tools (bulk or meta-tools) |
|
|
107
|
-
|
|
108
|
-
### Verify — check the work, keep context clean *(the eval-assist suite)*
|
|
109
|
-
|
|
110
|
-
| Component | What it does |
|
|
111
|
-
|---|---|
|
|
112
|
-
| **Evaluator + refine** | Judge output by `predicate` (no tokens), `rubric` (an isolated adversarial grader), `agentic` (a critic that exercises the live artifact), or `jev` (a cheap calibrated classifier tier). `refine` is the bounded generate→evaluate→regenerate loop |
|
|
113
|
-
| **SkillRegistry** | Surface skills on demand: one meta-tool catalog; activating a skill injects its instructions and unlocks its tools |
|
|
114
|
-
| **stash** | Compact finished work out of the live window (restorable), or auto-fold the middle under token pressure |
|
|
44
|
+
Every piece works alone — take what you need, ignore the rest. No required deps — the core imports nothing.
|
|
115
45
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`recurse(task, ctx, opts)` does **decompose → fan-out → verify → synthesize** in one call — Recursive Language Models as a single import, composed *around* the Loop (never a new engine). The default is **model-driven**: the worker is handed a `spawn_child` tool and decides whether to split, bounded by depth + bareguard (no second guard layer). Forced fan-out (`count` / `mode:'fanout'`) and data-driven width (`mode:'partition'`, measured from a corpus) are opt-in. Give workers a stance with `opts.persona` (prepended to every worker, carries down the tree, deliberately kept out of the isolated verifier), and tell them *where they are* with `opts.context` (a read-only paths/cwd blob threaded to every worker so a sliced child can locate its artifact — facts, not a stance). For a leaf that should self-correct, pass `opts.refineLeaf` (opt-in): a definite leaf becomes a bounded generate→sense→regenerate loop driven by *your* deterministic sensor (test/compile/lint — one that judges the *returned* result, never a worker side-effect it could game), feeding the gap back (with escalating temperature on models that accept it; on a temperature-fixed model like `claude-sonnet-5` the gap critique carries recovery, and the receipt records the effective temps). On a temperature-fixed model, `refineLeaf.rejectedBuffer` adds a second lever — it feeds the model's own prior *failed attempts* back verbatim ("write something structurally different"), the directed-diversity complement to temperature's random diversity (adaptive by default; the two are antagonistic, so it holds temperature flat when it engages). A *broken* arbiter — a sensor or a caller `evaluate` that throws or returns a malformed verdict — is named, never blamed on the model: the node stops at the first broken close and returns `{ incomplete, blocker: 'broken-sensor' | 'broken-verifier' }` with the model's last output preserved. The headline guarantee: **aggregation is code, never a model-stated number**, and a dead worker or exhausted guard returns an honest `{ incomplete, missingSlices }` — never a faked pass.
|
|
119
|
-
|
|
120
|
-
Over a corpus, context reaches a worker as a **handle routed by question shape** (`opts.retrieval`):
|
|
121
|
-
|
|
122
|
-
| `retrieval` | Use it for | How |
|
|
46
|
+
| Area | Component | What it does |
|
|
123
47
|
|---|---|---|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
**Tools:** Any function is a tool — REST, MCP, CLI, shell. Built-in web + mobile (optional).
|
|
154
|
-
|
|
155
|
-
**Cross-language:** Run as a subprocess; talk JSONL over stdin/stdout from Python, Go, Rust, Ruby, or Java. Wrappers in [`contrib/`](contrib/README.md).
|
|
156
|
-
|
|
157
|
-
**Deps:** none required — the core imports nothing. Optional peers: `bareguard >=0.9.0 <1.0.0` (governance), `better-sqlite3` (SQLite store); optional: `cron-parser`, `barebrowse`, `baremobile`, `wearehere`.
|
|
158
|
-
|
|
159
|
-
This table is the map, not the manual — per-component wiring and API detail live in the [Integration Guide](bareagent.context.md) and [Usage Guide](docs/archive/usage-guide.md).
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## Recipes
|
|
164
|
-
|
|
165
|
-
### Wire bareguard into Loop
|
|
166
|
-
|
|
167
|
-
```js
|
|
168
|
-
const { Gate } = require('bareguard');
|
|
169
|
-
const { Loop, wireGate, defaultActionTranslator } = require('bare-agent');
|
|
170
|
-
|
|
171
|
-
const gate = new Gate({
|
|
172
|
-
budget: { maxCostUsd: 0.50 },
|
|
173
|
-
limits: { maxToolRounds: 20 }, // bareguard 0.4.2+ — N tool rounds, LLM rounds bypass
|
|
174
|
-
audit: { path: './audit.jsonl' },
|
|
175
|
-
});
|
|
176
|
-
await gate.init();
|
|
177
|
-
|
|
178
|
-
const { policy, onLlmResult, onToolResult, filterTools } = wireGate(gate, {
|
|
179
|
-
// Optional: translate tool names → bareguard primitive types for bash/fs/net rules.
|
|
180
|
-
// bareguard 0.4.1+ reads args.command / args.path verbatim, so args passes through.
|
|
181
|
-
actionTranslator: (toolName, args, ctx) => {
|
|
182
|
-
if (toolName === 'shell_exec') return { type: 'bash', args, _ctx: ctx };
|
|
183
|
-
if (toolName === 'shell_read') return { type: 'read', args, _ctx: ctx };
|
|
184
|
-
if (toolName === 'shell_write') return { type: 'write', args, _ctx: ctx }; // gate by fs.writeScope
|
|
185
|
-
if (toolName === 'shell_edit') return { type: 'edit', args, _ctx: ctx }; // same fs.writeScope as write
|
|
186
|
-
return defaultActionTranslator(toolName, args, ctx);
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
const tools = await filterTools(myTools); // drop tools denied by static policy
|
|
190
|
-
|
|
191
|
-
const loop = new Loop({ provider, policy, onLlmResult, onToolResult });
|
|
192
|
-
await loop.run([{ role: 'user', content: 'go' }], tools, { ctx: { userId: 42 } });
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
`onLlmResult` + `onToolResult` are what make `budget.maxCostUsd` actually cover token-heavy workloads — without them, budget only sees tool cost. `ctx` flows through to `gate.record` as `_ctx` for per-principal accounting.
|
|
196
|
-
|
|
197
|
-
### Per-principal bypass (owner / admin role)
|
|
198
|
-
|
|
199
|
-
Wrap the gate policy when a principal is trusted unconditionally:
|
|
200
|
-
|
|
201
|
-
```js
|
|
202
|
-
const { policy: gatePolicy } = wireGate(gate);
|
|
203
|
-
|
|
204
|
-
const policy = async (toolName, args, ctx) => {
|
|
205
|
-
if (ctx?.role === 'owner') return true; // bypass gate entirely
|
|
206
|
-
return gatePolicy(toolName, args, ctx);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
new Loop({ provider, policy, onLlmResult, onToolResult });
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Bypassing the gate also bypasses audit and budget — only do this for principals you trust unconditionally. For partial trust, use ctx-aware rules inside bareguard instead.
|
|
213
|
-
|
|
214
|
-
### Custom deny strings (localize / strip prefix)
|
|
215
|
-
|
|
216
|
-
```js
|
|
217
|
-
const { policy } = wireGate(gate, {
|
|
218
|
-
formatDeny: (decision) => `Sorry — ${decision.reason || 'not allowed'}`,
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Halt-severity decisions bypass `formatDeny` (they throw `HaltError` and exit the loop without ever reaching the LLM).
|
|
223
|
-
|
|
224
|
-
### Catch halts in your app
|
|
225
|
-
|
|
226
|
-
```js
|
|
227
|
-
const result = await loop.run(msgs, tools);
|
|
228
|
-
if (result.error?.startsWith('halt:')) {
|
|
229
|
-
// budget cap, turn cap, or gate terminated. Inspect rule:
|
|
230
|
-
const rule = result.error.slice('halt:'.length);
|
|
231
|
-
// tell the user, schedule retry, escalate, etc.
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Halts also fire `loop:error` on the stream (`source: 'halt'`) and the `onError` callback (with a `HaltError` instance).
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## Examples
|
|
240
|
-
|
|
241
|
-
Runnable scripts in [`examples/`](examples/) — each is self-contained and the file's top docstring documents flags and required env vars.
|
|
242
|
-
|
|
243
|
-
| File | What it shows |
|
|
244
|
-
|---|---|
|
|
245
|
-
| [`with-bareguard.mjs`](examples/with-bareguard.mjs) | End-to-end Loop + bareguard wiring: budget cap, fs scope, bash allowlist, audit log, humanChannel. The canonical governed-loop reference. |
|
|
246
|
-
| [`mcp-bridge-poc.js`](examples/mcp-bridge-poc.js) | Auto-discover MCP servers from your IDE configs and expose them as bareagent tools. First run writes `.mcp-bridge.json` (edit to deny tools). |
|
|
247
|
-
| [`mcp-bridge-concurrent.js`](examples/mcp-bridge-concurrent.js) | Soak test: fan out concurrent `barebrowse_browse` calls against real domains (Amazon, Wikipedia, GitHub, a dead host) and verify resilience. |
|
|
248
|
-
| [`orchestrator/`](examples/orchestrator/) | Multi-agent dispatch via `spawn`. Three configs, one system prompt — no orchestrator class, no role types. Roles are JSON files. |
|
|
249
|
-
| [`wake.sh`](examples/wake.sh) + [`wake.md`](examples/wake.md) | Reference cron + jq script for firing deferred actions. The runtime half of `createDeferTool` — bareagent emits, `wake.sh` fires. |
|
|
250
|
-
| [`replay-job.js`](examples/replay-job.js) | Supervised replay POC: record a browser task once with the LLM driving, then replay against fresh snapshots with the LLM as locator-only. Falls back to full reasoning when the locator misses, and patches the trace. |
|
|
251
|
-
| [`litectx-as-store.mjs`](examples/litectx-as-store.mjs) | Mount [litectx](https://npmjs.com/package/litectx) as the `Memory` `Store` — one-line swap from `JsonFileStore` to ranked, graph-aware recall; the host code never changes (RT-3). |
|
|
252
|
-
| [`litectx-mcp-child.mjs`](examples/litectx-mcp-child.mjs) | Give a spawned child agent litectx's reasoning verbs as MCP tools, read-only on its own db, via `liteCtxMcpBridgeConfig` + `cfg.mcp` (RT-4). |
|
|
253
|
-
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
## Cross-language usage
|
|
257
|
-
|
|
258
|
-
Not using Node.js? Spawn bare-agent as a subprocess from any language. Ready-made wrappers in [`contrib/`](contrib/README.md) for Python, Go, Rust, Ruby, and Java — copy one file, no package registry needed.
|
|
259
|
-
|
|
260
|
-
```python
|
|
261
|
-
# Python — 3 lines to run an agent
|
|
262
|
-
from bareagent import BareAgent
|
|
263
|
-
|
|
264
|
-
agent = BareAgent(provider="openai", model="gpt-4o-mini")
|
|
265
|
-
result = agent.run("What is the capital of France?")
|
|
266
|
-
print(result["text"]) # → "The capital of France is Paris."
|
|
267
|
-
agent.close()
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
```go
|
|
271
|
-
// Go — same pattern
|
|
272
|
-
agent, _ := bareagent.New("anthropic", "claude-haiku-4-5-20251001", "")
|
|
273
|
-
result, _ := agent.Run("What is the capital of France?")
|
|
274
|
-
fmt.Println(result.Text)
|
|
275
|
-
agent.Close()
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
# Ruby — same pattern
|
|
280
|
-
agent = BareAgent.new(provider: "ollama", model: "llama3.2")
|
|
281
|
-
result = agent.run("What is the capital of France?")
|
|
282
|
-
puts result["text"]
|
|
283
|
-
agent.close
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
All wrappers support optional event streaming for intermediate results. See [`contrib/README.md`](contrib/README.md) for Rust, Java, and full protocol reference.
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
## Production-validated
|
|
291
|
-
|
|
292
|
-
| Component | Aurora (SOAR2) | Multis (assistant) |
|
|
293
|
-
|---|:---:|:---:|
|
|
294
|
-
| Loop | ✓ | ✓ |
|
|
295
|
-
| Planner | ✓ | ✓ |
|
|
296
|
-
| runPlan | ✓ | — |
|
|
297
|
-
| Retry | ✓ | ✓ |
|
|
298
|
-
| CircuitBreaker | — | ✓ |
|
|
299
|
-
| Scheduler | — | ✓ |
|
|
300
|
-
| Checkpoint | — | ✓ |
|
|
301
|
-
| CLIPipe | ✓ | — |
|
|
302
|
-
|
|
303
|
-
Aurora replaced ~400 lines of hand-rolled orchestration with ~60 lines of bare-agent wiring — zero workarounds, zero framework plumbing, 100% domain logic.
|
|
304
|
-
|
|
305
|
-
For wiring recipes and API details, see the **[Integration Guide](bareagent.context.md)** (LLM-optimized). For the full human guide — usage patterns, composition examples, and what bare-agent deliberately doesn't build in (with recipes to do it yourself), see the **[Usage Guide](docs/archive/usage-guide.md)**. For error reference, see **[Error Guide](docs/product/errors.md)**. For release history, see **[CHANGELOG](CHANGELOG.md)**.
|
|
48
|
+
| Act | Loop | think → act → observe until done, any provider, opt-in policy/assemble/trim seams |
|
|
49
|
+
| Act | Planner + runPlan | break a goal into a step DAG, run steps in parallel waves |
|
|
50
|
+
| Act | assessComplexity | rate a goal from its text alone, no LLM — gates whether to plan |
|
|
51
|
+
| Act | recurse | RLM in one call — decompose → fan-out → verify → synthesize (see above) |
|
|
52
|
+
| Act | Memory | persist and recall across sessions — JSON, SQLite, or litectx in a one-line swap |
|
|
53
|
+
| Act | StateMachine | task lifecycle: pending → running → done / failed / waiting / cancelled |
|
|
54
|
+
| Act | Scheduler | cron or relative triggers, survives restarts |
|
|
55
|
+
| Act | Checkpoint | human approval gate, bring your own transport |
|
|
56
|
+
| Act | Spawn | fork a child agent, sharing one audit log and budget |
|
|
57
|
+
| Act | Defer | queue an action for a waker to fire later, governed on emit and on fire |
|
|
58
|
+
| Act | Retry · CircuitBreaker · Fallback | backoff with jitter, fail-fast, provider failover |
|
|
59
|
+
| Act | Stream · Errors | structured JSONL events, typed error hierarchy |
|
|
60
|
+
| Verify | Evaluator + refine | judge output by predicate, rubric, an agentic critic, or the Jev tier; refine loops generate → evaluate → regenerate |
|
|
61
|
+
| Verify | judge | a decisive return-time check of the result against the original request |
|
|
62
|
+
| Verify | remember | distill durable facts out of a finished run |
|
|
63
|
+
| Verify | SkillRegistry | surface extra tools on demand instead of loading them all upfront |
|
|
64
|
+
| Verify | stash | compact finished work out of the live context window, restorable |
|
|
65
|
+
| Verify | JevProvider | fast, cheap, calibrated yes/no · pick-one · score decisions — the System-1 gate (see above) |
|
|
66
|
+
| Govern | wireGate → bareguard | one policy, one audit log, one budget cap over every call; stops the spin on repeated denials or a stuck call |
|
|
67
|
+
| Hands | Browsing · Mobile · Shell · MCP Bridge | barebrowse, baremobile, cross-platform shell, and auto-discovered MCP servers — all as tools |
|
|
68
|
+
| Providers | OpenAI-compatible, Anthropic, Gemini, Ollama, CLIPipe, Fallback | swap freely, or bring your own with one `generate` method; CLIPipe runs the loop over a CLI subscription instead of the metered API |
|
|
69
|
+
|
|
70
|
+
## It tells you the truth
|
|
71
|
+
|
|
72
|
+
- A cut-off, refused, or over-context answer is an error, not a success — and its half-written tool calls never run.
|
|
73
|
+
- A cost it can't price is `null`, never a silent `$0`.
|
|
74
|
+
- A dead worker or broken check returns `incomplete`, never a faked pass — and counts are done by code, not by the model.
|
|
75
|
+
- Stuck loops stop: repeated denials or the same failing call end the run cleanly instead of burning the budget.
|
|
306
76
|
|
|
307
77
|
## The bare ecosystem
|
|
308
78
|
|
|
@@ -331,6 +101,13 @@ mix and match, each module works standalone.
|
|
|
331
101
|
|
|
332
102
|
**Why this exists:** Most automation stacks ship 200MB of opinions before you write a line of code. These don't. Install, import, go.
|
|
333
103
|
|
|
104
|
+
## Docs
|
|
105
|
+
|
|
106
|
+
- **Integration Guide** (`bareagent.context.md`) — the complete contract: every option, the full API, wiring recipes. Ships in the package.
|
|
107
|
+
- **Primitives manifest** (`primitives.json`) — every primitive as machine-readable JSON. Ships in the package, generated from source.
|
|
108
|
+
- **[Error Guide](docs/product/errors.md)** — what each error means and how to react to it.
|
|
109
|
+
- **[CHANGELOG](CHANGELOG.md)** — release history. Not on Node? See [`contrib/`](contrib/README.md) for wrappers in Python, Go, Rust, Ruby, and Java.
|
|
110
|
+
|
|
334
111
|
## License
|
|
335
112
|
|
|
336
113
|
Apache License, Version 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
package/bareagent.context.md
CHANGED
|
@@ -572,7 +572,34 @@ await loop.run(messages, tools, {
|
|
|
572
572
|
});
|
|
573
573
|
```
|
|
574
574
|
|
|
575
|
-
For routing rules that don't fit bareguard's primitives (e.g. "owner can do anything; user can only read"), you can layer a custom closure on top of `wireGate(gate).policy` — but the cleaner pattern is one source of truth: encode the rules as bareguard primitives and let the gate evaluate them.
|
|
575
|
+
For routing rules that don't fit bareguard's primitives (e.g. "owner can do anything; user can only read"), you can layer a custom closure on top of `wireGate(gate).policy` — but the cleaner pattern is one source of truth: encode the rules as bareguard primitives and let the gate evaluate them. The recipe below is exactly that kind of closure, for the specific "owner bypasses everything" case.
|
|
576
|
+
|
|
577
|
+
### Per-principal bypass (owner / admin role)
|
|
578
|
+
|
|
579
|
+
Wrap the gate policy when a principal is trusted unconditionally:
|
|
580
|
+
|
|
581
|
+
```js
|
|
582
|
+
const { policy: gatePolicy } = wireGate(gate);
|
|
583
|
+
|
|
584
|
+
const policy = async (toolName, args, ctx) => {
|
|
585
|
+
if (ctx?.role === 'owner') return true; // bypass gate entirely
|
|
586
|
+
return gatePolicy(toolName, args, ctx);
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
new Loop({ provider, policy, onLlmResult, onToolResult });
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
Bypassing the gate also bypasses audit and budget — only do this for principals you trust unconditionally. For partial trust, use ctx-aware rules inside bareguard instead.
|
|
593
|
+
|
|
594
|
+
### Custom deny strings (localize / strip prefix)
|
|
595
|
+
|
|
596
|
+
```js
|
|
597
|
+
const { policy } = wireGate(gate, {
|
|
598
|
+
formatDeny: (decision) => `Sorry — ${decision.reason || 'not allowed'}`,
|
|
599
|
+
});
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Halt-severity decisions bypass `formatDeny` (they throw `HaltError` and exit the loop without ever reaching the LLM).
|
|
576
603
|
|
|
577
604
|
### Catalog pre-filter (omit denied tools from the LLM's view)
|
|
578
605
|
|