@phuetz/code-buddy 1.2.0 → 1.3.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.
Files changed (104) hide show
  1. package/README.md +56 -3
  2. package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
  3. package/dist/agent/base-agent.d.ts +4 -0
  4. package/dist/agent/base-agent.js +6 -0
  5. package/dist/agent/facades/infrastructure-facade.d.ts +9 -2
  6. package/dist/agent/facades/infrastructure-facade.js +15 -6
  7. package/dist/agent/self-improvement/authored-artifact-gate.d.ts +18 -0
  8. package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
  9. package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
  10. package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
  11. package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
  12. package/dist/agent/self-improvement/authored-tool-store.js +57 -0
  13. package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
  14. package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
  15. package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
  16. package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
  17. package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
  18. package/dist/agent/self-improvement/self-knowledge.js +24 -0
  19. package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
  20. package/dist/agent/self-improvement/skill-benchmark.js +22 -0
  21. package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
  22. package/dist/agent/self-improvement/skill-consolidator.js +137 -0
  23. package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
  24. package/dist/agent/self-improvement/skill-engine.js +87 -0
  25. package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
  26. package/dist/agent/self-improvement/skill-gate.js +62 -0
  27. package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
  28. package/dist/agent/self-improvement/skill-mutator.js +223 -0
  29. package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
  30. package/dist/agent/self-improvement/skill-proposer.js +82 -0
  31. package/dist/agent/self-improvement/skill-types.d.ts +41 -0
  32. package/dist/agent/self-improvement/skill-types.js +13 -0
  33. package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
  34. package/dist/agent/self-improvement/tool-benchmark.js +37 -0
  35. package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
  36. package/dist/agent/self-improvement/tool-engine.js +101 -0
  37. package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
  38. package/dist/agent/self-improvement/tool-gate.js +78 -0
  39. package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
  40. package/dist/agent/self-improvement/tool-proposer.js +34 -0
  41. package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
  42. package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
  43. package/dist/agent/self-improvement/tool-types.d.ts +48 -0
  44. package/dist/agent/self-improvement/tool-types.js +9 -0
  45. package/dist/agent/self-improvement/types.d.ts +3 -1
  46. package/dist/agent/tool-handler.js +3 -0
  47. package/dist/codebuddy/providers/provider-chatgpt-responses.js +6 -1
  48. package/dist/codebuddy/tools.d.ts +7 -0
  49. package/dist/codebuddy/tools.js +40 -0
  50. package/dist/commands/cli/improve-command.js +123 -0
  51. package/dist/commands/enhanced-command-handler.js +1 -1
  52. package/dist/commands/handlers/missing-handlers.d.ts +1 -1
  53. package/dist/commands/handlers/missing-handlers.js +26 -3
  54. package/dist/commands/skills-cli/index.js +123 -0
  55. package/dist/commands/slash/builtin-commands.js +1 -1
  56. package/dist/companion/percepts.js +11 -1
  57. package/dist/context/bootstrap-loader.js +6 -23
  58. package/dist/context/import-directive-parser.d.ts +4 -0
  59. package/dist/context/import-directive-parser.js +51 -6
  60. package/dist/context/instruction-excludes.d.ts +30 -1
  61. package/dist/context/instruction-excludes.js +71 -1
  62. package/dist/context/jit-context.d.ts +8 -10
  63. package/dist/context/jit-context.js +28 -106
  64. package/dist/context/project-context.d.ts +90 -0
  65. package/dist/context/project-context.js +295 -0
  66. package/dist/daemon/autonomous-loop.d.ts +31 -1
  67. package/dist/daemon/autonomous-loop.js +80 -2
  68. package/dist/harness/contract.d.ts +28 -28
  69. package/dist/identity/identity-manager.js +3 -2
  70. package/dist/index.js +17 -1
  71. package/dist/mcp/mcp-resources.js +2 -3
  72. package/dist/sensory/dreaming.d.ts +45 -0
  73. package/dist/sensory/dreaming.js +114 -0
  74. package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
  75. package/dist/sensory/heartbeat-scheduler.js +72 -0
  76. package/dist/sensory/reactions.d.ts +24 -0
  77. package/dist/sensory/reactions.js +31 -0
  78. package/dist/sensory/screen-reaction.d.ts +23 -0
  79. package/dist/sensory/screen-reaction.js +59 -0
  80. package/dist/sensory/sensory-bridge.d.ts +23 -0
  81. package/dist/sensory/sensory-bridge.js +85 -0
  82. package/dist/sensory/sensory-memory.d.ts +20 -0
  83. package/dist/sensory/sensory-memory.js +39 -0
  84. package/dist/sensory/speech-reaction.d.ts +21 -0
  85. package/dist/sensory/speech-reaction.js +83 -0
  86. package/dist/sensory/vision-reaction.d.ts +31 -0
  87. package/dist/sensory/vision-reaction.js +74 -0
  88. package/dist/server/index.js +89 -0
  89. package/dist/services/prompt-builder.d.ts +10 -0
  90. package/dist/services/prompt-builder.js +75 -9
  91. package/dist/skills/parser.js +3 -0
  92. package/dist/skills/skill-importer.d.ts +58 -0
  93. package/dist/skills/skill-importer.js +261 -0
  94. package/dist/skills/skill-sources.d.ts +20 -0
  95. package/dist/skills/skill-sources.js +102 -0
  96. package/dist/skills/types.d.ts +6 -0
  97. package/dist/tools/register-tool-handler.d.ts +25 -0
  98. package/dist/tools/register-tool-handler.js +100 -0
  99. package/dist/tools/registry.d.ts +6 -0
  100. package/dist/tools/registry.js +8 -0
  101. package/dist/utils/init-project.d.ts +7 -0
  102. package/dist/utils/init-project.js +37 -0
  103. package/dist/utils/settings-manager.d.ts +12 -0
  104. package/package.json +1 -1
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.2.0-GA-blueviolet?style=flat-square" alt="Version 1.2.0 GA"/>
20
+ <img src="https://img.shields.io/badge/v1.3.0-GA-blueviolet?style=flat-square" alt="Version 1.3.0 GA"/>
21
21
  </p>
