@phuetz/code-buddy 1.2.0 → 1.3.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 +119 -24
- package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
- package/dist/agent/base-agent.d.ts +4 -0
- package/dist/agent/base-agent.js +6 -0
- package/dist/agent/facades/infrastructure-facade.d.ts +9 -2
- package/dist/agent/facades/infrastructure-facade.js +15 -6
- package/dist/agent/self-improvement/authored-artifact-gate.d.ts +18 -0
- package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
- package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
- package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
- package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
- package/dist/agent/self-improvement/authored-tool-store.js +57 -0
- package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
- package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
- package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
- package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
- package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
- package/dist/agent/self-improvement/self-knowledge.js +24 -0
- package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
- package/dist/agent/self-improvement/skill-benchmark.js +22 -0
- package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
- package/dist/agent/self-improvement/skill-consolidator.js +137 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
- package/dist/agent/self-improvement/skill-engine.js +87 -0
- package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
- package/dist/agent/self-improvement/skill-gate.js +62 -0
- package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
- package/dist/agent/self-improvement/skill-mutator.js +223 -0
- package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
- package/dist/agent/self-improvement/skill-proposer.js +82 -0
- package/dist/agent/self-improvement/skill-types.d.ts +41 -0
- package/dist/agent/self-improvement/skill-types.js +13 -0
- package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
- package/dist/agent/self-improvement/tool-benchmark.js +37 -0
- package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
- package/dist/agent/self-improvement/tool-engine.js +101 -0
- package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
- package/dist/agent/self-improvement/tool-gate.js +78 -0
- package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
- package/dist/agent/self-improvement/tool-proposer.js +34 -0
- package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
- package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
- package/dist/agent/self-improvement/tool-types.d.ts +48 -0
- package/dist/agent/self-improvement/tool-types.js +9 -0
- package/dist/agent/self-improvement/types.d.ts +3 -1
- package/dist/agent/tool-handler.js +3 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +6 -1
- package/dist/codebuddy/tools.d.ts +7 -0
- package/dist/codebuddy/tools.js +40 -0
- package/dist/commands/cli/improve-command.js +123 -0
- package/dist/commands/enhanced-command-handler.js +1 -1
- package/dist/commands/handlers/missing-handlers.d.ts +1 -1
- package/dist/commands/handlers/missing-handlers.js +26 -3
- package/dist/commands/skills-cli/index.js +123 -0
- package/dist/commands/slash/builtin-commands.js +1 -1
- package/dist/companion/percepts.js +11 -1
- package/dist/context/bootstrap-loader.js +6 -23
- package/dist/context/import-directive-parser.d.ts +4 -0
- package/dist/context/import-directive-parser.js +51 -6
- package/dist/context/instruction-excludes.d.ts +30 -1
- package/dist/context/instruction-excludes.js +71 -1
- package/dist/context/jit-context.d.ts +8 -10
- package/dist/context/jit-context.js +28 -106
- package/dist/context/project-context.d.ts +90 -0
- package/dist/context/project-context.js +295 -0
- package/dist/daemon/autonomous-loop.d.ts +31 -1
- package/dist/daemon/autonomous-loop.js +80 -2
- package/dist/harness/contract.d.ts +28 -28
- package/dist/identity/identity-manager.js +3 -2
- package/dist/index.js +17 -1
- package/dist/mcp/mcp-resources.js +2 -3
- package/dist/sensory/dreaming.d.ts +45 -0
- package/dist/sensory/dreaming.js +114 -0
- package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
- package/dist/sensory/heartbeat-scheduler.js +72 -0
- package/dist/sensory/reactions.d.ts +24 -0
- package/dist/sensory/reactions.js +31 -0
- package/dist/sensory/screen-reaction.d.ts +23 -0
- package/dist/sensory/screen-reaction.js +59 -0
- package/dist/sensory/sensory-bridge.d.ts +23 -0
- package/dist/sensory/sensory-bridge.js +85 -0
- package/dist/sensory/sensory-memory.d.ts +20 -0
- package/dist/sensory/sensory-memory.js +39 -0
- package/dist/sensory/speech-reaction.d.ts +21 -0
- package/dist/sensory/speech-reaction.js +83 -0
- package/dist/sensory/vision-reaction.d.ts +31 -0
- package/dist/sensory/vision-reaction.js +74 -0
- package/dist/server/index.js +89 -0
- package/dist/services/prompt-builder.d.ts +10 -0
- package/dist/services/prompt-builder.js +75 -9
- package/dist/skills/parser.js +3 -0
- package/dist/skills/skill-importer.d.ts +58 -0
- package/dist/skills/skill-importer.js +261 -0
- package/dist/skills/skill-sources.d.ts +20 -0
- package/dist/skills/skill-sources.js +102 -0
- package/dist/skills/types.d.ts +6 -0
- package/dist/tools/register-tool-handler.d.ts +25 -0
- package/dist/tools/register-tool-handler.js +100 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +8 -0
- package/dist/utils/init-project.d.ts +7 -0
- package/dist/utils/init-project.js +37 -0
- package/dist/utils/settings-manager.d.ts +12 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<p align="center">
|
|
18
18
|
<a href="https://github.com/phuetz/code-buddy/stargazers"><img src="https://img.shields.io/github/stars/phuetz/code-buddy?style=flat-square&logo=github&color=feca57&label=Star" alt="GitHub stars"/></a>
|
|
19
19
|
<img src="https://img.shields.io/badge/Tests-27K%2B-00d26a?style=flat-square&logo=jest" alt="Tests"/>
|
|
20
|
-
<img src="https://img.shields.io/badge/v1.
|
|
20
|
+
<img src="https://img.shields.io/badge/v1.3.1-GA-blueviolet?style=flat-square" alt="Version 1.3.1 GA"/>
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
<br/>
|
|
@@ -45,7 +45,7 @@ Watch a **local model reason on screen, then use real tools to do the work** —
|
|
|
45
45
|
[Proof ✅](docs/proof.md) ·
|
|
46
46
|
[Quick Start](#quick-start) ·
|
|
47
47
|
[In action](#in-action) ·
|
|
48
|
-
[
|
|
48
|
+
[What it does](#what-code-buddy-does) ·
|
|
49
49
|
[FAQ](docs/faq.md) ·
|
|
50
50
|
[Docs](#documentation) ·
|
|
51
51
|
[Contributing](#contributing)
|
|
@@ -62,6 +62,12 @@ An open-source, multi-provider AI coding agent with a terminal UI, an HTTP/WebSo
|
|
|
62
62
|
|
|
63
63
|
## In action
|
|
64
64
|
|
|
65
|
+
**It writes the code *and* the test, then runs it — `$0`.** Hand Code Buddy a task in the terminal; here Grok (a flat-fee subscription, no API key) writes FizzBuzz + a test and runs it green — then a human re-runs the test to confirm. Unedited:
|
|
66
|
+
|
|
67
|
+
<p align="center">
|
|
68
|
+
<img src="docs/assets/coding-demo.gif" alt="Code Buddy writes fizzbuzz.mjs and a test on Grok, runs it, and the test passes — $0, no API key" width="760"/>
|
|
69
|
+
</p>
|
|
70
|
+
|
|
65
71
|
**Free local AI, with the reasoning on screen.** A local Ollama model (`qwen3.6:35b-a3b`) thinks through a task, then *uses tools* to do it — no cloud, ~`$0.0001`. Unedited captures from the Cowork desktop app:
|
|
66
72
|
|
|
67
73
|
<table>
|
|
@@ -83,6 +89,12 @@ An open-source, multi-provider AI coding agent with a terminal UI, an HTTP/WebSo
|
|
|
83
89
|
<img src="docs/screenshots/chatgpt-oauth-login.png" alt="ChatGPT OAuth login flow" width="820"/>
|
|
84
90
|
</p>
|
|
85
91
|
|
|
92
|
+
**xAI / SuperGrok login** — `buddy login xai`, sign in once, then Grok answers for `$0` (flat-fee subscription, no API key):
|
|
93
|
+
|
|
94
|
+
<p align="center">
|
|
95
|
+
<img src="docs/assets/login-demo.gif" alt="After buddy login xai, Grok writes a haiku with no API key, $0 marginal" width="760"/>
|
|
96
|
+
</p>
|
|
97
|
+
|
|
86
98
|
**Self-audit.** Asked to find a bug in its own integration code, `gpt-5.5` reads `provider-chatgpt-responses.ts`, spots a stale-variable issue (mutated `body.model` not propagated), and proposes the exact fix:
|
|
87
99
|
|
|
88
100
|
<p align="center">
|
|
@@ -108,6 +120,23 @@ More desktop demos (Fleet, Autonomy, Companion, …) and captures: [`cowork/read
|
|
|
108
120
|
|
|
109
121
|
---
|
|
110
122
|
|
|
123
|
+
## Research — a sensory "nervous system" *(experimental)*
|
|
124
|
+
|
|
125
|
+
Toward the long-term companion/robot vision, [`buddy-sense/`](buddy-sense/) is a **Rust, event-driven perception layer**. Parallel **sense modules** (audio VAD — energy or Silero neural; an autonomic **heartbeat**; screen via `xcap`; UI focus via AT-SPI) feed a **thalamus** that gates + coalesces the stream and broadcasts it over a loopback WebSocket into Code Buddy's event bus — where the heartbeat **paces background memory consolidation** ("dreaming", inspired by OpenClaw). Local, `$0`, permissive deps only (clean-room — no proprietary code copied).
|
|
126
|
+
|
|
127
|
+
<p align="center"><img src="buddy-sense/docs/architecture.svg" alt="buddy-sense nervous-system architecture: senses → thalamus → bridge → Code Buddy event bus" width="840"/></p>
|
|
128
|
+
|
|
129
|
+
**Honestly experimental** — distinct from the GA core above: the default daemon emits the heartbeat (+ audio from a WAV file); the live camera/mic aren't wired into the daemon yet. `speech → STT → 'hearing' percept` **is** wired (faster-whisper), with a hook left for driving a full agent turn. What's real today: the pure detector cores + thalamus + bridge are unit-tested (`cargo test`, 20 tests, no hardware), and the loopback bridge → event bus → reaction path (incl. the speech transcription) is covered on the Code Buddy side.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
cd buddy-sense && cargo test # 20 tests, no hardware
|
|
133
|
+
./buddy-sense/demo.sh # headless end-to-end: heartbeat + audio VAD → Code Buddy
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Design, the five sense modules, the opt-in features, and the diagrams: [`buddy-sense/README.md`](buddy-sense/README.md).
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
111
140
|
## Quick Start
|
|
112
141
|
|
|
113
142
|
```bash
|
|
@@ -154,6 +183,18 @@ buddy llm ensemble "is this approach sound?" # ask ChatGPT + Grok + Ollama toget
|
|
|
154
183
|
CODEBUDDY_LLM_FAILOVER=1 buddy -p "…" # if the primary errors, auto-continue on the next active LLM
|
|
155
184
|
```
|
|
156
185
|
|
|
186
|
+
<p align="center">
|
|
187
|
+
<img src="docs/assets/llm-demo.gif" alt="buddy llm lists your active LLMs, then auto-fails over from Grok to ChatGPT when the primary errors" width="760"/>
|
|
188
|
+
<br/>
|
|
189
|
+
<sub>Your logins at a glance — and automatic failover from one to the next when one has a problem, at <code>$0</code>. Real run, unedited.</sub>
|
|
190
|
+
</p>
|
|
191
|
+
|
|
192
|
+
<p align="center">
|
|
193
|
+
<img src="docs/assets/ensemble-demo.gif" alt="buddy llm ensemble asks ChatGPT, Ollama and Grok the same question, then synthesizes one answer" width="760"/>
|
|
194
|
+
<br/>
|
|
195
|
+
<sub><code>buddy llm ensemble</code> — every brain you're logged into answers, then it's synthesized into one. Real run, unedited.</sub>
|
|
196
|
+
</p>
|
|
197
|
+
|
|
157
198
|
See [Getting Started](docs/getting-started.md) for install options, headless mode, sessions, and typical workflows.
|
|
158
199
|
|
|
159
200
|
---
|
|
@@ -168,10 +209,22 @@ Cowork is the desktop cockpit for Code Buddy: chat, tools, traces, workflows, se
|
|
|
168
209
|
<sub>Real <code>gpt-5.5</code> in the Cowork desktop app — the answer streams in, cost <code>$0.0000</code>. <a href="docs/qa/code-buddy-studio/showcase-2026-06-16/cowork-chat-stream.mp4">MP4 →</a></sub>
|
|
169
210
|
</p>
|
|
170
211
|
|
|
212
|
+
<p align="center">
|
|
213
|
+
<a href="docs/assets/cowork-chat-demo.mp4"><img src="docs/assets/cowork-chat-demo.gif" alt="A local reasoning model thinks through a haiku on screen in the Cowork desktop app, $0" width="760"/></a>
|
|
214
|
+
<br/>
|
|
215
|
+
<sub>…and fully local: a reasoning model (<code>qwen3.6:35b-a3b</code>) <b>thinks on screen</b>, then answers — no cloud, <code>$0</code>. <a href="docs/assets/cowork-chat-demo.mp4">MP4 →</a></sub>
|
|
216
|
+
</p>
|
|
217
|
+
|
|
218
|
+
<p align="center">
|
|
219
|
+
<a href="docs/assets/cowork-panels-demo.mp4"><img src="docs/assets/cowork-panels-demo.gif" alt="The Cowork left rail opens the Autonomy dashboard, Memory and other panels as dock tabs" width="760"/></a>
|
|
220
|
+
<br/>
|
|
221
|
+
<sub>The left rail opens every panel as a dock tab — here the <b>Autonomy dashboard</b> (24/7 daemon, free-first model ladder, live subagents) and <b>Project Memory</b>. <a href="docs/assets/cowork-panels-demo.mp4">MP4 →</a></sub>
|
|
222
|
+
</p>
|
|
223
|
+
|
|
171
224
|
<table>
|
|
172
225
|
<tr>
|
|
173
|
-
<td width="50%" align="center"><img src="docs/
|
|
174
|
-
<td width="50%" align="center"><img src="docs/
|
|
226
|
+
<td width="50%" align="center"><img src="docs/assets/cowork-welcome.png" alt="Cowork desktop home with the expanded left menu and quick-action cards" width="430"/><br/><sub>Home — expanded menu, quick-action cards, gradient hero</sub></td>
|
|
227
|
+
<td width="50%" align="center"><img src="docs/assets/cowork-panel.png" alt="A launcher opens its panel as a tab — here the Autonomy dashboard" width="430"/><br/><sub>A launcher opens its panel as a tab — here the Autonomy dashboard (daemon, model ladder, subagents)</sub></td>
|
|
175
228
|
</tr>
|
|
176
229
|
<tr>
|
|
177
230
|
<td width="50%" align="center"><img src="docs/qa/code-buddy-studio/showcase-2026-06-16/03-fleet-autonomy.png" alt="Fleet and autonomy dashboard" width="430"/><br/><sub>Fleet dispatch · tool-permission posture · Hermes toolsets</sub></td>
|
|
@@ -226,26 +279,68 @@ The CLI guards this: on Node < 22, `buddy gui` prints a clear upgrade message in
|
|
|
226
279
|
|
|
227
280
|
---
|
|
228
281
|
|
|
229
|
-
##
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
|
234
|
-
|
|
235
|
-
|
|
|
236
|
-
|
|
|
237
|
-
|
|
|
238
|
-
|
|
|
239
|
-
|
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
247
|
-
|
|
|
248
|
-
|
|
|
282
|
+
## What Code Buddy does
|
|
283
|
+
|
|
284
|
+
Code Buddy is one engine — terminal, desktop, and HTTP — that an LLM drives to read code, edit files, run commands, search the web, open PRs, and plan complex work. Below is the whole surface, explained. Jump to any area:
|
|
285
|
+
|
|
286
|
+
| Area | In one line | Deep dive |
|
|
287
|
+
|:-----|:------------|:----------|
|
|
288
|
+
| [Providers & login](#providers--login) | 15 LLM providers + ChatGPT/xAI login at **$0** flat-fee, auto-failover, ensembles | [providers.md](docs/providers.md) |
|
|
289
|
+
| [The agentic loop](#the-agentic-loop) | autonomous tool-calling with a middleware pipeline + confirm-before-execute | [CLAUDE.md](CLAUDE.md) |
|
|
290
|
+
| [~110 tools](#110-tools) | edit/shell/web/browser/docs/media, RAG-selected, 5-strategy edit matching | [tools-reference.md](docs/tools-reference.md) |
|
|
291
|
+
| [Reasoning](#reasoning) | extended thinking + Tree-of-Thought / MCTS, `/think` | [reasoning.md](docs/reasoning.md) |
|
|
292
|
+
| [Goal loops & autonomy](#goal-loops--autonomy) | Ralph loop + LLM judge, YOLO, a 24/7 daemon | [fleet-guide.md](docs/fleet-guide.md) |
|
|
293
|
+
| [Multi-AI Fleet](#multi-ai-fleet) | peers call each other's models + read-only tools over WebSocket | [fleet-guide.md](docs/fleet-guide.md) |
|
|
294
|
+
| [Self-improvement](#self-improvement) | authors + empirically gates its own tools/skills (opt-in) | [CLAUDE.md](CLAUDE.md) |
|
|
295
|
+
| [Skills](#skills) | 40 bundled (Office/research/automation) + authored + imported, firewalled | [commands.md](docs/commands.md) |
|
|
296
|
+
| [Memory & context](#memory--context) | compression, importance-weighted window, JIT project context | [context-engine.md](docs/context-engine.md) |
|
|
297
|
+
| [Security & sandboxing](#security--sandboxing) | Guardian risk-scorer, permission modes, sandbox tiers, SSRF guard, secrets | [security.md](docs/security.md) |
|
|
298
|
+
| [Server & infrastructure](#server--infrastructure) | OpenAI-compatible HTTP, WS gateway, daemon, cron | [infrastructure.md](docs/infrastructure.md) |
|
|
299
|
+
| [Channels](#channels) | 20+ messaging platforms with DM-pairing access control | [channels.md](docs/channels.md) |
|
|
300
|
+
| [Git & code intelligence](#git--code-intelligence) | auto-commit, `/pr`, LSP rename, bug finder, the Code Explorer graph | [development.md](docs/development.md) |
|
|
301
|
+
| [Config & modes](#config--modes) | TOML profiles, permission/agent/security modes, model-aware limits | [configuration.md](docs/configuration.md) |
|
|
302
|
+
|
|
303
|
+
### Providers & login
|
|
304
|
+
Code Buddy talks to **15 LLM providers** through one OpenAI-compatible dispatcher (`src/codebuddy/client.ts`), picking exactly one strategy at startup: Grok, Claude, GPT, Gemini, Ollama, LM Studio, AWS Bedrock, Azure, Groq, Together, Fireworks, OpenRouter, vLLM, Copilot, Mistral. **`buddy login`** signs into a ChatGPT Plus/Pro subscription (routed via OpenAI's Codex Responses backend) and **`buddy login xai`** into SuperGrok — both **flat-fee, no API key, cost reported `$0.0000`** (no per-token metering). Multiple logins coexist: **`buddy llm`** lists them, **`buddy llm ensemble "<q>"`** asks them all and synthesizes one answer, and `CODEBUDDY_LLM_FAILOVER=1` auto-continues on the next active LLM when one errors (per-provider circuit breakers). `[model_pairs]` in TOML splits an *architect* and *editor* model.
|
|
305
|
+
|
|
306
|
+
### The agentic loop
|
|
307
|
+
The core is a stateful multi-turn loop (`src/agent/execution/agent-executor.ts`, `runTurnLoop`): the LLM proposes tool calls, the executor validates + confirms + runs them, feeds results back, and loops until done or you stop. A **middleware pipeline** (`src/agent/middleware/`) adds turn/cost limits, reasoning injection, workflow guards, auto-repair, and quality gates in priority order. Before any risky action the **ConfirmationService** checks permission mode → declarative rules → session flags → the Guardian Agent, and fail-closed guards block catastrophic commands (`rm -rf /`, fork bombs, `drop database`). Run it interactively (`buddy`), one-shot (`buddy -p "<task>"`), or fully autonomous (`buddy --yolo`).
|
|
308
|
+
|
|
309
|
+
### ~110 tools
|
|
310
|
+
The agent has **~110 tools** — file edit, shell, web search (5-provider fallback), a real headless browser, PDF/Office, media/vision, code-exec, agent orchestration — and uses **RAG selection** to send only the relevant ones each turn (BM25 `tool_search` as fallback). Edits land even in refactored code via a **5-strategy cascade**: exact → flexible (trim/indent) → regex (tokenized) → fuzzy (Levenshtein 10%) → LCS (90%). It also speaks Codex-style **`apply_patch`**, and `code_exec` runs LLM-written JavaScript in a `vm` sandbox (no `process`/`require`, 30s). Extend it with MCP servers (auto-discovered from `.codebuddy/mcp.json`), plugins, or new tool classes.
|
|
311
|
+
|
|
312
|
+
### Reasoning
|
|
313
|
+
Two systems: **Extended Thinking** (provider budget tokens — off/minimal/low/medium/high/xhigh) and Code Buddy's own **Tree-of-Thought + MCTS** with four depths (shallow CoT → beam search → MCTS → exhaustive). A reasoning middleware auto-detects complex queries and injects guidance; `/think`, `/megathink`, and `/ultrathink` set the depth, and the `reason` tool streams its search. (MCTSr Q-value `Q(a) = 0.5·(min(R) + mean(R))`.)
|
|
314
|
+
|
|
315
|
+
### Goal loops & autonomy
|
|
316
|
+
A **goal loop** is autonomy with a referee: the agent acts, an LLM **judge** checks the goal after each turn, and it self-corrects until done or the turn budget runs out — no hand-written retry logic. Drive it with `/goal "<objective>"` + `/subgoal` (numbered criteria), or headless `buddy goal`. **`buddy --yolo`** grants 400 tool rounds under a `$100` cap with guardrails, and the **24/7 autonomous daemon** (`buddy autonomy install`) claims tasks from a shared queue and runs them free-first (local → Tailscale → paid).
|
|
317
|
+
|
|
318
|
+
### Multi-AI Fleet
|
|
319
|
+
Run several Code Buddy instances as **peers on a WebSocket mesh** that observe each other's events live and call each other's models + read-only tools: `peer.chat` (one-shot), `peer.chat-session.*` (multi-turn, persisted), and `peer.tool.invoke` (remote read-only tools, behind three security gates that fail closed). **`/fleet route "<prompt>"`** classifies a task, gathers peer capabilities, runs a privacy lint (SSN/IBAN/card detection), and recommends a delegation; `/fleet listen|send|status|history` manage the mesh. It interops over A2A + ACP + MCP.
|
|
320
|
+
|
|
321
|
+
### Self-improvement
|
|
322
|
+
An empirically-gated loop (`src/agent/self-improvement/`) that improves Code Buddy's *learned* layer — never its own `src/`. It can author its own **lessons**, **tools** (`register_tool`, sandboxed, namespaced `authored__*`), and **skills**, each passing a gate before it's kept: tools must pass **held-out** behavioral cases the proposer never sees (a tool that hardcodes the visible answers fails fresh inputs → rejected), and skills pass a prompt-injection **firewall**. It's **opt-in and off by default** (`CODEBUDDY_SELF_IMPROVE=true`); `buddy improve status|cycle|tools|skills` drive it.
|
|
323
|
+
|
|
324
|
+
### Skills
|
|
325
|
+
Skills are procedural guidance (Markdown + frontmatter + triggers) the agent discovers and injects by topic. **40 are bundled**, including ones that build real Office docs and run analysis in *visible* Python steps (preflight libs → write script → run → verify): `xlsx`/`docx`/`pptx`, `doc-ingest` (PDF/Office → Markdown), `data-charts` (pandas/matplotlib), `web-automate` (Playwright), `web-research` (cited briefs). The agent can also **author** its own skills and **import** external ones from Hermes / OpenClaw — every imported skill is scanned by a **firewall** that quarantines prompt-injection/exfiltration payloads (`buddy skills import|imported|list`).
|
|
326
|
+
|
|
327
|
+
### Memory & context
|
|
328
|
+
For long sessions, `ContextManagerV2` compresses with a sliding window + **importance-weighted scoring** (errors 0.95, decisions 0.90, code 0.70, chat 0.25 — high-value messages survive truncation), masks old tool output, prunes stale images, and repairs the transcript after compaction. **JIT context** loads nearby `CODEBUDDY.md`/`CONTEXT.md`/`AGENTS.md` files when a tool touches a path, and each turn injects `<lessons_context>` and `<todo_context>`. Durable facts persist to bounded project/user memory (`/memory recent|remember|recall`), security-scanned against injection/secret-exfiltration.
|
|
329
|
+
|
|
330
|
+
### Security & sandboxing
|
|
331
|
+
Layered, fail-closed safety: the **Guardian Agent** scores each operation 0–100 (auto-approve <80, prompt 80–90, deny ≥90; read-only tools skip the LLM call), **permission modes** (`plan`/`acceptEdits`/`dontAsk`/`bypassPermissions`), **sandbox tiers** (read-only / workspace-write / full-access via bubblewrap·landlock·seatbelt, with `.git`/`.ssh`/`.aws` always read-only), an **SSRF guard** (blocks private ranges + IPv4/IPv6 bypass vectors with a DNS check before every fetch), an AES-256-GCM **secrets vault** (`buddy secrets`), a **write policy** (`strict` forces `apply_patch`), and an **output sanitizer** that strips model-leakage tokens.
|
|
332
|
+
|
|
333
|
+
### Server & infrastructure
|
|
334
|
+
**`buddy server`** exposes an HTTP API (port 3000) including an **OpenAI-compatible `/api/chat/completions`**, plus a **WebSocket gateway** (3001) for desktop/mobile clients (device pairing, presence, Origin-hardened, JWT in production). A **daemon** runs 24/7 with auto-restart, a heartbeat checklist, daily session reset, and a cross-platform service installer (systemd/launchd/Task Scheduler). **Cron** scheduling (`buddy cron add`) supports no-LLM `--watchdog` monitors and `--pre-check` gates so an expensive LLM run only fires when something actually changed.
|
|
335
|
+
|
|
336
|
+
### Channels
|
|
337
|
+
Code Buddy runs on **20+ messaging platforms** — Telegram, Discord, Slack, WhatsApp, Signal, Matrix, IRC, Nostr, Mattermost, Nextcloud Talk, iMessage (real persistent transports with auto-reconnect) plus REST/webhook adapters (Teams, Google Chat, Feishu, LINE, ntfy, DingTalk, WeCom, …). **DM pairing** prevents unauthorized credit burn: an unknown user gets a 6-char code (15-min TTL) you approve via `buddy pairing approve`. (A few niche adapters — Twitch/Tlon/Gmail — are in-process stubs, and Feishu real-time *inbound* needs the Lark SDK installed.)
|
|
338
|
+
|
|
339
|
+
### Git & code intelligence
|
|
340
|
+
`buddy dev run` plans + implements + tests + **auto-commits** with a Conventional-Commit message; **`/pr`** opens a summarized PR; `lsp_rename`/`lsp_code_action` drive language servers for safe refactors; the **bug finder** flags 25+ patterns across 6 languages. For whole-repo understanding, the optional **Code Explorer** (the `gitnexus` MCP server) pre-indexes the repo into a knowledge graph with 31 tools for impact/blast-radius, coupling, hotspots, and execution traces. Code Buddy also runs as an **ACP** agent (`buddy acp`) so editors like Zed can drive it natively.
|
|
341
|
+
|
|
342
|
+
### Config & modes
|
|
343
|
+
Configure via env vars, **TOML profiles** (`[profiles.<name>]`, `buddy --profile`), and per-project `.codebuddy/settings.json`. **Permission modes** gate approvals, **agent modes** (`plan`/`code`/`ask`/`architect`) restrict the tool surface, and **security modes** (`suggest`/`auto-edit`/`full-auto`) tune the approval flow. Per-model capabilities (context window, max output, patch format) live in `src/config/model-tools.ts`. The UI ships in **English and French (complete)**; `de`/`es`/`ja`/`zh` are registered locale scaffolds that currently fall back to English.
|
|
249
344
|
|
|
250
345
|
---
|
|
251
346
|
|
|
@@ -58,15 +58,15 @@ export declare const agenticCodingApprovalDecisionSchema: z.ZodObject<{
|
|
|
58
58
|
}, "strict", z.ZodTypeAny, {
|
|
59
59
|
reason: string;
|
|
60
60
|
decision: "rejected" | "approved";
|
|
61
|
-
kind: "agentic-coding-approval-decision";
|
|
62
61
|
schemaVersion: 1;
|
|
62
|
+
kind: "agentic-coding-approval-decision";
|
|
63
63
|
reviewer: string;
|
|
64
64
|
decidedAt?: string | undefined;
|
|
65
65
|
}, {
|
|
66
66
|
reason: string;
|
|
67
67
|
decision: "rejected" | "approved";
|
|
68
|
-
kind: "agentic-coding-approval-decision";
|
|
69
68
|
schemaVersion: 1;
|
|
69
|
+
kind: "agentic-coding-approval-decision";
|
|
70
70
|
reviewer: string;
|
|
71
71
|
decidedAt?: string | undefined;
|
|
72
72
|
}>;
|
|
@@ -108,6 +108,7 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
108
108
|
risks: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
109
109
|
}, "strict", z.ZodTypeAny, {
|
|
110
110
|
summary: string;
|
|
111
|
+
schemaVersion: 1;
|
|
111
112
|
kind: "agentic-coding-workflow-builder-proposal";
|
|
112
113
|
nodes: {
|
|
113
114
|
type: "action" | "trigger" | "logic";
|
|
@@ -116,7 +117,6 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
116
117
|
description: string;
|
|
117
118
|
agenticType: "edit" | "analysis" | "handoff" | "approval" | "verification" | "gate";
|
|
118
119
|
}[];
|
|
119
|
-
schemaVersion: 1;
|
|
120
120
|
edges: {
|
|
121
121
|
source: string;
|
|
122
122
|
target: string;
|
|
@@ -126,6 +126,7 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
126
126
|
coworkVisualizationNotes: string[];
|
|
127
127
|
}, {
|
|
128
128
|
summary: string;
|
|
129
|
+
schemaVersion: 1;
|
|
129
130
|
kind: "agentic-coding-workflow-builder-proposal";
|
|
130
131
|
nodes: {
|
|
131
132
|
type: "action" | "trigger" | "logic";
|
|
@@ -134,7 +135,6 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
134
135
|
description: string;
|
|
135
136
|
agenticType: "edit" | "analysis" | "handoff" | "approval" | "verification" | "gate";
|
|
136
137
|
}[];
|
|
137
|
-
schemaVersion: 1;
|
|
138
138
|
edges: {
|
|
139
139
|
source: string;
|
|
140
140
|
target: string;
|
|
@@ -144,6 +144,7 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
144
144
|
coworkVisualizationNotes?: string[] | undefined;
|
|
145
145
|
}>, {
|
|
146
146
|
summary: string;
|
|
147
|
+
schemaVersion: 1;
|
|
147
148
|
kind: "agentic-coding-workflow-builder-proposal";
|
|
148
149
|
nodes: {
|
|
149
150
|
type: "action" | "trigger" | "logic";
|
|
@@ -152,7 +153,6 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
152
153
|
description: string;
|
|
153
154
|
agenticType: "edit" | "analysis" | "handoff" | "approval" | "verification" | "gate";
|
|
154
155
|
}[];
|
|
155
|
-
schemaVersion: 1;
|
|
156
156
|
edges: {
|
|
157
157
|
source: string;
|
|
158
158
|
target: string;
|
|
@@ -162,6 +162,7 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
162
162
|
coworkVisualizationNotes: string[];
|
|
163
163
|
}, {
|
|
164
164
|
summary: string;
|
|
165
|
+
schemaVersion: 1;
|
|
165
166
|
kind: "agentic-coding-workflow-builder-proposal";
|
|
166
167
|
nodes: {
|
|
167
168
|
type: "action" | "trigger" | "logic";
|
|
@@ -170,7 +171,6 @@ export declare const agenticCodingWorkflowBuilderProposalSchema: z.ZodEffects<z.
|
|
|
170
171
|
description: string;
|
|
171
172
|
agenticType: "edit" | "analysis" | "handoff" | "approval" | "verification" | "gate";
|
|
172
173
|
}[];
|
|
173
|
-
schemaVersion: 1;
|
|
174
174
|
edges: {
|
|
175
175
|
source: string;
|
|
176
176
|
target: string;
|
|
@@ -191,6 +191,10 @@ export declare abstract class BaseAgent extends EventEmitter implements Agent {
|
|
|
191
191
|
getMCPTools(): Promise<Map<string, unknown[]>>;
|
|
192
192
|
getMCPClient(): MCPClient;
|
|
193
193
|
protected initializeMCP(): void;
|
|
194
|
+
/** Resolves once MCP server initialization has settled (or no-op if MCP was
|
|
195
|
+
* never started). Headless/one-shot callers await this so the gitnexus-style
|
|
196
|
+
* MCP tools are registered before the first turn instead of racing init. */
|
|
197
|
+
getMCPReady(): Promise<void>;
|
|
194
198
|
getContextStats(): import("./facades/agent-context-facade.js").ContextStats;
|
|
195
199
|
formatContextStats(): string;
|
|
196
200
|
/**
|
package/dist/agent/base-agent.js
CHANGED
|
@@ -316,6 +316,12 @@ export class BaseAgent extends EventEmitter {
|
|
|
316
316
|
initializeMCP() {
|
|
317
317
|
this.infrastructureFacade.initializeMCP();
|
|
318
318
|
}
|
|
319
|
+
/** Resolves once MCP server initialization has settled (or no-op if MCP was
|
|
320
|
+
* never started). Headless/one-shot callers await this so the gitnexus-style
|
|
321
|
+
* MCP tools are registered before the first turn instead of racing init. */
|
|
322
|
+
getMCPReady() {
|
|
323
|
+
return this.infrastructureFacade.getMCPReady();
|
|
324
|
+
}
|
|
319
325
|
// ============================================================================
|
|
320
326
|
// Context Management (delegates to contextFacade)
|
|
321
327
|
// ============================================================================
|
|
@@ -77,11 +77,18 @@ export declare class InfrastructureFacade {
|
|
|
77
77
|
* Get the MCP client instance (for advanced operations)
|
|
78
78
|
*/
|
|
79
79
|
getMCPClient(): MCPClient;
|
|
80
|
+
/** Resolves once initializeMCP() has finished connecting servers (or failed
|
|
81
|
+
* softly). Callers that need MCP tools available — notably headless one-shot
|
|
82
|
+
* runs — can await getMCPReady() instead of racing the fire-and-forget init. */
|
|
83
|
+
private mcpReady;
|
|
80
84
|
/**
|
|
81
|
-
* Initialize MCP servers asynchronously
|
|
82
|
-
*
|
|
85
|
+
* Initialize MCP servers asynchronously. Kicks off in the background (the
|
|
86
|
+
* constructor path stays non-blocking), but the readiness promise is captured
|
|
87
|
+
* so a one-shot/headless caller can await getMCPReady() before its first turn.
|
|
83
88
|
*/
|
|
84
89
|
initializeMCP(): void;
|
|
90
|
+
/** Promise that resolves when MCP server initialization has settled. */
|
|
91
|
+
getMCPReady(): Promise<void>;
|
|
85
92
|
/**
|
|
86
93
|
* Get the ICM bridge instance for memory operations
|
|
87
94
|
*/
|
|
@@ -67,12 +67,17 @@ export class InfrastructureFacade {
|
|
|
67
67
|
getMCPClient() {
|
|
68
68
|
return this.mcpClient;
|
|
69
69
|
}
|
|
70
|
+
/** Resolves once initializeMCP() has finished connecting servers (or failed
|
|
71
|
+
* softly). Callers that need MCP tools available — notably headless one-shot
|
|
72
|
+
* runs — can await getMCPReady() instead of racing the fire-and-forget init. */
|
|
73
|
+
mcpReady = Promise.resolve();
|
|
70
74
|
/**
|
|
71
|
-
* Initialize MCP servers asynchronously
|
|
72
|
-
*
|
|
75
|
+
* Initialize MCP servers asynchronously. Kicks off in the background (the
|
|
76
|
+
* constructor path stays non-blocking), but the readiness promise is captured
|
|
77
|
+
* so a one-shot/headless caller can await getMCPReady() before its first turn.
|
|
73
78
|
*/
|
|
74
79
|
initializeMCP() {
|
|
75
|
-
(async () => {
|
|
80
|
+
this.mcpReady = (async () => {
|
|
76
81
|
try {
|
|
77
82
|
const config = loadMCPConfig();
|
|
78
83
|
if (config.servers.length > 0) {
|
|
@@ -87,9 +92,13 @@ export class InfrastructureFacade {
|
|
|
87
92
|
catch (error) {
|
|
88
93
|
logger.warn('MCP initialization failed', { error: getErrorMessage(error) });
|
|
89
94
|
}
|
|
90
|
-
})()
|
|
91
|
-
|
|
92
|
-
});
|
|
95
|
+
})();
|
|
96
|
+
// Never let this become an unhandled rejection for callers that don't await.
|
|
97
|
+
void this.mcpReady.catch(() => { });
|
|
98
|
+
}
|
|
99
|
+
/** Promise that resolves when MCP server initialization has settled. */
|
|
100
|
+
getMCPReady() {
|
|
101
|
+
return this.mcpReady;
|
|
93
102
|
}
|
|
94
103
|
/**
|
|
95
104
|
* Get the ICM bridge instance for memory operations
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored-artifact gate — the static (no-execution) safety scan applied to any
|
|
3
|
+
* code the agent authors for itself (tools or skill scripts) BEFORE it is run or
|
|
4
|
+
* registered. Blocking and ordered; the behavioural held-out scoring (which DOES
|
|
5
|
+
* run the code, sandboxed) lives in sandbox-scorer.ts and runs only after this.
|
|
6
|
+
*
|
|
7
|
+
* @module agent/self-improvement/authored-artifact-gate
|
|
8
|
+
*/
|
|
9
|
+
export interface ArtifactGateResult {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
reasons: string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Statically inspect authored code. Returns ok=false with one or more reasons on
|
|
15
|
+
* any finding. `subsystem` selects the dangerous-pattern set ('code' for tools,
|
|
16
|
+
* 'skill' for skill scripts).
|
|
17
|
+
*/
|
|
18
|
+
export declare function inspectAuthoredCode(code: string, subsystem?: 'code' | 'skill'): ArtifactGateResult;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored-artifact gate — the static (no-execution) safety scan applied to any
|
|
3
|
+
* code the agent authors for itself (tools or skill scripts) BEFORE it is run or
|
|
4
|
+
* registered. Blocking and ordered; the behavioural held-out scoring (which DOES
|
|
5
|
+
* run the code, sandboxed) lives in sandbox-scorer.ts and runs only after this.
|
|
6
|
+
*
|
|
7
|
+
* @module agent/self-improvement/authored-artifact-gate
|
|
8
|
+
*/
|
|
9
|
+
import { matchAllDangerousPatterns } from '../../security/dangerous-patterns.js';
|
|
10
|
+
/** Omission placeholders that signal truncated / non-self-contained code. */
|
|
11
|
+
const OMISSION_RE = /\/\/\s*\.\.\.\s*(rest|remaining|other|more|implementation|code)\b|#\s*\.\.\.\s*(rest|remaining)\b/i;
|
|
12
|
+
/** Obvious secret shapes — never let an authored artifact embed these. */
|
|
13
|
+
const SECRET_RE = /(sk-[a-z0-9]{16,}|api[_-]?key\s*[:=]\s*['"]?\S{12,}|-----BEGIN [A-Z ]*PRIVATE KEY-----|ghp_[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16})/i;
|
|
14
|
+
/** Writing under src/ is the hard self-modification invariant — refuse it. */
|
|
15
|
+
const SRC_WRITE_RE = /(writeFile|writeFileSync|appendFile\w*|fs\.\w*write|open\s*\([^)]*['"]w|>>?\s*['"]?\.{0,2}\/?src\/)/i;
|
|
16
|
+
const MAX_CODE_BYTES = 64 * 1024;
|
|
17
|
+
/**
|
|
18
|
+
* Statically inspect authored code. Returns ok=false with one or more reasons on
|
|
19
|
+
* any finding. `subsystem` selects the dangerous-pattern set ('code' for tools,
|
|
20
|
+
* 'skill' for skill scripts).
|
|
21
|
+
*/
|
|
22
|
+
export function inspectAuthoredCode(code, subsystem = 'code') {
|
|
23
|
+
const reasons = [];
|
|
24
|
+
const text = String(code ?? '');
|
|
25
|
+
if (!text.trim())
|
|
26
|
+
reasons.push('code is empty');
|
|
27
|
+
if (text.length > MAX_CODE_BYTES)
|
|
28
|
+
reasons.push(`code too large (${text.length} > ${MAX_CODE_BYTES} bytes)`);
|
|
29
|
+
const dangerous = matchAllDangerousPatterns(text, subsystem);
|
|
30
|
+
if (dangerous.length > 0) {
|
|
31
|
+
reasons.push(`matched ${dangerous.length} dangerous pattern(s): ${dangerous.map((d) => d.description).slice(0, 4).join('; ')}`);
|
|
32
|
+
}
|
|
33
|
+
if (SECRET_RE.test(text))
|
|
34
|
+
reasons.push('looks like it embeds a secret');
|
|
35
|
+
if (OMISSION_RE.test(text))
|
|
36
|
+
reasons.push('contains an omission placeholder (not self-contained)');
|
|
37
|
+
if (/src\//.test(text) && SRC_WRITE_RE.test(text)) {
|
|
38
|
+
reasons.push('writes under src/ (forbidden self-modification invariant)');
|
|
39
|
+
}
|
|
40
|
+
return { ok: reasons.length === 0, reasons };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=authored-artifact-gate.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored-tool runtime — shared by `register_tool` (the live capability) and the
|
|
3
|
+
* self-improvement engine's sandbox scorer (the gate). One definition of "how an
|
|
4
|
+
* authored tool runs" so the thing we GATE is exactly the thing we REGISTER.
|
|
5
|
+
*
|
|
6
|
+
* An authored tool runs its `code` SANDBOXED: a throwaway cwd, RPC off (no
|
|
7
|
+
* callback into the tool system), and its call arguments arrive as JSON in the
|
|
8
|
+
* CODEBUDDY_TOOL_INPUT env var; the script prints its result to stdout.
|
|
9
|
+
*
|
|
10
|
+
* @module agent/self-improvement/authored-tool-runtime
|
|
11
|
+
*/
|
|
12
|
+
import type { ITool } from '../../tools/registry/types.js';
|
|
13
|
+
import { type ExecuteCodeLanguage } from '../../tools/execute-code-runner.js';
|
|
14
|
+
export declare const AUTHORED_PREFIX = "authored__";
|
|
15
|
+
export declare const AUTHORED_LANGUAGES: ExecuteCodeLanguage[];
|
|
16
|
+
export interface AuthoredToolSpec {
|
|
17
|
+
/** Namespaced tool name (see toAuthoredName). */
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
parameters: Record<string, unknown>;
|
|
21
|
+
language: ExecuteCodeLanguage;
|
|
22
|
+
code: string;
|
|
23
|
+
}
|
|
24
|
+
/** Namespace + sanitize a raw tool name to `authored__<slug>` (never shadows a built-in). */
|
|
25
|
+
export declare function toAuthoredName(raw: string): string;
|
|
26
|
+
/** Build an ITool that runs the authored `code` sandboxed when invoked. */
|
|
27
|
+
export declare function buildAuthoredTool(spec: AuthoredToolSpec): ITool;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored-tool runtime — shared by `register_tool` (the live capability) and the
|
|
3
|
+
* self-improvement engine's sandbox scorer (the gate). One definition of "how an
|
|
4
|
+
* authored tool runs" so the thing we GATE is exactly the thing we REGISTER.
|
|
5
|
+
*
|
|
6
|
+
* An authored tool runs its `code` SANDBOXED: a throwaway cwd, RPC off (no
|
|
7
|
+
* callback into the tool system), and its call arguments arrive as JSON in the
|
|
8
|
+
* CODEBUDDY_TOOL_INPUT env var; the script prints its result to stdout.
|
|
9
|
+
*
|
|
10
|
+
* @module agent/self-improvement/authored-tool-runtime
|
|
11
|
+
*/
|
|
12
|
+
import * as os from 'os';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { randomUUID } from 'crypto';
|
|
15
|
+
import { executeCode } from '../../tools/execute-code-runner.js';
|
|
16
|
+
export const AUTHORED_PREFIX = 'authored__';
|
|
17
|
+
export const AUTHORED_LANGUAGES = ['javascript', 'typescript', 'python'];
|
|
18
|
+
/** Namespace + sanitize a raw tool name to `authored__<slug>` (never shadows a built-in). */
|
|
19
|
+
export function toAuthoredName(raw) {
|
|
20
|
+
const base = String(raw)
|
|
21
|
+
.trim()
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
24
|
+
.replace(/^_+|_+$/g, '');
|
|
25
|
+
return base.startsWith(AUTHORED_PREFIX) ? base : `${AUTHORED_PREFIX}${base || 'tool'}`;
|
|
26
|
+
}
|
|
27
|
+
/** Build an ITool that runs the authored `code` sandboxed when invoked. */
|
|
28
|
+
export function buildAuthoredTool(spec) {
|
|
29
|
+
const { name, description, parameters, language, code } = spec;
|
|
30
|
+
return {
|
|
31
|
+
name,
|
|
32
|
+
description,
|
|
33
|
+
async execute(input) {
|
|
34
|
+
const rootDir = path.join(os.tmpdir(), `cb-authored-${randomUUID()}`);
|
|
35
|
+
try {
|
|
36
|
+
const res = await executeCode({ code, language, env: { CODEBUDDY_TOOL_INPUT: JSON.stringify(input ?? {}) } }, { rootDir, rpcEnabled: false });
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: `authored tool "${name}" failed (exit ${res.exitCode}): ${res.stderr.slice(0, 2000) || res.error || 'no output'}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { success: true, output: res.stdout.slice(0, 100_000) || '(no stdout)' };
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: `authored tool "${name}" error: ${err instanceof Error ? err.message : String(err)}`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
getSchema() {
|
|
53
|
+
return { name, description, parameters: parameters };
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=authored-tool-runtime.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored-tool store — durable persistence for tools the agent has authored and
|
|
3
|
+
* kept (auto-apply). Without this, authored tools live only for the session; with
|
|
4
|
+
* it, they are reloaded into both registries at startup so a self-improvement
|
|
5
|
+
* survives a restart. A flat JSON file alongside the evolutionary archive.
|
|
6
|
+
*
|
|
7
|
+
* @module agent/self-improvement/authored-tool-store
|
|
8
|
+
*/
|
|
9
|
+
import type { AuthoredToolSpec } from './authored-tool-runtime.js';
|
|
10
|
+
export declare const AUTHORED_TOOL_STORE_SCHEMA_VERSION = 1;
|
|
11
|
+
export interface AuthoredToolStoreOptions {
|
|
12
|
+
workDir?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class AuthoredToolStore {
|
|
15
|
+
private readonly filePath;
|
|
16
|
+
constructor(options?: AuthoredToolStoreOptions);
|
|
17
|
+
get path(): string;
|
|
18
|
+
private read;
|
|
19
|
+
private write;
|
|
20
|
+
list(): AuthoredToolSpec[];
|
|
21
|
+
/** Upsert a spec by name (a re-authored tool replaces the prior version). */
|
|
22
|
+
add(spec: AuthoredToolSpec): void;
|
|
23
|
+
remove(name: string): boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored-tool store — durable persistence for tools the agent has authored and
|
|
3
|
+
* kept (auto-apply). Without this, authored tools live only for the session; with
|
|
4
|
+
* it, they are reloaded into both registries at startup so a self-improvement
|
|
5
|
+
* survives a restart. A flat JSON file alongside the evolutionary archive.
|
|
6
|
+
*
|
|
7
|
+
* @module agent/self-improvement/authored-tool-store
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
export const AUTHORED_TOOL_STORE_SCHEMA_VERSION = 1;
|
|
12
|
+
export class AuthoredToolStore {
|
|
13
|
+
filePath;
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
const root = options.workDir ?? process.cwd();
|
|
16
|
+
this.filePath = path.join(root, '.codebuddy', 'self-improvement', 'authored-tools.json');
|
|
17
|
+
}
|
|
18
|
+
get path() {
|
|
19
|
+
return this.filePath;
|
|
20
|
+
}
|
|
21
|
+
read() {
|
|
22
|
+
try {
|
|
23
|
+
const parsed = JSON.parse(fs.readFileSync(this.filePath, 'utf-8'));
|
|
24
|
+
if (Array.isArray(parsed.tools)) {
|
|
25
|
+
return { schemaVersion: AUTHORED_TOOL_STORE_SCHEMA_VERSION, tools: parsed.tools };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* no store yet */
|
|
30
|
+
}
|
|
31
|
+
return { schemaVersion: AUTHORED_TOOL_STORE_SCHEMA_VERSION, tools: [] };
|
|
32
|
+
}
|
|
33
|
+
write(file) {
|
|
34
|
+
fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
|
|
35
|
+
fs.writeFileSync(this.filePath, JSON.stringify(file, null, 2), 'utf-8');
|
|
36
|
+
}
|
|
37
|
+
list() {
|
|
38
|
+
return this.read().tools;
|
|
39
|
+
}
|
|
40
|
+
/** Upsert a spec by name (a re-authored tool replaces the prior version). */
|
|
41
|
+
add(spec) {
|
|
42
|
+
const file = this.read();
|
|
43
|
+
file.tools = file.tools.filter((t) => t.name !== spec.name);
|
|
44
|
+
file.tools.push(spec);
|
|
45
|
+
this.write(file);
|
|
46
|
+
}
|
|
47
|
+
remove(name) {
|
|
48
|
+
const file = this.read();
|
|
49
|
+
const before = file.tools.length;
|
|
50
|
+
file.tools = file.tools.filter((t) => t.name !== name);
|
|
51
|
+
if (file.tools.length === before)
|
|
52
|
+
return false;
|
|
53
|
+
this.write(file);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=authored-tool-store.js.map
|