bare-agent 0.12.1 → 0.13.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/README.md +4 -2
- package/bareagent.context.md +29 -4
- package/bin/cli.js +54 -2
- package/examples/README.md +15 -0
- package/examples/litectx-as-store.mjs +78 -0
- package/examples/litectx-mcp-child.mjs +57 -0
- package/examples/mcp-bridge-concurrent.js +106 -0
- package/examples/mcp-bridge-poc.js +77 -0
- package/examples/orchestrator/README.md +53 -0
- package/examples/orchestrator/orchestrator.json +14 -0
- package/examples/orchestrator/specialists/researcher.json +12 -0
- package/examples/orchestrator/specialists/summarizer.json +11 -0
- package/examples/replay-job.js +213 -0
- package/examples/wake.md +99 -0
- package/examples/wake.sh +84 -0
- package/examples/with-bareguard.mjs +65 -0
- package/index.d.ts +4 -1
- package/index.js +4 -0
- package/package.json +4 -2
- package/src/context-units.d.ts +44 -0
- package/src/context-units.js +225 -0
- package/src/loop.d.ts +11 -0
- package/src/loop.js +31 -1
- package/src/tools.d.ts +2 -1
- package/src/tools.js +2 -0
- package/tools/litectx-mcp.d.ts +28 -0
- package/tools/litectx-mcp.js +65 -0
package/README.md
CHANGED
|
@@ -66,13 +66,13 @@ Every piece works alone — take what you need, ignore the rest.
|
|
|
66
66
|
|
|
67
67
|
| Component | What it does |
|
|
68
68
|
|---|---|
|
|
69
|
-
| **Loop** | Think → act → observe → repeat. Calls any LLM, executes your tools, loops until done. Returns estimated USD cost per run. Governance via `Loop({ policy })` — wire bareguard's `Gate` through `wireGate(gate)` and every tool call (native, MCP, browsing, mobile) traverses one chokepoint with per-caller `ctx` routing. Bareguard owns the audit log, budget caps, and halt decisions; Loop respects the verdict. `onError` + `loop:error` surface every silent-ish failure (callback throw, Checkpoint timeout) |
|
|
69
|
+
| **Loop** | Think → act → observe → repeat. Calls any LLM, executes your tools, loops until done. Returns estimated USD cost per run. Governance via `Loop({ policy })` — wire bareguard's `Gate` through `wireGate(gate)` and every tool call (native, MCP, browsing, mobile) traverses one chokepoint with per-caller `ctx` routing. Bareguard owns the audit log, budget caps, and halt decisions; Loop respects the verdict. Context engineering via `Loop({ assemble })` — a per-round `assemble(msgs, ctx)` chokepoint to recall/compress/trim the window sent to the model (the seam litectx plugs into); returns a view, the canonical transcript stays intact, fail-open. The exported `unitAssembler`/`toUnits`/`fromUnits` adapter lets a consumer work over a neutral unit `{id, role, content, kind, pinned, atomic, tokensApprox}` — bareagent owns the grammar (atomic tool-pair bundling, pinned system/task, a pairing seatbelt), the consumer owns content + relevance. `onError` + `loop:error` surface every silent-ish failure (callback throw, Checkpoint timeout) |
|
|
70
70
|
| **Planner** | Break a goal into a step DAG via LLM. Built-in caching (`cacheTTL`) |
|
|
71
71
|
| **runPlan** | Execute steps in parallel waves. Dependency-aware, failure propagation, per-step retry |
|
|
72
72
|
| **Retry** | Exponential/linear backoff with jitter. Respects `err.retryable` |
|
|
73
73
|
| **CircuitBreaker** | Fail fast after N errors. Auto-recovers after cooldown. Per-key isolation |
|
|
74
74
|
| **Fallback** | Try providers in order — if one is down, next one picks up. Transparent to Loop |
|
|
75
|
-
| **Memory** | Persist and search context
|
|
75
|
+
| **Memory** | Persist and search context across turns/sessions through a swappable `Store`. Zero-dep JSON file by default, or mount [litectx](https://npmjs.com/package/litectx) for ranked, graph-aware recall in one line — the host code never changes ([example](examples/litectx-as-store.mjs)). A minimal `SQLite` FTS5 store also ships, though litectx supersedes it for SQLite-backed memory |
|
|
76
76
|
| **StateMachine** | Task lifecycle tracking with event hooks. `pending → running → done / failed / waiting / cancelled` |
|
|
77
77
|
| **Checkpoint** | Human approval gate. You provide the transport — terminal, Telegram, Slack, whatever |
|
|
78
78
|
| **Scheduler** | Cron (`0 9 * * 1-5`) or relative (`2h`, `30m`). Persisted jobs survive restarts |
|
|
@@ -182,6 +182,8 @@ Runnable scripts in [`examples/`](examples/) — each is self-contained and the
|
|
|
182
182
|
| [`orchestrator/`](examples/orchestrator/) | Multi-agent dispatch via `spawn`. Three configs, one system prompt — no orchestrator class, no role types. Roles are JSON files. |
|
|
183
183
|
| [`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. |
|
|
184
184
|
| [`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. |
|
|
185
|
+
| [`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). |
|
|
186
|
+
| [`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). |
|
|
185
187
|
|
|
186
188
|
---
|
|
187
189
|
|
package/bareagent.context.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# bareagent — Integration Guide
|
|
2
2
|
|
|
3
3
|
> For AI assistants and developers wiring bareagent into a project.
|
|
4
|
-
> v0.
|
|
4
|
+
> v0.13.0 | Node.js >= 18 | one required dep (`bareguard ^0.4.2`) | Apache 2.0
|
|
5
5
|
>
|
|
6
6
|
> Full human guide with composition examples, design philosophy, and recipes: [Usage Guide](docs/02-features/usage-guide.md)
|
|
7
7
|
|
|
@@ -14,12 +14,12 @@ npm install bare-agent
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Eight entry points:
|
|
17
|
-
- `require('bare-agent')` — Loop, Planner, StateMachine, Scheduler, Checkpoint, Memory, Stream, Retry, runPlan, CircuitBreaker, wireGate, defaultActionTranslator, BareAgentError, ProviderError, ToolError, TimeoutError, ValidationError, CircuitOpenError, **HaltError**
|
|
17
|
+
- `require('bare-agent')` — Loop, Planner, StateMachine, Scheduler, Checkpoint, Memory, Stream, Retry, runPlan, CircuitBreaker, wireGate, defaultActionTranslator, **toUnits, fromUnits, unitAssembler** (the `assemble` context-units adapter, v0.13+), BareAgentError, ProviderError, ToolError, TimeoutError, ValidationError, CircuitOpenError, **HaltError**
|
|
18
18
|
- `require('bare-agent/errors')` — same error classes via a stable subpath (v0.10.1+) for adopters who want to import only the error surface
|
|
19
19
|
- `require('bare-agent/providers')` — OpenAI, Anthropic, Ollama, CLIPipe, Fallback (the canonical short names; `*Provider` aliases — `OpenAIProvider`, `AnthropicProvider`, etc. — are also exported and match the class names, so either destructure works, v0.12.1+)
|
|
20
20
|
- `require('bare-agent/stores')` — SQLite (FTS5), JsonFile
|
|
21
21
|
- `require('bare-agent/transports')` — JsonlTransport
|
|
22
|
-
- `require('bare-agent/tools')` — createBrowsingTools, createMobileTools, createShellTools, createSpawnTool, createDeferTool, spawnChild, readDeferQueue
|
|
22
|
+
- `require('bare-agent/tools')` — createBrowsingTools, createMobileTools, createShellTools, createSpawnTool, createDeferTool, spawnChild, readDeferQueue, liteCtxMcpBridgeConfig
|
|
23
23
|
- `require('bare-agent/mcp')` — createMCPBridge (returns `tools` + `metaTools`), discoverServers, buildMetaTools
|
|
24
24
|
- `require('bare-agent/bareguard')` — wireGate (one-line bareguard Gate integration), defaultActionTranslator
|
|
25
25
|
|
|
@@ -69,6 +69,7 @@ Eight entry points:
|
|
|
69
69
|
| **Spawn a child specialist agent** | createSpawnTool + bin/cli.js --config (v0.9+) |
|
|
70
70
|
| **Defer an action for later (cron-fired)** | createDeferTool + examples/wake.sh (v0.9+) |
|
|
71
71
|
| **Expose a large MCP catalog dynamically** | createMCPBridge → bridge.metaTools (v0.9+) |
|
|
72
|
+
| **Give a child agent litectx memory (read-only, own db)** | liteCtxMcpBridgeConfig → `cfg.mcp` in a spawn child config (RT-4) |
|
|
72
73
|
|
|
73
74
|
**Most projects start with Loop + Provider.** Add components as needed.
|
|
74
75
|
|
|
@@ -549,15 +550,39 @@ All return `{ text, toolCalls, usage: { inputTokens, outputTokens } }`. CLIPipe
|
|
|
549
550
|
|
|
550
551
|
```javascript
|
|
551
552
|
// SQLite FTS5 — full-text search with BM25 ranking (requires: npm install better-sqlite3)
|
|
553
|
+
// Minimal store, kept for back-compat. litectx strictly dominates it (same better-sqlite3
|
|
554
|
+
// requirement, but adds ranked graph-aware recall) — prefer litectx for SQLite-backed memory.
|
|
552
555
|
new SQLite({ path: './memory.db' })
|
|
553
556
|
|
|
554
557
|
// JSON file — zero deps, substring search
|
|
555
558
|
new JsonFile({ path: './memory.json' })
|
|
556
559
|
|
|
560
|
+
// litectx — ranked, graph-aware recall (RT-3 mount; requires: npm install litectx)
|
|
561
|
+
// One-line swap; the host code (memory.store/search/get/delete) never changes.
|
|
562
|
+
// import { LiteCtx, liteCtxAsStore } from 'litectx';
|
|
563
|
+
// const memory = new Memory({ store: liteCtxAsStore(new LiteCtx({ dbPath: './agent.db' })) });
|
|
564
|
+
// See examples/litectx-as-store.mjs. litectx ships the adapter; bareagent owns the Store socket.
|
|
565
|
+
|
|
557
566
|
// Custom — implement { store, search, get, delete }
|
|
558
567
|
```
|
|
559
568
|
|
|
560
|
-
**JsonFile scaling:** `search()` is an O(n) substring scan (no index) and every `store()`/`delete()` rewrites the whole file. Fine for hundreds–low-thousands of entries; for larger or write-heavy memory
|
|
569
|
+
**JsonFile scaling:** `search()` is an O(n) substring scan (no index) and every `store()`/`delete()` rewrites the whole file. Fine for hundreds–low-thousands of entries; for larger or write-heavy memory mount `litectx` for ranked graph-aware recall (the minimal bundled `SQLite` FTS5 store remains for back-compat, but litectx strictly dominates it — same `better-sqlite3` requirement, richer recall). JsonFile warns once past ~10k entries.
|
|
570
|
+
|
|
571
|
+
**Two ways to use litectx, pick by who consumes it (the two are independent):**
|
|
572
|
+
- **As your `Store`** (RT-3, above) — *your* host code recalls via `memory.search/get`. One-line `liteCtxAsStore` swap.
|
|
573
|
+
- **As a child agent's MCP toolbox** (RT-4) — give a *spawned sub-agent* litectx's own reasoning verbs (`litectx_recall`, `litectx_get`, …) so the model calls them in its loop. Use `liteCtxMcpBridgeConfig` to build the curated mount and hand it to the child via `cfg.mcp`:
|
|
574
|
+
|
|
575
|
+
```js
|
|
576
|
+
const { liteCtxMcpBridgeConfig } = require('bare-agent/tools');
|
|
577
|
+
// Read-only by default: recall/get/impact/recent allowed; remember/forget denied
|
|
578
|
+
// (writable:true to opt in — writes stay in the child's OWN --root db); index/promotions always denied.
|
|
579
|
+
const mcp = liteCtxMcpBridgeConfig({ root: './child-mem' }); // own-db isolation via --root
|
|
580
|
+
// In the spawn child config (bin/cli.js --config): { provider, model, tools, mcp, gate }
|
|
581
|
+
// mcp: <the config above> → child's MCPBridge mounts litectx-mcp; tools join BEFORE gating
|
|
582
|
+
// cfg.mcp also accepts a directory-confined { bridgePath } pointing at a .mcp-bridge.json on disk.
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Requires litectx's `litectx-mcp` binary on PATH. Isolation is **physical** (each child a distinct `--root` db) — promotion to the parent is an explicit parent-side `recall`→`remember`, never automatic. See `examples/litectx-mcp-child.mjs`. bareagent imports nothing from litectx; the helper is pure config curation.
|
|
561
586
|
|
|
562
587
|
## Tool format
|
|
563
588
|
|
package/bin/cli.js
CHANGED
|
@@ -66,11 +66,54 @@ async function runConfigMode(cfgPath) {
|
|
|
66
66
|
const stream = new Stream({ transport: new JsonlTransport() });
|
|
67
67
|
|
|
68
68
|
// Provider
|
|
69
|
-
const provider = createProvider(cfg.provider || 'openai', cfg.model);
|
|
69
|
+
const provider = createProvider(cfg.provider || 'openai', cfg.model, { command: cfg.command, args: cfg.args });
|
|
70
70
|
|
|
71
71
|
// Tools — registry resolved by name from a curated set of built-ins.
|
|
72
72
|
const tools = await resolveTools(cfg.tools || [], { stream });
|
|
73
73
|
|
|
74
|
+
// Optional MCP mount (RT-4) — a child config can mount MCP servers (e.g. litectx-mcp, read-only on
|
|
75
|
+
// its own db) via `cfg.mcp`. Accepts an inline bridge config (`{ servers, ttl }`, as built by
|
|
76
|
+
// `liteCtxMcpBridgeConfig`) or `{ bridgePath }` pointing at one (confined to the config directory,
|
|
77
|
+
// same rule as gate.humanChannel). Mounted tools join the set BEFORE gating, so they traverse the
|
|
78
|
+
// same policy as native tools. The server `command` runs unsandboxed — same trust as `cfg.tools`.
|
|
79
|
+
/** @type {{ tools: ToolDef[], close: Function } | null} */
|
|
80
|
+
let mcpBridge = null;
|
|
81
|
+
if (cfg.mcp) {
|
|
82
|
+
const { createMCPBridge } = require('../src/mcp-bridge');
|
|
83
|
+
const os = require('node:os');
|
|
84
|
+
const cfgDir = path.resolve(path.dirname(cfgPath));
|
|
85
|
+
let bridgePath;
|
|
86
|
+
let tmpBridge = null;
|
|
87
|
+
if (cfg.mcp && typeof cfg.mcp === 'object' && cfg.mcp.servers) {
|
|
88
|
+
tmpBridge = path.join(os.tmpdir(), `bareagent-mcp-${process.pid}.json`);
|
|
89
|
+
fs.writeFileSync(tmpBridge, JSON.stringify(cfg.mcp));
|
|
90
|
+
bridgePath = tmpBridge;
|
|
91
|
+
} else if (cfg.mcp && typeof cfg.mcp.bridgePath === 'string') {
|
|
92
|
+
const p = path.resolve(cfgDir, cfg.mcp.bridgePath);
|
|
93
|
+
if (p !== cfgDir && !p.startsWith(cfgDir + path.sep)) {
|
|
94
|
+
process.stderr.write(`[cli] cfg.mcp.bridgePath must resolve inside the config directory (${cfgDir}); refusing ${p}\n`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
bridgePath = p;
|
|
98
|
+
} else {
|
|
99
|
+
process.stderr.write('[cli] cfg.mcp must be an inline bridge config ({ servers }) or { bridgePath }\n');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
mcpBridge = await createMCPBridge({
|
|
104
|
+
bridgePath,
|
|
105
|
+
servers: cfg.mcp.servers ? Object.keys(cfg.mcp.servers) : undefined,
|
|
106
|
+
timeout: cfg.mcp.timeout || 15000,
|
|
107
|
+
});
|
|
108
|
+
tools.push(...mcpBridge.tools);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
process.stderr.write(`[cli] failed to mount MCP (cfg.mcp): ${err.message}\n`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
} finally {
|
|
113
|
+
if (tmpBridge) { try { fs.unlinkSync(tmpBridge); } catch { /* best-effort */ } }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
74
117
|
// Bareguard Gate (optional but strongly recommended for spawn children).
|
|
75
118
|
// Fail-closed: if the config asks for a gate but wiring fails, exit non-zero
|
|
76
119
|
// rather than run an ungoverned child agent.
|
|
@@ -163,6 +206,7 @@ async function runConfigMode(cfgPath) {
|
|
|
163
206
|
});
|
|
164
207
|
|
|
165
208
|
await loop.run([initialMessage], gatedTools);
|
|
209
|
+
if (mcpBridge) await mcpBridge.close();
|
|
166
210
|
// Stream's loop:done event has already been emitted; exit clean.
|
|
167
211
|
process.exit(0);
|
|
168
212
|
}
|
|
@@ -299,7 +343,15 @@ function runStdioMode() {
|
|
|
299
343
|
* @param {string} [model]
|
|
300
344
|
* @returns {Provider}
|
|
301
345
|
*/
|
|
302
|
-
function createProvider(name, model) {
|
|
346
|
+
function createProvider(name, model, opts = {}) {
|
|
347
|
+
if (name === 'clipipe') {
|
|
348
|
+
const { CLIPipeProvider } = require('../src/provider-clipipe');
|
|
349
|
+
if (!opts.command) {
|
|
350
|
+
process.stderr.write('[cli] provider "clipipe" requires a `command` in the config (or --command).\n');
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
return new CLIPipeProvider({ command: opts.command, args: opts.args || [], ...(model && { model }) });
|
|
354
|
+
}
|
|
303
355
|
if (name === 'openai') {
|
|
304
356
|
const { OpenAIProvider } = require('../src/provider-openai');
|
|
305
357
|
return new OpenAIProvider({
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# examples
|
|
2
|
+
|
|
3
|
+
Runnable reference scripts for bare-agent. Each is self-contained — the top-of-file docstring documents flags and required env vars. These ship in the npm package, so you can copy them straight out of `node_modules/bare-agent/examples/`.
|
|
4
|
+
|
|
5
|
+
| Example | What it shows |
|
|
6
|
+
|---------|---------------|
|
|
7
|
+
| [`with-bareguard.mjs`](with-bareguard.mjs) | End-to-end Loop + bareguard wiring: budget cap, fs scope, bash allowlist, audit log, humanChannel. The canonical governed-loop reference. |
|
|
8
|
+
| [`mcp-bridge-poc.js`](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). |
|
|
9
|
+
| [`mcp-bridge-concurrent.js`](mcp-bridge-concurrent.js) | Soak test: fan out concurrent `barebrowse_browse` calls against real domains (Amazon, Wikipedia, GitHub, a dead host) and verify resilience. |
|
|
10
|
+
| [`orchestrator/`](orchestrator/) | Multi-agent dispatch via `spawn`. Three configs, one system prompt — no orchestrator class, no role types. Roles are JSON files. See its [README](orchestrator/README.md). |
|
|
11
|
+
| [`wake.sh`](wake.sh) + [`wake.md`](wake.md) | Reference cron + jq script for firing deferred actions. The runtime half of `createDeferTool` — bareagent emits, `wake.sh` fires. |
|
|
12
|
+
| [`replay-job.js`](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. |
|
|
13
|
+
| [`litectx-as-store.mjs`](litectx-as-store.mjs) | RT-3 Store mount: swap the zero-dep `JsonFileStore` for litectx's ranked, graph-aware recall in one line — the host code never changes. Runs the JsonFileStore half always; runs the litectx half if `litectx` is installed, else prints the one-line swap. |
|
|
14
|
+
|
|
15
|
+
For wiring recipes and API details see the [Integration Guide](../bareagent.context.md); for usage patterns and design philosophy see the [Usage Guide](../docs/02-features/usage-guide.md).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// examples/litectx-as-store.mjs
|
|
2
|
+
//
|
|
3
|
+
// RT-3 — mount litectx as a bareagent `Memory` backend (the rich `Store`).
|
|
4
|
+
//
|
|
5
|
+
// Run: node examples/litectx-as-store.mjs
|
|
6
|
+
// (zero-dep: runs the JsonFileStore half always; runs the litectx half only if `litectx`
|
|
7
|
+
// is installed — `npm install litectx` — otherwise it prints the one-line swap and skips.)
|
|
8
|
+
//
|
|
9
|
+
// What this demonstrates:
|
|
10
|
+
// - The `Store` socket (`{ store, search, get, delete }`) is litectx's documented mount point.
|
|
11
|
+
// Swapping the zero-dep JsonFileStore for litectx's ranked, graph-aware recall is a ONE-LINE
|
|
12
|
+
// change — the host code (everything in `hostWorkflow` below) never changes.
|
|
13
|
+
// - litectx ships the adapter (`liteCtxAsStore`); bareagent ships the socket. No bareagent import
|
|
14
|
+
// in litectx, no litectx import in bareagent — the dependency direction stays one-way.
|
|
15
|
+
//
|
|
16
|
+
// The five points where the schemaless socket and litectx's typed model are reconciled (PRD §3.2),
|
|
17
|
+
// all handled INSIDE litectx's adapter — the host never sees them:
|
|
18
|
+
// #1 the adapter mints the id (`kind:uuid`); the host supplies none.
|
|
19
|
+
// #2 `search` returns content inline via `recall({ body: true })`.
|
|
20
|
+
// #3 arbitrary host metadata round-trips through a sealed `meta` passthrough (kind/by are typed).
|
|
21
|
+
// #4 an un-kinded write defaults to `kind:"fact"` (durable agent memory); `metadata.kind` overrides.
|
|
22
|
+
// #5 `search` targets one kind so scores stay comparable across hits.
|
|
23
|
+
|
|
24
|
+
import { createRequire } from 'node:module';
|
|
25
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { tmpdir } from 'node:os';
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
29
|
+
const { Memory } = require('bare-agent');
|
|
30
|
+
const { JsonFile: JsonFileStore } = require('bare-agent/stores');
|
|
31
|
+
|
|
32
|
+
// --- the HOST workflow: written once, runs against ANY Store. This is the code that does NOT change
|
|
33
|
+
// when you swap the backend. Note `await` works whether the store is sync (JsonFileStore) or
|
|
34
|
+
// async (litectx) — Memory delegates the return value without awaiting. ---
|
|
35
|
+
async function hostWorkflow(memory, label) {
|
|
36
|
+
const id = await memory.store(
|
|
37
|
+
'the auth service uses a token-bucket rate limiter on /login',
|
|
38
|
+
{ sessionId: 'sess-1', tag: 'architecture' }, // arbitrary metadata — must survive the round-trip
|
|
39
|
+
);
|
|
40
|
+
const hits = await memory.search('rate limiter');
|
|
41
|
+
const fetched = memory.get(id);
|
|
42
|
+
|
|
43
|
+
console.log(`\n[${label}]`);
|
|
44
|
+
console.log(` store() → id: ${id}`);
|
|
45
|
+
console.log(` search('rate limiter') → ${hits.length} hit(s); top score=${hits[0]?.score?.toFixed?.(3) ?? hits[0]?.score}`);
|
|
46
|
+
console.log(` get(id).content: ${JSON.stringify(fetched?.content)}`);
|
|
47
|
+
console.log(` get(id).metadata: ${JSON.stringify(fetched?.metadata)} ← host metadata round-tripped`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
// 1) Zero-dep baseline: JsonFileStore (always runs).
|
|
52
|
+
const dir = mkdtempSync(join(tmpdir(), 'litectx-as-store-'));
|
|
53
|
+
try {
|
|
54
|
+
await hostWorkflow(new Memory({ store: new JsonFileStore({ path: join(dir, 'mem.json') }) }), 'JsonFileStore (zero-dep)');
|
|
55
|
+
} finally {
|
|
56
|
+
rmSync(dir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 2) The ONE-LINE swap to litectx — identical hostWorkflow, ranked graph-aware recall.
|
|
60
|
+
let liteCtxAsStore, LiteCtx;
|
|
61
|
+
try {
|
|
62
|
+
({ LiteCtx, liteCtxAsStore } = require('litectx')); // both from the main entry (litectx 0.10+)
|
|
63
|
+
} catch {
|
|
64
|
+
console.log('\n[litectx] not installed — the swap is one line:');
|
|
65
|
+
console.log(" import { LiteCtx, liteCtxAsStore } from 'litectx';");
|
|
66
|
+
console.log(' const lc = new LiteCtx({ dbPath: \'./agent.db\' }); await lc.ready();');
|
|
67
|
+
console.log(' const memory = new Memory({ store: liteCtxAsStore(lc) }); // ← only this line changes');
|
|
68
|
+
console.log('\n Install it (`npm install litectx`) to run the litectx half of this example.');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const lc = new LiteCtx({ dbPath: join(tmpdir(), `litectx-as-store-${process.pid}.db`) });
|
|
73
|
+
if (typeof lc.ready === 'function') await lc.ready();
|
|
74
|
+
await hostWorkflow(new Memory({ store: liteCtxAsStore(lc) }), 'litectx (ranked, graph-aware)');
|
|
75
|
+
if (typeof lc.close === 'function') lc.close();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
main().catch((err) => { console.error(err); process.exit(1); });
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// examples/litectx-mcp-child.mjs
|
|
2
|
+
//
|
|
3
|
+
// RT-4 — give a child/sub-agent litectx memory, READ-ONLY, on its own db (own-db isolation).
|
|
4
|
+
//
|
|
5
|
+
// Run: node examples/litectx-mcp-child.mjs [--root <indexed-litectx-root>]
|
|
6
|
+
// Prints the curated .mcp-bridge.json always; launches the real mount if `litectx-mcp` is on
|
|
7
|
+
// PATH (`npm install -g litectx`), otherwise prints the one-line recipe and exits 0.
|
|
8
|
+
//
|
|
9
|
+
// What this demonstrates:
|
|
10
|
+
// - `liteCtxMcpBridgeConfig({ root })` builds the curated bridge config: the read-only default
|
|
11
|
+
// (recall/get/impact/recent allow; remember/forget/index/promotions deny) so a child can reason
|
|
12
|
+
// over memory but can't mutate durable shared state. Flip with `{ writable: true }` to opt a child
|
|
13
|
+
// into writes — which still land in ITS OWN db, never the parent's.
|
|
14
|
+
// - `createMCPBridge` launches `litectx-mcp --root <child-db>` over stdio and exposes only the
|
|
15
|
+
// allowed verbs as bareagent tools. Pass `bridge.tools` to a child Loop.
|
|
16
|
+
// - Isolation is the child's own `--root`, not a shared store — which is what keeps RT-5's scope
|
|
17
|
+
// column deferred. Promotion of a child-learned fact to the parent is an explicit, parent-side
|
|
18
|
+
// `recall`(child db) → `remember`(parent db); never automatic.
|
|
19
|
+
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const { createMCPBridge } = require('../src/mcp-bridge');
|
|
23
|
+
const { liteCtxMcpBridgeConfig } = require('bare-agent/tools');
|
|
24
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { tmpdir } from 'node:os';
|
|
27
|
+
|
|
28
|
+
const rootFlag = process.argv.indexOf('--root');
|
|
29
|
+
const childRoot = rootFlag !== -1 ? process.argv[rootFlag + 1] : process.cwd();
|
|
30
|
+
|
|
31
|
+
// 1) Build the curated config — the artifact a parent drops in to compose the child's toolbox.
|
|
32
|
+
const cfg = liteCtxMcpBridgeConfig({ root: childRoot });
|
|
33
|
+
console.log('Curated .mcp-bridge.json (read-only litectx mount):');
|
|
34
|
+
console.log(JSON.stringify(cfg, null, 2));
|
|
35
|
+
console.log('\n allow: recall, get, impact, recent deny: remember, forget, index, promotions');
|
|
36
|
+
console.log(' (writable:true opts into remember/forget — still child-db-local)\n');
|
|
37
|
+
|
|
38
|
+
// 2) Attempt the real mount. Connects iff `litectx-mcp` is on PATH and the root is an indexed litectx db.
|
|
39
|
+
const dir = mkdtempSync(join(tmpdir(), 'litectx-mcp-child-'));
|
|
40
|
+
const bridgePath = join(dir, '.mcp-bridge.json');
|
|
41
|
+
writeFileSync(bridgePath, JSON.stringify(cfg));
|
|
42
|
+
try {
|
|
43
|
+
const bridge = await createMCPBridge({ bridgePath, servers: ['litectx'], timeout: 8000 });
|
|
44
|
+
if (bridge.servers.includes('litectx')) {
|
|
45
|
+
console.log(`[mounted] child tools: ${bridge.tools.map((t) => t.name).join(', ')}`);
|
|
46
|
+
console.log(`[mounted] withheld: ${bridge.denied.map((d) => d.tool).join(', ')}`);
|
|
47
|
+
// hand bridge.tools to a child Loop here: new Loop({ provider, ... }).run(msgs, bridge.tools)
|
|
48
|
+
await bridge.close();
|
|
49
|
+
} else {
|
|
50
|
+
console.log('[litectx-mcp not on PATH] the mount above is the recipe. To run it live:');
|
|
51
|
+
console.log(' npm install -g litectx # provides the `litectx-mcp` command');
|
|
52
|
+
console.log(` litectx-mcp --root ${childRoot} # (index the root first; see litectx docs)`);
|
|
53
|
+
console.log(' then re-run this example. The parent code never changes.');
|
|
54
|
+
}
|
|
55
|
+
} finally {
|
|
56
|
+
rmSync(dir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Concurrent MCP stress test — real domains, real payloads, varying complexity.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node examples/mcp-bridge-concurrent.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { readFileSync } = require('node:fs');
|
|
12
|
+
const { createMCPBridge } = require('../src/mcp-bridge');
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
console.log('Connecting to barebrowse...');
|
|
16
|
+
const bridge = await createMCPBridge({ servers: ['barebrowse'], timeout: 15000 });
|
|
17
|
+
|
|
18
|
+
if (bridge.servers.length === 0) {
|
|
19
|
+
console.error('No servers connected');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const browse = bridge.tools.find(t => t.name === 'barebrowse_browse');
|
|
24
|
+
|
|
25
|
+
const tasks = [
|
|
26
|
+
{
|
|
27
|
+
label: 'Amazon NL — 2.5 inch HDD SATA case',
|
|
28
|
+
args: { url: 'https://www.amazon.nl/s?k=2.5+inch+hdd+sata+case', maxChars: 3000 },
|
|
29
|
+
verify: (r) => /amazon/i.test(r) || /hdd|sata|case|behuizing/i.test(r),
|
|
30
|
+
shouldFail: false,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: 'Wikipedia — Phoenician language',
|
|
34
|
+
args: { url: 'https://en.wikipedia.org/wiki/Phoenician_language', maxChars: 3000 },
|
|
35
|
+
verify: (r) => /phoenician/i.test(r) || /semitic|canaanite/i.test(r),
|
|
36
|
+
shouldFail: false,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'Dead domain — should fail or timeout',
|
|
40
|
+
args: { url: 'https://this-domain-does-not-exist-xyz-999.com/page', maxChars: 1000 },
|
|
41
|
+
verify: () => true, // any response is fine, we just want to see it doesn't hang or crash others
|
|
42
|
+
shouldFail: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: 'GitHub — bare-agent repo',
|
|
46
|
+
args: { url: 'https://github.com/nicobailon/bareagent', maxChars: 2000 },
|
|
47
|
+
verify: (r) => /bare.?agent|orchestration|lightweight/i.test(r),
|
|
48
|
+
shouldFail: false,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'Slow static page — archive.org',
|
|
52
|
+
args: { url: 'https://web.archive.org/web/2024/https://example.com/', maxChars: 2000 },
|
|
53
|
+
verify: (r) => /example|wayback|archive/i.test(r),
|
|
54
|
+
shouldFail: false,
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
console.log(`\nFiring ${tasks.length} concurrent browse calls...\n`);
|
|
59
|
+
|
|
60
|
+
const t0 = Date.now();
|
|
61
|
+
const results = await Promise.allSettled(
|
|
62
|
+
tasks.map(t => browse.execute(t.args))
|
|
63
|
+
);
|
|
64
|
+
const elapsed = Date.now() - t0;
|
|
65
|
+
|
|
66
|
+
let passed = 0;
|
|
67
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
68
|
+
const task = tasks[i];
|
|
69
|
+
const r = results[i];
|
|
70
|
+
const status = r.status === 'fulfilled' ? 'OK' : 'FAIL';
|
|
71
|
+
const value = r.status === 'fulfilled' ? r.value : r.reason.message;
|
|
72
|
+
let text = typeof value === 'string' ? value : JSON.stringify(value);
|
|
73
|
+
// If barebrowse saved to disk, read the file to verify actual content
|
|
74
|
+
const fileMatch = text.match(/saved to (.+\.yml)/);
|
|
75
|
+
if (fileMatch) {
|
|
76
|
+
try { text += '\n' + readFileSync(fileMatch[1], 'utf8'); } catch {}
|
|
77
|
+
}
|
|
78
|
+
const correct = r.status === 'fulfilled' && task.verify(text);
|
|
79
|
+
|
|
80
|
+
console.log(`[${i + 1}] ${task.label}`);
|
|
81
|
+
if (task.shouldFail) {
|
|
82
|
+
const handled = r.status === 'rejected' || (r.status === 'fulfilled' && /error|fail|not|ERR_/i.test(text.slice(0, 500)));
|
|
83
|
+
console.log(` Status: ${status} | Error handled gracefully: ${handled ? 'YES' : 'NO'}`);
|
|
84
|
+
console.log(` Preview: ${(r.status === 'rejected' ? r.reason.message : text).slice(0, 120)}...`);
|
|
85
|
+
console.log();
|
|
86
|
+
if (handled) passed++;
|
|
87
|
+
} else {
|
|
88
|
+
console.log(` Status: ${status} | Routed correctly: ${correct ? 'YES' : 'NO'}`);
|
|
89
|
+
console.log(` Size: ${text.length} chars`);
|
|
90
|
+
console.log(` Preview: ${text.slice(0, 120)}...`);
|
|
91
|
+
console.log();
|
|
92
|
+
if (correct) passed++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`--- Result: ${passed}/${tasks.length} routed correctly in ${elapsed}ms ---`);
|
|
97
|
+
console.log(passed === tasks.length ? 'PASS' : 'FAIL');
|
|
98
|
+
|
|
99
|
+
await bridge.close();
|
|
100
|
+
console.log('Closed.');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
main().catch(err => {
|
|
104
|
+
console.error(err);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MCP Bridge POC — auto-discover MCP servers, expose as bareagent tools, run Loop.
|
|
6
|
+
*
|
|
7
|
+
* First run: discovers servers from IDE configs, writes .mcp-bridge.json (all tools allowed).
|
|
8
|
+
* Edit .mcp-bridge.json to deny tools. Changes survive refresh.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* OPENAI_API_KEY=sk-... node examples/mcp-bridge-poc.js Go to example.com and describe it
|
|
12
|
+
* ANTHROPIC_API_KEY=sk-... node examples/mcp-bridge-poc.js --provider anthropic What tools do you have?
|
|
13
|
+
* node examples/mcp-bridge-poc.js --refresh # force re-discovery
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { Loop } = require('../src/loop');
|
|
17
|
+
const { createMCPBridge } = require('../src/mcp-bridge');
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
|
|
22
|
+
// Parse flags
|
|
23
|
+
const providerFlag = args.includes('--provider') ? args.splice(args.indexOf('--provider'), 2)[1] : 'openai';
|
|
24
|
+
const refresh = args.includes('--refresh') ? (args.splice(args.indexOf('--refresh'), 1), true) : false;
|
|
25
|
+
|
|
26
|
+
// Everything left is the prompt
|
|
27
|
+
const prompt = args.join(' ') || 'List all your available tools and describe what each one does.';
|
|
28
|
+
|
|
29
|
+
// Provider
|
|
30
|
+
let provider;
|
|
31
|
+
if (providerFlag === 'anthropic') {
|
|
32
|
+
const { AnthropicProvider } = require('../src/provider-anthropic');
|
|
33
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
34
|
+
if (!apiKey) { console.error('Set ANTHROPIC_API_KEY'); process.exit(1); }
|
|
35
|
+
provider = new AnthropicProvider({ apiKey, model: 'claude-sonnet-4-20250514' });
|
|
36
|
+
} else {
|
|
37
|
+
const { OpenAIProvider } = require('../src/provider-openai');
|
|
38
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
39
|
+
if (!apiKey) { console.error('Set OPENAI_API_KEY'); process.exit(1); }
|
|
40
|
+
provider = new OpenAIProvider({ apiKey, model: 'gpt-4.1-mini' });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Bridge — reads .mcp-bridge.json if it exists, discovers on first run or TTL expiry
|
|
44
|
+
const bridge = await createMCPBridge({ refresh, timeout: 20000 });
|
|
45
|
+
|
|
46
|
+
if (bridge.servers.length === 0) {
|
|
47
|
+
console.error('No MCP servers found. Check your .mcp.json or ~/.claude/mcp_servers.json');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log();
|
|
52
|
+
console.log(`Prompt: ${prompt}`);
|
|
53
|
+
console.log('---');
|
|
54
|
+
|
|
55
|
+
const loop = new Loop({
|
|
56
|
+
provider,
|
|
57
|
+
maxRounds: 5,
|
|
58
|
+
system: `You are a helpful assistant with access to MCP tools. Use them to accomplish the user\'s request. Be concise.\n\n${bridge.systemContext}`,
|
|
59
|
+
onToolCall: (name, args) => console.log(`[tool] ${name}(${JSON.stringify(args)})`),
|
|
60
|
+
onText: (text) => console.log(`[response] ${text}`),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const result = await loop.run(
|
|
65
|
+
[{ role: 'user', content: prompt }],
|
|
66
|
+
bridge.tools,
|
|
67
|
+
);
|
|
68
|
+
console.log('---');
|
|
69
|
+
console.log('Done.', { usage: result.usage, cost: result.cost ? `$${result.cost.toFixed(4)}` : 'n/a' });
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error('Loop error:', err.message);
|
|
72
|
+
} finally {
|
|
73
|
+
await bridge.close();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# orchestrator/
|
|
2
|
+
|
|
3
|
+
Reference pattern for multi-agent dispatch. **Three configs and a system
|
|
4
|
+
prompt — no orchestrator class, no role types, no DAG runner.** The LLM
|
|
5
|
+
itself is the dispatcher; the only primitive is `spawn(config, input)`.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
orchestrator/
|
|
9
|
+
├── orchestrator.json # the parent agent — picks a specialist per job
|
|
10
|
+
└── specialists/
|
|
11
|
+
├── summarizer.json # specialist: summarise text given as input
|
|
12
|
+
└── researcher.json # specialist: shell_grep + shell_read across cwd
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Run it
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd examples/orchestrator
|
|
19
|
+
OPENAI_API_KEY=... echo '{"content":"Summarise the README in this repo."}' \
|
|
20
|
+
| bare-agent --config orchestrator.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The orchestrator's system prompt tells the model:
|
|
24
|
+
|
|
25
|
+
> You receive a job. Decide which specialist handles it (summarizer or
|
|
26
|
+
> researcher), `spawn` that specialist with the relevant input, and return
|
|
27
|
+
> the specialist's result.
|
|
28
|
+
|
|
29
|
+
The model picks `spawn(config: 'specialists/researcher.json', input: ...)`,
|
|
30
|
+
the researcher reads the README via `shell_read`, returns its summary, the
|
|
31
|
+
orchestrator returns that to stdout.
|
|
32
|
+
|
|
33
|
+
## What's gated
|
|
34
|
+
|
|
35
|
+
Both `orchestrator.json` and `specialists/*.json` declare a `gate` block
|
|
36
|
+
that wires bareguard. Defaults:
|
|
37
|
+
|
|
38
|
+
- `budget.maxCostUsd: 0.50` — hard cap on the *family* (parent + children
|
|
39
|
+
share via `BAREGUARD_BUDGET_FILE`).
|
|
40
|
+
- `limits.maxTurns: 20`, `limits.maxChildren: 3`, `limits.maxDepth: 2` —
|
|
41
|
+
spawn-tree shape bounds.
|
|
42
|
+
- `spawn.ratePerMinute: 5`, `defer.ratePerMinute: 10` — bareguard 0.2 rate
|
|
43
|
+
caps.
|
|
44
|
+
- `bash.allow` and `fs.readScope` scoped per specialist.
|
|
45
|
+
|
|
46
|
+
## Why this isn't a framework
|
|
47
|
+
|
|
48
|
+
There's no `class Orchestrator`, no `dispatch_to_specialist()`, no shared
|
|
49
|
+
state object. Roles are *configs*, not types. Adding a new specialist is
|
|
50
|
+
adding one JSON file — no code change to bareagent or the orchestrator.
|
|
51
|
+
|
|
52
|
+
The "intelligence" is the orchestrator's system prompt. Substitute a
|
|
53
|
+
better-written prompt and the same primitives handle a 5-specialist team.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"systemPrompt": "You are an orchestrator. You receive a job (the user's first message), decide which specialist handles it, then spawn that specialist with the relevant input and return its result.\n\nAvailable specialists (config paths relative to cwd):\n - specialists/summarizer.json — summarises text content given via input.content\n - specialists/researcher.json — searches and reads files in the project\n\nUse the spawn tool: spawn({ config: '<path>', input: { ...whatever the specialist expects... } }). The result.text from spawn is the specialist's final answer — return it verbatim or with a one-line preface.\n\nDo not attempt the work yourself; you are a router. If no specialist fits, say so plainly.",
|
|
3
|
+
"provider": "openai",
|
|
4
|
+
"model": "gpt-4o-mini",
|
|
5
|
+
"tools": ["spawn"],
|
|
6
|
+
"gate": {
|
|
7
|
+
"budget": { "maxCostUsd": 0.50 },
|
|
8
|
+
"limits": { "maxTurns": 20, "maxChildren": 3, "maxDepth": 2 },
|
|
9
|
+
"spawn": { "ratePerMinute": 5 },
|
|
10
|
+
"defer": { "ratePerMinute": 10 },
|
|
11
|
+
"tools": { "allowlist": ["spawn"] },
|
|
12
|
+
"audit": { "path": "./bareagent-audit.jsonl" }
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"systemPrompt": "You are a researcher. You have shell_read and shell_grep tools scoped to the current working directory. The user's message describes what to find or read. Use shell_grep to locate matches and shell_read to read whole files. Return a concise summary of what you found (3-8 sentences). Do not invent file contents; if you can't find something, say so.",
|
|
3
|
+
"provider": "openai",
|
|
4
|
+
"model": "gpt-4o-mini",
|
|
5
|
+
"tools": ["shell_read", "shell_grep"],
|
|
6
|
+
"gate": {
|
|
7
|
+
"limits": { "maxTurns": 10 },
|
|
8
|
+
"fs": { "readScope": ["."] },
|
|
9
|
+
"tools": { "allowlist": ["shell_read", "shell_grep"] },
|
|
10
|
+
"audit": { "path": "./bareagent-audit.jsonl" }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"systemPrompt": "You are a summarizer. The user's message contains text to summarise. Produce a concise (3-5 sentence) summary capturing the main points. Return the summary as your final response — no tool calls needed unless the input is malformed.",
|
|
3
|
+
"provider": "openai",
|
|
4
|
+
"model": "gpt-4o-mini",
|
|
5
|
+
"tools": [],
|
|
6
|
+
"gate": {
|
|
7
|
+
"limits": { "maxTurns": 5 },
|
|
8
|
+
"tools": { "allowlist": [] },
|
|
9
|
+
"audit": { "path": "./bareagent-audit.jsonl" }
|
|
10
|
+
}
|
|
11
|
+
}
|