22
22
 
23
23
  <br/>
@@ -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/qa/code-buddy-studio/showcase-2026-06-16/00-welcome.png" alt="Cowork desktop cockpit" width="430"/><br/><sub>Desktop cockpit menus, sessions, composer</sub></td>
174
- <td width="50%" align="center"><img src="docs/qa/code-buddy-studio/showcase-2026-06-16/05-onboarding-provider.png" alt="Onboarding pick a provider" width="430"/><br/><sub>Onboarding15 providers, ChatGPT <code>$0</code> or local Ollama</sub></td>
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>Homeexpanded 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>
@@ -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
  /**
@@ -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
- * This is a fire-and-forget operation
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
- * This is a fire-and-forget operation
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
- })().catch((error) => {
91
- logger.warn('Uncaught error in MCP initialization', { error: getErrorMessage(error) });
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
@@ -0,0 +1,41 @@
1
+ /**
2
+ * LLM tool proposer — authors a candidate tool with the agent's own model from a
3
+ * REDACTED scenario view (capability + visible cases only; never the held-out
4
+ * cases). The behavioural held-out gate validates every draft, so even a gamed or
5
+ * broken draft is caught — the LLM is a generator, not a trusted oracle.
6
+ *
7
+ * Lazy + graceful: if no provider is configured the proposer declines (null) and
8
+ * the engine simply finds no proposal.
9
+ *
10
+ * @module agent/self-improvement/llm-tool-proposer
11
+ */
12
+ import { type AuthoredToolSpec } from './authored-tool-runtime.js';
13
+ import type { ProposerScenarioView, ToolProposer } from './tool-proposer.js';
14
+ import type { ToolProposal } from './tool-types.js';
15
+ interface MinimalClient {
16
+ chat(messages: Array<{
17
+ role: string;
18
+ content: string;
19
+ }>, tools?: unknown[]): Promise<{
20
+ choices?: Array<{
21
+ message?: {
22
+ content?: string | null;
23
+ };
24
+ }>;
25
+ }>;
26
+ }
27
+ export declare function buildToolDraftPrompt(view: ProposerScenarioView): string;
28
+ /** Parse + validate a model draft into an AuthoredToolSpec, or null. */
29
+ export declare function parseToolDraft(text: string): AuthoredToolSpec | null;
30
+ export interface LlmToolProposerOptions {
31
+ /** Override the chat client (tests). Defaults to provider-detected CodeBuddyClient. */
32
+ client?: MinimalClient | null;
33
+ }
34
+ export declare class LlmToolProposer implements ToolProposer {
35
+ private readonly options;
36
+ private clientPromise;
37
+ constructor(options?: LlmToolProposerOptions);
38
+ private getClient;
39
+ propose(view: ProposerScenarioView): Promise<ToolProposal | null>;
40
+ }
41
+ export {};