clawpowers 1.1.4 → 2.0.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 (75) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/LICENSE +44 -0
  3. package/README.md +204 -228
  4. package/SECURITY.md +72 -0
  5. package/dist/index.d.ts +844 -0
  6. package/dist/index.js +2536 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +50 -44
  9. package/.claude-plugin/manifest.json +0 -19
  10. package/.codex/INSTALL.md +0 -36
  11. package/.cursor-plugin/manifest.json +0 -21
  12. package/.opencode/INSTALL.md +0 -52
  13. package/ARCHITECTURE.md +0 -69
  14. package/bin/clawpowers.js +0 -625
  15. package/bin/clawpowers.sh +0 -91
  16. package/docs/demo/clawpowers-demo.cast +0 -197
  17. package/docs/demo/clawpowers-demo.gif +0 -0
  18. package/docs/launch-images/25-skills-breakdown.jpg +0 -0
  19. package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
  20. package/docs/launch-images/economic-code-optimization.jpg +0 -0
  21. package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
  22. package/docs/launch-images/native-vs-bridge.jpg +0 -0
  23. package/docs/launch-images/post1-hero-lobster.jpg +0 -0
  24. package/docs/launch-images/post2-dashboard.jpg +0 -0
  25. package/docs/launch-images/post3-superpowers.jpg +0 -0
  26. package/docs/launch-images/post4-before-after.jpg +0 -0
  27. package/docs/launch-images/post5-install-now.jpg +0 -0
  28. package/docs/launch-images/ultimate-stack.jpg +0 -0
  29. package/docs/launch-posts.md +0 -76
  30. package/docs/quickstart-first-transaction.md +0 -204
  31. package/gemini-extension.json +0 -32
  32. package/hooks/session-start +0 -205
  33. package/hooks/session-start.cmd +0 -43
  34. package/hooks/session-start.js +0 -163
  35. package/runtime/demo/README.md +0 -78
  36. package/runtime/demo/x402-mock-server.js +0 -230
  37. package/runtime/feedback/analyze.js +0 -621
  38. package/runtime/feedback/analyze.sh +0 -546
  39. package/runtime/init.js +0 -210
  40. package/runtime/init.sh +0 -178
  41. package/runtime/metrics/collector.js +0 -361
  42. package/runtime/metrics/collector.sh +0 -308
  43. package/runtime/payments/ledger.js +0 -305
  44. package/runtime/payments/ledger.sh +0 -262
  45. package/runtime/payments/pipeline.js +0 -455
  46. package/runtime/persistence/store.js +0 -433
  47. package/runtime/persistence/store.sh +0 -303
  48. package/skill.json +0 -106
  49. package/skills/agent-bounties/SKILL.md +0 -553
  50. package/skills/agent-payments/SKILL.md +0 -479
  51. package/skills/brainstorming/SKILL.md +0 -233
  52. package/skills/content-pipeline/SKILL.md +0 -282
  53. package/skills/cross-project-knowledge/SKILL.md +0 -345
  54. package/skills/dispatching-parallel-agents/SKILL.md +0 -305
  55. package/skills/economic-code-optimization/SKILL.md +0 -265
  56. package/skills/executing-plans/SKILL.md +0 -255
  57. package/skills/finishing-a-development-branch/SKILL.md +0 -260
  58. package/skills/formal-verification-lite/SKILL.md +0 -441
  59. package/skills/learn-how-to-learn/SKILL.md +0 -235
  60. package/skills/market-intelligence/SKILL.md +0 -323
  61. package/skills/meta-skill-evolution/SKILL.md +0 -325
  62. package/skills/prospecting/SKILL.md +0 -454
  63. package/skills/receiving-code-review/SKILL.md +0 -225
  64. package/skills/requesting-code-review/SKILL.md +0 -206
  65. package/skills/security-audit/SKILL.md +0 -353
  66. package/skills/self-healing-code/SKILL.md +0 -369
  67. package/skills/subagent-driven-development/SKILL.md +0 -244
  68. package/skills/systematic-debugging/SKILL.md +0 -355
  69. package/skills/test-driven-development/SKILL.md +0 -416
  70. package/skills/using-clawpowers/SKILL.md +0 -160
  71. package/skills/using-git-worktrees/SKILL.md +0 -261
  72. package/skills/validator/SKILL.md +0 -281
  73. package/skills/verification-before-completion/SKILL.md +0 -254
  74. package/skills/writing-plans/SKILL.md +0 -276
  75. package/skills/writing-skills/SKILL.md +0 -260
package/dist/index.js ADDED
@@ -0,0 +1,2536 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/skills/catalog.ts
23
+ var catalog_exports = {};
24
+ __export(catalog_exports, {
25
+ SKILLS_CATALOG: () => SKILLS_CATALOG,
26
+ SKILLS_COUNT: () => SKILLS_COUNT
27
+ });
28
+ var SKILLS_CATALOG, SKILLS_COUNT;
29
+ var init_catalog = __esm({
30
+ "src/skills/catalog.ts"() {
31
+ "use strict";
32
+ SKILLS_CATALOG = [
33
+ // ── productivity ──────────────────────────────────────────────────────────
34
+ {
35
+ name: "1password",
36
+ description: "Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.",
37
+ source: "openclaw-bundled",
38
+ category: "productivity"
39
+ },
40
+ {
41
+ name: "apple-notes",
42
+ description: "Manage Apple Notes via the `memo` CLI on macOS (create, view, edit, delete, search, move, and export notes). Use when a user asks OpenClaw to add a note, list notes, search notes, or manage note folders.",
43
+ source: "openclaw-bundled",
44
+ category: "productivity"
45
+ },
46
+ {
47
+ name: "apple-reminders",
48
+ description: "Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.",
49
+ source: "openclaw-bundled",
50
+ category: "productivity"
51
+ },
52
+ {
53
+ name: "bear-notes",
54
+ description: "Create, search, and manage Bear notes via grizzly CLI.",
55
+ source: "openclaw-bundled",
56
+ category: "productivity"
57
+ },
58
+ {
59
+ name: "notion",
60
+ description: "Notion API for creating and managing pages, databases, and blocks.",
61
+ source: "openclaw-bundled",
62
+ category: "productivity"
63
+ },
64
+ {
65
+ name: "obsidian",
66
+ description: "Work with Obsidian vaults (plain Markdown notes) and automate via obsidian-cli.",
67
+ source: "openclaw-bundled",
68
+ category: "productivity"
69
+ },
70
+ {
71
+ name: "things-mac",
72
+ description: "Manage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database). Use when a user asks OpenClaw to add a task to Things, list items, or search tasks.",
73
+ source: "openclaw-bundled",
74
+ category: "productivity"
75
+ },
76
+ {
77
+ name: "trello",
78
+ description: "Manage Trello boards, lists, and cards via the Trello REST API.",
79
+ source: "openclaw-bundled",
80
+ category: "productivity"
81
+ },
82
+ {
83
+ name: "summarize",
84
+ description: 'Summarize or extract text/transcripts from URLs, podcasts, and local files (great fallback for "transcribe this YouTube/video").',
85
+ source: "openclaw-bundled",
86
+ category: "productivity"
87
+ },
88
+ {
89
+ name: "tmux",
90
+ description: "Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output.",
91
+ source: "openclaw-bundled",
92
+ category: "productivity"
93
+ },
94
+ // ── development ───────────────────────────────────────────────────────────
95
+ {
96
+ name: "coding-agent",
97
+ description: "Delegate coding tasks to Codex, Claude Code, or Pi agents via background process. Use when: (1) building/creating new features or apps, (2) reviewing PRs (spawn in temp dir), (3) refactoring large codebases.",
98
+ source: "openclaw-bundled",
99
+ category: "development"
100
+ },
101
+ {
102
+ name: "github",
103
+ description: "GitHub operations via `gh` CLI: issues, PRs, CI runs, code review, API queries. Use when: (1) checking PR status or CI, (2) creating/commenting on issues, (3) listing/filtering PRs or issues, (4) viewing diffs.",
104
+ source: "openclaw-bundled",
105
+ category: "development"
106
+ },
107
+ {
108
+ name: "gh-issues",
109
+ description: "Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments.",
110
+ source: "openclaw-bundled",
111
+ category: "development"
112
+ },
113
+ {
114
+ name: "skill-creator",
115
+ description: "Create, edit, improve, or audit AgentSkills. Use when creating a new skill from scratch or when asked to improve, review, audit, tidy up, or clean up an existing skill or SKILL.md file.",
116
+ source: "openclaw-bundled",
117
+ category: "development"
118
+ },
119
+ {
120
+ name: "clawhub",
121
+ description: "Use the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com. Use when you need to fetch new skills on the fly, sync installed skills to latest or a specific version, or publish your own skills.",
122
+ source: "openclaw-bundled",
123
+ category: "development"
124
+ },
125
+ {
126
+ name: "oracle",
127
+ description: "Best practices for using the oracle CLI (prompt + file bundling, engines, sessions, and file attachment patterns).",
128
+ source: "openclaw-bundled",
129
+ category: "development"
130
+ },
131
+ {
132
+ name: "mcporter",
133
+ description: "Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.",
134
+ source: "openclaw-bundled",
135
+ category: "development"
136
+ },
137
+ {
138
+ name: "nano-pdf",
139
+ description: "Edit PDFs with natural-language instructions using the nano-pdf CLI.",
140
+ source: "openclaw-bundled",
141
+ category: "development"
142
+ },
143
+ {
144
+ name: "node-connect",
145
+ description: "Diagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps. Use when QR/setup code/manual connect fails, local Wi-Fi works but VPS/tailnet does not, or errors mention pairing.",
146
+ source: "openclaw-bundled",
147
+ category: "development"
148
+ },
149
+ // ── communication ─────────────────────────────────────────────────────────
150
+ {
151
+ name: "discord",
152
+ description: "Discord ops via the message tool (channel=discord).",
153
+ source: "openclaw-bundled",
154
+ category: "communication"
155
+ },
156
+ {
157
+ name: "slack",
158
+ description: "Use when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.",
159
+ source: "openclaw-bundled",
160
+ category: "communication"
161
+ },
162
+ {
163
+ name: "bluebubbles",
164
+ description: "Use when you need to send or manage iMessages via BlueBubbles (recommended iMessage integration). Calls go through the generic message tool with channel=bluebubbles.",
165
+ source: "openclaw-bundled",
166
+ category: "communication"
167
+ },
168
+ {
169
+ name: "imsg",
170
+ description: "iMessage/SMS CLI for listing chats, history, and sending messages via Messages.app.",
171
+ source: "openclaw-bundled",
172
+ category: "communication"
173
+ },
174
+ {
175
+ name: "wacli",
176
+ description: "Send WhatsApp messages to other people or search/sync WhatsApp history via the wacli CLI (not for normal user chats).",
177
+ source: "openclaw-bundled",
178
+ category: "communication"
179
+ },
180
+ {
181
+ name: "himalaya",
182
+ description: "CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML.",
183
+ source: "openclaw-bundled",
184
+ category: "communication"
185
+ },
186
+ {
187
+ name: "gog",
188
+ description: "Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.",
189
+ source: "openclaw-bundled",
190
+ category: "communication"
191
+ },
192
+ // ── media ─────────────────────────────────────────────────────────────────
193
+ {
194
+ name: "camsnap",
195
+ description: "Capture frames or clips from RTSP/ONVIF cameras.",
196
+ source: "openclaw-bundled",
197
+ category: "media"
198
+ },
199
+ {
200
+ name: "gifgrep",
201
+ description: "Search GIF providers with CLI/TUI, download results, and extract stills/sheets.",
202
+ source: "openclaw-bundled",
203
+ category: "media"
204
+ },
205
+ {
206
+ name: "video-frames",
207
+ description: "Extract frames or short clips from videos using ffmpeg.",
208
+ source: "openclaw-bundled",
209
+ category: "media"
210
+ },
211
+ {
212
+ name: "openai-whisper",
213
+ description: "Local speech-to-text with the Whisper CLI (no API key).",
214
+ source: "openclaw-bundled",
215
+ category: "media"
216
+ },
217
+ {
218
+ name: "openai-whisper-api",
219
+ description: "Transcribe audio via OpenAI Audio Transcriptions API (Whisper).",
220
+ source: "openclaw-bundled",
221
+ category: "media"
222
+ },
223
+ {
224
+ name: "sag",
225
+ description: "ElevenLabs text-to-speech with mac-style say UX.",
226
+ source: "openclaw-bundled",
227
+ category: "media"
228
+ },
229
+ {
230
+ name: "sherpa-onnx-tts",
231
+ description: "Local text-to-speech via sherpa-onnx (offline, no cloud).",
232
+ source: "openclaw-bundled",
233
+ category: "media"
234
+ },
235
+ {
236
+ name: "songsee",
237
+ description: "Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.",
238
+ source: "openclaw-bundled",
239
+ category: "media"
240
+ },
241
+ {
242
+ name: "voice-call",
243
+ description: "Start voice calls via the OpenClaw voice-call plugin.",
244
+ source: "openclaw-bundled",
245
+ category: "media"
246
+ },
247
+ {
248
+ name: "peekaboo",
249
+ description: "Capture and automate macOS UI with the Peekaboo CLI.",
250
+ source: "openclaw-bundled",
251
+ category: "media"
252
+ },
253
+ // ── music ─────────────────────────────────────────────────────────────────
254
+ {
255
+ name: "spotify-player",
256
+ description: "Terminal Spotify playback/search via spogo (preferred) or spotify_player.",
257
+ source: "openclaw-bundled",
258
+ category: "music"
259
+ },
260
+ {
261
+ name: "sonoscli",
262
+ description: "Control Sonos speakers (discover/status/play/volume/group).",
263
+ source: "openclaw-bundled",
264
+ category: "music"
265
+ },
266
+ {
267
+ name: "blucli",
268
+ description: "BluOS CLI (blu) for discovery, playback, grouping, and volume.",
269
+ source: "openclaw-bundled",
270
+ category: "music"
271
+ },
272
+ // ── smart-home ────────────────────────────────────────────────────────────
273
+ {
274
+ name: "openhue",
275
+ description: "Control Philips Hue lights and scenes via the OpenHue CLI.",
276
+ source: "openclaw-bundled",
277
+ category: "smart-home"
278
+ },
279
+ {
280
+ name: "eightctl",
281
+ description: "Control Eight Sleep pods (status, temperature, alarms, schedules).",
282
+ source: "openclaw-bundled",
283
+ category: "smart-home"
284
+ },
285
+ // ── ai ────────────────────────────────────────────────────────────────────
286
+ {
287
+ name: "gemini",
288
+ description: "Gemini CLI for one-shot Q&A, summaries, and generation.",
289
+ source: "openclaw-bundled",
290
+ category: "ai"
291
+ },
292
+ {
293
+ name: "canvas",
294
+ description: "Display HTML content on connected OpenClaw nodes (Mac app, iOS, Android). Great for displaying games, visualizations, and dashboards.",
295
+ source: "openclaw-bundled",
296
+ category: "ai"
297
+ },
298
+ // ── utilities ─────────────────────────────────────────────────────────────
299
+ {
300
+ name: "blogwatcher",
301
+ description: "Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.",
302
+ source: "openclaw-bundled",
303
+ category: "utilities"
304
+ },
305
+ {
306
+ name: "goplaces",
307
+ description: "Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.",
308
+ source: "openclaw-bundled",
309
+ category: "utilities"
310
+ },
311
+ {
312
+ name: "healthcheck",
313
+ description: "Host security hardening and risk-tolerance configuration for OpenClaw deployments. Use when a user asks for security audits, firewall/SSH/update hardening, risk posture, exposure review, or OpenClaw cron health.",
314
+ source: "openclaw-bundled",
315
+ category: "utilities"
316
+ },
317
+ {
318
+ name: "model-usage",
319
+ description: "Use CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost details.",
320
+ source: "openclaw-bundled",
321
+ category: "utilities"
322
+ },
323
+ {
324
+ name: "ordercli",
325
+ description: "Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).",
326
+ source: "openclaw-bundled",
327
+ category: "utilities"
328
+ },
329
+ {
330
+ name: "session-logs",
331
+ description: "Search and analyze your own session logs (older/parent conversations) using jq.",
332
+ source: "openclaw-bundled",
333
+ category: "utilities"
334
+ },
335
+ {
336
+ name: "weather",
337
+ description: "Get current weather and forecasts via wttr.in or Open-Meteo. Use when: user asks about weather, temperature, or forecasts for any location. NOT for: historical weather data, severe weather alerts, or other weather APIs.",
338
+ source: "openclaw-bundled",
339
+ category: "utilities"
340
+ },
341
+ {
342
+ name: "xurl",
343
+ description: "A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with X/Twitter programmatically.",
344
+ source: "openclaw-bundled",
345
+ category: "utilities"
346
+ },
347
+ // ─── Managed Skills (~/.openclaw/skills/) ──────────────────────────────────
348
+ {
349
+ name: "agent-nexus-2",
350
+ description: "Multi-agent coordination and task delegation for complex workflows requiring parallel agent execution.",
351
+ source: "managed",
352
+ category: "development"
353
+ },
354
+ {
355
+ name: "autoresearch",
356
+ description: "Autonomous code quality improvement loop using keep-or-revert cycles. Optimizes a composite quality score derived from tests, lint, and type coverage.",
357
+ source: "managed",
358
+ category: "development"
359
+ },
360
+ {
361
+ name: "business-strategy",
362
+ description: "PMF validation, beachhead identification, activity ROI analysis, kill/invest decisions, and strategic metrics for the AI Agent Economy.",
363
+ source: "managed",
364
+ category: "productivity"
365
+ },
366
+ {
367
+ name: "coding-discipline.skill",
368
+ description: "Enforces strict TypeScript coding standards, test-first development, and zero-stub policies for production-grade agent code.",
369
+ source: "managed",
370
+ category: "development"
371
+ },
372
+ {
373
+ name: "content-writer",
374
+ description: "Use this skill everytime you are writing an article, social media post, email, etc.",
375
+ source: "managed",
376
+ category: "communication"
377
+ },
378
+ {
379
+ name: "execution-validation.skill",
380
+ description: "Multi-round automated validation pipeline for TypeScript/Solidity projects before publish or deploy.",
381
+ source: "managed",
382
+ category: "development"
383
+ },
384
+ {
385
+ name: "humanize",
386
+ description: "Transforms AI-generated writing into content that reads authentically human \u2014 passing AI detectors and resonating with real readers.",
387
+ source: "managed",
388
+ category: "communication"
389
+ },
390
+ {
391
+ name: "polyclaw",
392
+ description: "Trade on Polymarket via split + CLOB execution. Browse markets, track positions with P&L, discover hedges via LLM. Polygon/Web3.",
393
+ source: "managed",
394
+ category: "finance"
395
+ },
396
+ {
397
+ name: "prospector",
398
+ description: "Find leads, prospects, and contacts matching an Ideal Customer Profile. Searches companies via Exa and enriches contacts via Apollo, outputting to CSV and optionally syncing to Attio CRM.",
399
+ source: "managed",
400
+ category: "productivity"
401
+ },
402
+ {
403
+ name: "rsi.skill",
404
+ description: "RSI self-improvement cycles implementing measure \u2192 hypothesize \u2192 mutate \u2192 test \u2192 apply/discard \u2192 repeat for agent capability enhancement.",
405
+ source: "managed",
406
+ category: "development"
407
+ },
408
+ {
409
+ name: "security",
410
+ description: "Infrastructure threat detection, vulnerability management, and security audit workflows for the OpenClaw workspace.",
411
+ source: "managed",
412
+ category: "utilities"
413
+ },
414
+ {
415
+ name: "strykr-prism",
416
+ description: "Real-time financial data API for AI agents. Stocks, crypto, forex, ETFs. 120+ endpoints. Alternative to Alpha Vantage, CoinGecko. Works with Claude, Cursor.",
417
+ source: "managed",
418
+ category: "finance"
419
+ },
420
+ {
421
+ name: "taskbridge",
422
+ description: "Bridge tasks between agent sessions, preserving context and handoff state for long-running multi-session workflows.",
423
+ source: "managed",
424
+ category: "productivity"
425
+ },
426
+ {
427
+ name: "validator-agent",
428
+ description: "Multi-round automated validation pipeline for TypeScript/Solidity projects. Runs 8 rounds of checks before any publish or deploy: compile gate, lint, test suite, security audit, type coverage, docs, changelog, and final review.",
429
+ source: "managed",
430
+ category: "development"
431
+ },
432
+ {
433
+ name: "webmcp-payments",
434
+ description: "Handle HTTP 402 Payment Required responses via agentpay-mcp, enabling autonomous micropayment execution within configured spending limits.",
435
+ source: "managed",
436
+ category: "finance"
437
+ }
438
+ ];
439
+ SKILLS_COUNT = SKILLS_CATALOG.length;
440
+ }
441
+ });
442
+
443
+ // src/config.ts
444
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
445
+ import { dirname } from "path";
446
+ import { z } from "zod";
447
+
448
+ // src/constants.ts
449
+ import { join } from "path";
450
+ import { homedir } from "os";
451
+ var VERSION = "2.0.0";
452
+ var PACKAGE_NAME = "clawpowers";
453
+ var CLAWPOWERS_HOME = join(homedir(), ".clawpowers");
454
+ var CONFIG_PATH = join(CLAWPOWERS_HOME, "config.json");
455
+ var SKILLS_DIR = join(CLAWPOWERS_HOME, "skills");
456
+ var DATA_DIR = join(CLAWPOWERS_HOME, "data");
457
+ var LOGS_DIR = join(CLAWPOWERS_HOME, "logs");
458
+ var MEMORY_DIR = join(CLAWPOWERS_HOME, "memory");
459
+ var METRICS_DIR = join(CLAWPOWERS_HOME, "metrics");
460
+ var PROFILES_DIR = join(CLAWPOWERS_HOME, "profiles");
461
+ var WALLET_DIR = join(CLAWPOWERS_HOME, "wallet");
462
+ var CHECKPOINTS_DIR = join(CLAWPOWERS_HOME, "state", "checkpoints");
463
+ var DEFAULT_CONFIG = {
464
+ version: VERSION,
465
+ profile: "dev",
466
+ rsi: {
467
+ enabled: true,
468
+ tiers: {
469
+ t1: "auto",
470
+ t2: "auto",
471
+ t3: "ask",
472
+ t4: "ask"
473
+ }
474
+ },
475
+ payments: {
476
+ mode: "human-first",
477
+ dailyLimitUsd: 25,
478
+ weeklyLimitUsd: 100,
479
+ allowedDomains: []
480
+ },
481
+ logging: {
482
+ level: "info",
483
+ retentionDays: 30
484
+ },
485
+ skillsDir: SKILLS_DIR,
486
+ dataDir: DATA_DIR
487
+ };
488
+ var RSI_TIER_ALLOWED_MODES = {
489
+ t1: ["auto", "ask", "off"],
490
+ t2: ["auto", "ask", "off"],
491
+ t3: ["auto", "ask", "off"],
492
+ t4: ["ask", "off"]
493
+ };
494
+ var SAFETY_INVARIANTS = [
495
+ "Spending limits and SpendingPolicy",
496
+ "Core identity and directives",
497
+ "RSI safety tier definitions",
498
+ "Sandbox boundaries",
499
+ "Authentication credentials"
500
+ ];
501
+ var PERFORMANCE = {
502
+ coldStartupMs: 2e3,
503
+ maxMemoryRssMb: 150,
504
+ maxContextTokens: 2e3,
505
+ checkpointWriteMs: 100,
506
+ episodicSearchMs: 50,
507
+ profileSwitchMs: 500,
508
+ healthCheckIntervalMs: 3e4,
509
+ maxRetries: 3
510
+ };
511
+
512
+ // src/config.ts
513
+ var RSITierSchema = z.object({
514
+ t1: z.enum(["auto", "ask", "off"]),
515
+ t2: z.enum(["auto", "ask", "off"]),
516
+ t3: z.enum(["auto", "ask", "off"]),
517
+ t4: z.enum(["ask", "off"])
518
+ // NO "auto" — safety invariant
519
+ });
520
+ var RSIConfigSchema = z.object({
521
+ enabled: z.boolean(),
522
+ tiers: RSITierSchema
523
+ });
524
+ var PaymentConfigSchema = z.object({
525
+ mode: z.enum(["human-first", "auto", "disabled"]),
526
+ dailyLimitUsd: z.number().min(0),
527
+ weeklyLimitUsd: z.number().min(0),
528
+ allowedDomains: z.array(z.string())
529
+ });
530
+ var LoggingConfigSchema = z.object({
531
+ level: z.enum(["debug", "info", "warn", "error"]),
532
+ retentionDays: z.number().min(1).max(365)
533
+ });
534
+ var ConfigFileSchema = z.object({
535
+ version: z.string(),
536
+ profile: z.enum(["dev", "lead", "secure", "growth", "full"]),
537
+ rsi: RSIConfigSchema,
538
+ payments: PaymentConfigSchema,
539
+ logging: LoggingConfigSchema,
540
+ skillsDir: z.string(),
541
+ dataDir: z.string()
542
+ });
543
+ function loadConfig(configPath = CONFIG_PATH) {
544
+ if (!existsSync(configPath)) {
545
+ return DEFAULT_CONFIG;
546
+ }
547
+ const raw = readFileSync(configPath, "utf-8");
548
+ const parsed = JSON.parse(raw);
549
+ return ConfigFileSchema.parse(parsed);
550
+ }
551
+ function loadConfigSafe(configPath = CONFIG_PATH) {
552
+ try {
553
+ return loadConfig(configPath);
554
+ } catch {
555
+ return DEFAULT_CONFIG;
556
+ }
557
+ }
558
+ function saveConfig(config, configPath = CONFIG_PATH) {
559
+ const validated = ConfigFileSchema.parse(config);
560
+ const dir = dirname(configPath);
561
+ if (!existsSync(dir)) {
562
+ mkdirSync(dir, { recursive: true });
563
+ }
564
+ writeFileSync(configPath, JSON.stringify(validated, null, 2) + "\n", "utf-8");
565
+ }
566
+ function initConfig(configPath = CONFIG_PATH) {
567
+ const config = DEFAULT_CONFIG;
568
+ saveConfig(config, configPath);
569
+ return config;
570
+ }
571
+ function getConfigValue(config, key) {
572
+ const parts = key.split(".");
573
+ let current = config;
574
+ for (const part of parts) {
575
+ if (current === null || current === void 0 || typeof current !== "object") {
576
+ return void 0;
577
+ }
578
+ current = current[part];
579
+ }
580
+ return current;
581
+ }
582
+ function setConfigValue(config, key, value) {
583
+ validateTierSetting(key, value);
584
+ const parts = key.split(".");
585
+ const mutable = JSON.parse(JSON.stringify(config));
586
+ let current = mutable;
587
+ for (let i = 0; i < parts.length - 1; i++) {
588
+ const part = parts[i];
589
+ if (current[part] === void 0 || typeof current[part] !== "object") {
590
+ throw new Error(`Invalid config path: ${key}`);
591
+ }
592
+ current = current[part];
593
+ }
594
+ const lastKey = parts[parts.length - 1];
595
+ if (!(lastKey in current)) {
596
+ throw new Error(`Invalid config key: ${key}`);
597
+ }
598
+ const coerced = coerceValue(current[lastKey], value);
599
+ current[lastKey] = coerced;
600
+ return ConfigFileSchema.parse(mutable);
601
+ }
602
+ function validateTierSetting(key, value) {
603
+ const tierMatch = key.match(/^rsi\.tiers\.(t[1-4])$/);
604
+ if (tierMatch) {
605
+ const tier = tierMatch[1];
606
+ const allowed = RSI_TIER_ALLOWED_MODES[tier];
607
+ if (!allowed.includes(value)) {
608
+ if (tier === "t4" && value === "auto") {
609
+ throw new Error(
610
+ 'T4 (Architecture Proposals) cannot be set to "auto". This is a safety invariant \u2014 T4 changes always require human approval. Allowed modes: ask, off'
611
+ );
612
+ }
613
+ throw new Error(
614
+ `Invalid mode "${value}" for tier ${tier}. Allowed: ${allowed.join(", ")}`
615
+ );
616
+ }
617
+ }
618
+ }
619
+ function coerceValue(existing, value) {
620
+ if (typeof existing === "boolean") {
621
+ if (value === "true") return true;
622
+ if (value === "false") return false;
623
+ throw new Error(`Expected boolean value (true/false), got "${value}"`);
624
+ }
625
+ if (typeof existing === "number") {
626
+ const num = Number(value);
627
+ if (Number.isNaN(num)) {
628
+ throw new Error(`Expected number value, got "${value}"`);
629
+ }
630
+ return num;
631
+ }
632
+ return value;
633
+ }
634
+
635
+ // src/payments/discovery.ts
636
+ var REQUIRED_HEADERS = [
637
+ "x-payment-amount",
638
+ "x-payment-currency",
639
+ "x-payment-recipient",
640
+ "x-payment-network"
641
+ ];
642
+ function detect402(response) {
643
+ if (response.status !== 402) {
644
+ return null;
645
+ }
646
+ const normalizedHeaders = {};
647
+ for (const [key, value] of Object.entries(response.headers)) {
648
+ normalizedHeaders[key.toLowerCase()] = value;
649
+ }
650
+ for (const header of REQUIRED_HEADERS) {
651
+ if (!normalizedHeaders[header] || normalizedHeaders[header].trim() === "") {
652
+ return null;
653
+ }
654
+ }
655
+ const amountStr = normalizedHeaders["x-payment-amount"];
656
+ const amount = Number(amountStr);
657
+ if (Number.isNaN(amount) || amount <= 0) {
658
+ return null;
659
+ }
660
+ const x402Headers = {};
661
+ for (const [key, value] of Object.entries(normalizedHeaders)) {
662
+ if (key.startsWith("x-payment-")) {
663
+ x402Headers[key] = value;
664
+ }
665
+ }
666
+ return {
667
+ amount,
668
+ currency: normalizedHeaders["x-payment-currency"],
669
+ recipient: normalizedHeaders["x-payment-recipient"],
670
+ network: normalizedHeaders["x-payment-network"],
671
+ x402Headers
672
+ };
673
+ }
674
+ function isPaymentRequired(error) {
675
+ if (error === null || error === void 0) return false;
676
+ if (typeof error === "object") {
677
+ const obj = error;
678
+ if ("status" in obj && obj["status"] === 402) return true;
679
+ if ("statusCode" in obj && obj["statusCode"] === 402) return true;
680
+ if ("response" in obj && typeof obj["response"] === "object" && obj["response"] !== null) {
681
+ const response = obj["response"];
682
+ if ("status" in response && response["status"] === 402) return true;
683
+ }
684
+ if ("message" in obj && typeof obj["message"] === "string" && obj["message"].includes("402")) {
685
+ return true;
686
+ }
687
+ }
688
+ return false;
689
+ }
690
+
691
+ // src/payments/spending.ts
692
+ function getUtcDayStart() {
693
+ const now = /* @__PURE__ */ new Date();
694
+ const utcStart = new Date(Date.UTC(
695
+ now.getUTCFullYear(),
696
+ now.getUTCMonth(),
697
+ now.getUTCDate(),
698
+ 0,
699
+ 0,
700
+ 0,
701
+ 0
702
+ ));
703
+ return utcStart.getTime();
704
+ }
705
+ var SpendingPolicy = class {
706
+ dailyLimit;
707
+ transactionLimit;
708
+ allowedDomains;
709
+ spendingLog = [];
710
+ constructor(options) {
711
+ this.dailyLimit = options.dailyLimit;
712
+ this.transactionLimit = options.transactionLimit;
713
+ this.allowedDomains = options.allowedDomains;
714
+ }
715
+ /**
716
+ * Get total spending for the current UTC day.
717
+ */
718
+ getDailySpent() {
719
+ const dayStart = getUtcDayStart();
720
+ return this.spendingLog.filter((r) => r.timestamp >= dayStart).reduce((sum, r) => sum + r.amount, 0);
721
+ }
722
+ /**
723
+ * Check whether a transaction is allowed under the current policy.
724
+ * Fail-closed: any validation error results in rejection.
725
+ */
726
+ checkTransaction(amount, domain) {
727
+ try {
728
+ if (amount <= 0 || !Number.isFinite(amount)) {
729
+ return {
730
+ allowed: false,
731
+ reason: `Invalid amount: ${amount}`,
732
+ remainingDaily: this.dailyLimit - this.getDailySpent()
733
+ };
734
+ }
735
+ if (amount > this.transactionLimit) {
736
+ return {
737
+ allowed: false,
738
+ reason: `Amount $${amount} exceeds per-transaction limit of $${this.transactionLimit}`,
739
+ remainingDaily: this.dailyLimit - this.getDailySpent()
740
+ };
741
+ }
742
+ if (this.allowedDomains.length > 0) {
743
+ const normalizedDomain = domain.toLowerCase();
744
+ const isAllowed = this.allowedDomains.some(
745
+ (d) => d.toLowerCase() === normalizedDomain
746
+ );
747
+ if (!isAllowed) {
748
+ return {
749
+ allowed: false,
750
+ reason: `Domain "${domain}" is not in the allowed domains list`,
751
+ remainingDaily: this.dailyLimit - this.getDailySpent()
752
+ };
753
+ }
754
+ }
755
+ const dailySpent = this.getDailySpent();
756
+ if (dailySpent + amount > this.dailyLimit) {
757
+ return {
758
+ allowed: false,
759
+ reason: `Transaction of $${amount} would exceed daily limit of $${this.dailyLimit} (already spent: $${dailySpent})`,
760
+ remainingDaily: this.dailyLimit - dailySpent
761
+ };
762
+ }
763
+ return {
764
+ allowed: true,
765
+ reason: "Transaction approved",
766
+ remainingDaily: this.dailyLimit - dailySpent - amount
767
+ };
768
+ } catch {
769
+ return {
770
+ allowed: false,
771
+ reason: "Policy check failed due to internal error \u2014 rejecting for safety",
772
+ remainingDaily: 0
773
+ };
774
+ }
775
+ }
776
+ /**
777
+ * Record a completed spending transaction.
778
+ */
779
+ recordSpend(amount, domain) {
780
+ this.spendingLog.push({
781
+ amount,
782
+ timestamp: Date.now(),
783
+ domain
784
+ });
785
+ }
786
+ /**
787
+ * Reset all spending records (used for testing).
788
+ */
789
+ reset() {
790
+ this.spendingLog = [];
791
+ }
792
+ /**
793
+ * Get the full spending log (for audit purposes).
794
+ */
795
+ getSpendingLog() {
796
+ return [...this.spendingLog];
797
+ }
798
+ };
799
+
800
+ // src/payments/executor.ts
801
+ var PaymentExecutor = class {
802
+ policy;
803
+ client;
804
+ auditLog = [];
805
+ constructor(policy, client) {
806
+ this.policy = policy;
807
+ this.client = client;
808
+ }
809
+ /**
810
+ * Execute a payment request.
811
+ * 1. Check spending policy
812
+ * 2. If allowed, execute via MCP client
813
+ * 3. Log the result (success or failure)
814
+ * 4. Never auto-retry on failure
815
+ */
816
+ async executePayment(request) {
817
+ const decision = this.policy.checkTransaction(request.amount, request.domain);
818
+ if (!decision.allowed) {
819
+ const result = {
820
+ success: false,
821
+ error: `Spending policy rejected: ${decision.reason}`
822
+ };
823
+ this.logAudit(request, result);
824
+ return result;
825
+ }
826
+ try {
827
+ const mcpResult = await this.client.executePayment({
828
+ amount: request.amount,
829
+ currency: request.currency,
830
+ recipient: request.recipient,
831
+ x402Headers: request.x402Headers
832
+ });
833
+ if (mcpResult.status === "success") {
834
+ this.policy.recordSpend(request.amount, request.domain);
835
+ const result2 = {
836
+ success: true,
837
+ txHash: mcpResult.txHash
838
+ };
839
+ this.logAudit(request, result2);
840
+ return result2;
841
+ }
842
+ const result = {
843
+ success: false,
844
+ error: "Payment execution failed at MCP layer"
845
+ };
846
+ this.logAudit(request, result);
847
+ return result;
848
+ } catch (err) {
849
+ const errorMessage = err instanceof Error ? err.message : String(err);
850
+ const result = {
851
+ success: false,
852
+ error: `Payment execution error: ${errorMessage}`
853
+ };
854
+ this.logAudit(request, result);
855
+ return result;
856
+ }
857
+ }
858
+ /**
859
+ * Get the full payment audit log.
860
+ */
861
+ getAuditLog() {
862
+ return [...this.auditLog];
863
+ }
864
+ /**
865
+ * Log a payment attempt to the audit trail.
866
+ */
867
+ logAudit(request, result) {
868
+ this.auditLog.push({
869
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
870
+ request,
871
+ result,
872
+ spendingSnapshot: {
873
+ dailySpent: this.policy.getDailySpent(),
874
+ dailyLimit: this.policy.dailyLimit
875
+ }
876
+ });
877
+ }
878
+ };
879
+
880
+ // src/memory/working.ts
881
+ function estimateTokens(text) {
882
+ return Math.ceil(text.length / 4);
883
+ }
884
+ var WorkingMemoryManager = class {
885
+ memory = null;
886
+ create(taskId, goal) {
887
+ const emptyPlan = {
888
+ taskId,
889
+ steps: [],
890
+ status: "draft",
891
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
892
+ approvedAt: null,
893
+ parallelizable: false
894
+ };
895
+ this.memory = {
896
+ taskId,
897
+ goal,
898
+ plan: emptyPlan,
899
+ currentStepId: null,
900
+ intermediateOutputs: {},
901
+ contextWindow: []
902
+ };
903
+ return this.memory;
904
+ }
905
+ updateCurrentStep(stepId) {
906
+ if (!this.memory) {
907
+ throw new Error("Working memory not initialized. Call create() first.");
908
+ }
909
+ this.memory = {
910
+ ...this.memory,
911
+ currentStepId: stepId
912
+ };
913
+ }
914
+ addIntermediateOutput(stepId, output) {
915
+ if (!this.memory) {
916
+ throw new Error("Working memory not initialized. Call create() first.");
917
+ }
918
+ this.memory = {
919
+ ...this.memory,
920
+ intermediateOutputs: {
921
+ ...this.memory.intermediateOutputs,
922
+ [stepId]: output
923
+ }
924
+ };
925
+ }
926
+ /**
927
+ * Inject context entries into working memory, enforcing token budget.
928
+ * Entries are added until the budget is exhausted, then truncated.
929
+ */
930
+ injectContext(entries) {
931
+ if (!this.memory) {
932
+ throw new Error("Working memory not initialized. Call create() first.");
933
+ }
934
+ const maxTokens = PERFORMANCE.maxContextTokens;
935
+ const injected = [];
936
+ let totalTokens = 0;
937
+ for (const entry of entries) {
938
+ const entryTokens = estimateTokens(entry);
939
+ if (totalTokens + entryTokens > maxTokens) {
940
+ const remainingTokens = maxTokens - totalTokens;
941
+ if (remainingTokens > 10) {
942
+ const truncatedLength = remainingTokens * 4;
943
+ injected.push(entry.slice(0, truncatedLength) + "...");
944
+ }
945
+ break;
946
+ }
947
+ totalTokens += entryTokens;
948
+ injected.push(entry);
949
+ }
950
+ this.memory = {
951
+ ...this.memory,
952
+ contextWindow: injected
953
+ };
954
+ }
955
+ getSnapshot() {
956
+ if (!this.memory) {
957
+ throw new Error("Working memory not initialized. Call create() first.");
958
+ }
959
+ return this.memory;
960
+ }
961
+ clear() {
962
+ this.memory = null;
963
+ }
964
+ };
965
+
966
+ // src/memory/episodic.ts
967
+ import { readFile, appendFile, writeFile, mkdir } from "fs/promises";
968
+ import { existsSync as existsSync2 } from "fs";
969
+ import { dirname as dirname2 } from "path";
970
+ var EpisodicMemory = class {
971
+ filePath;
972
+ constructor(filePath) {
973
+ this.filePath = filePath;
974
+ }
975
+ async ensureDir() {
976
+ const dir = dirname2(this.filePath);
977
+ if (!existsSync2(dir)) {
978
+ await mkdir(dir, { recursive: true });
979
+ }
980
+ }
981
+ async append(entry) {
982
+ await this.ensureDir();
983
+ const line = JSON.stringify(entry) + "\n";
984
+ await appendFile(this.filePath, line, "utf-8");
985
+ }
986
+ async readAll() {
987
+ if (!existsSync2(this.filePath)) {
988
+ return [];
989
+ }
990
+ const content = await readFile(this.filePath, "utf-8");
991
+ return this.parseLines(content);
992
+ }
993
+ async search(query, limit = 10) {
994
+ const entries = await this.readAll();
995
+ const queryLower = query.toLowerCase();
996
+ const queryWords = queryLower.split(/\s+/).filter(Boolean);
997
+ const scored = [];
998
+ for (const entry of entries) {
999
+ const searchText = [
1000
+ entry.description,
1001
+ ...entry.lessonsLearned,
1002
+ ...entry.tags
1003
+ ].join(" ").toLowerCase();
1004
+ let score = 0;
1005
+ for (const word of queryWords) {
1006
+ if (searchText.includes(word)) {
1007
+ score += 1;
1008
+ }
1009
+ }
1010
+ if (searchText.includes(queryLower)) {
1011
+ score += queryWords.length;
1012
+ }
1013
+ if (score > 0) {
1014
+ scored.push({ entry, score });
1015
+ }
1016
+ }
1017
+ scored.sort((a, b) => b.score - a.score);
1018
+ return scored.slice(0, limit).map((s) => s.entry);
1019
+ }
1020
+ async readRecent(count) {
1021
+ const entries = await this.readAll();
1022
+ return entries.slice(-count);
1023
+ }
1024
+ async recoverFromCorruption() {
1025
+ if (!existsSync2(this.filePath)) {
1026
+ return { recovered: 0, lost: 0 };
1027
+ }
1028
+ const content = await readFile(this.filePath, "utf-8");
1029
+ const lines = content.split("\n").filter((line) => line.trim().length > 0);
1030
+ const validLines = [];
1031
+ let lost = 0;
1032
+ for (const line of lines) {
1033
+ try {
1034
+ JSON.parse(line);
1035
+ validLines.push(line);
1036
+ } catch {
1037
+ lost++;
1038
+ }
1039
+ }
1040
+ if (lost > 0) {
1041
+ await writeFile(this.filePath, validLines.map((l) => l + "\n").join(""), "utf-8");
1042
+ }
1043
+ return { recovered: validLines.length, lost };
1044
+ }
1045
+ parseLines(content) {
1046
+ const lines = content.split("\n").filter((line) => line.trim().length > 0);
1047
+ const entries = [];
1048
+ for (const line of lines) {
1049
+ try {
1050
+ entries.push(JSON.parse(line));
1051
+ } catch {
1052
+ }
1053
+ }
1054
+ return entries;
1055
+ }
1056
+ };
1057
+
1058
+ // src/memory/procedural.ts
1059
+ import { readFile as readFile2, writeFile as writeFile2, rename, mkdir as mkdir2, copyFile } from "fs/promises";
1060
+ import { existsSync as existsSync3 } from "fs";
1061
+ import { dirname as dirname3 } from "path";
1062
+ var ProceduralMemory = class {
1063
+ filePath;
1064
+ cache = null;
1065
+ constructor(filePath) {
1066
+ this.filePath = filePath;
1067
+ }
1068
+ async ensureDir() {
1069
+ const dir = dirname3(this.filePath);
1070
+ if (!existsSync3(dir)) {
1071
+ await mkdir2(dir, { recursive: true });
1072
+ }
1073
+ }
1074
+ async load() {
1075
+ if (!existsSync3(this.filePath)) {
1076
+ this.cache = [];
1077
+ return [];
1078
+ }
1079
+ const content = await readFile2(this.filePath, "utf-8");
1080
+ const entries = JSON.parse(content);
1081
+ this.cache = entries;
1082
+ return entries;
1083
+ }
1084
+ async update(skillName, result) {
1085
+ const entries = await this.load();
1086
+ const existing = entries.find((e) => e.skillName === skillName);
1087
+ if (existing) {
1088
+ const newCount = existing.invocationCount + 1;
1089
+ const successCount = Math.round(existing.successRate * existing.invocationCount) + (result.succeeded ? 1 : 0);
1090
+ const newSuccessRate = successCount / newCount;
1091
+ const newAvgContribution = (existing.avgContribution * existing.invocationCount + result.durationMs) / newCount;
1092
+ const updated = {
1093
+ ...existing,
1094
+ invocationCount: newCount,
1095
+ successRate: newSuccessRate,
1096
+ avgContribution: newAvgContribution,
1097
+ lastUsed: (/* @__PURE__ */ new Date()).toISOString()
1098
+ };
1099
+ const index = entries.indexOf(existing);
1100
+ entries[index] = updated;
1101
+ } else {
1102
+ const newEntry = {
1103
+ skillName,
1104
+ invocationCount: 1,
1105
+ successRate: result.succeeded ? 1 : 0,
1106
+ avgContribution: result.durationMs,
1107
+ preferredContexts: [],
1108
+ lastUsed: (/* @__PURE__ */ new Date()).toISOString(),
1109
+ mutations: []
1110
+ };
1111
+ entries.push(newEntry);
1112
+ }
1113
+ await this.atomicWrite(entries);
1114
+ this.cache = entries;
1115
+ }
1116
+ getSkillScore(skillName) {
1117
+ if (!this.cache) {
1118
+ return null;
1119
+ }
1120
+ return this.cache.find((e) => e.skillName === skillName) ?? null;
1121
+ }
1122
+ getTopSkills(context, limit) {
1123
+ if (!this.cache) {
1124
+ return [];
1125
+ }
1126
+ const contextLower = context.toLowerCase();
1127
+ const contextWords = contextLower.split(/\s+/).filter(Boolean);
1128
+ const scored = [];
1129
+ for (const entry of this.cache) {
1130
+ let score = entry.successRate * entry.invocationCount;
1131
+ for (const preferred of entry.preferredContexts) {
1132
+ const prefLower = preferred.toLowerCase();
1133
+ for (const word of contextWords) {
1134
+ if (prefLower.includes(word)) {
1135
+ score += 2;
1136
+ }
1137
+ }
1138
+ }
1139
+ if (contextLower.includes(entry.skillName.toLowerCase())) {
1140
+ score += 5;
1141
+ }
1142
+ scored.push({ entry, score });
1143
+ }
1144
+ scored.sort((a, b) => b.score - a.score);
1145
+ return scored.slice(0, limit).map((s) => s.entry);
1146
+ }
1147
+ async recordMutation(skillName, mutation) {
1148
+ const entries = await this.load();
1149
+ const existing = entries.find((e) => e.skillName === skillName);
1150
+ if (!existing) {
1151
+ throw new Error(`Skill "${skillName}" not found in procedural memory`);
1152
+ }
1153
+ const updated = {
1154
+ ...existing,
1155
+ mutations: [...existing.mutations, mutation]
1156
+ };
1157
+ const index = entries.indexOf(existing);
1158
+ entries[index] = updated;
1159
+ await this.atomicWrite(entries);
1160
+ this.cache = entries;
1161
+ }
1162
+ async rollbackMutation(skillName, mutationId) {
1163
+ const entries = await this.load();
1164
+ const existing = entries.find((e) => e.skillName === skillName);
1165
+ if (!existing) {
1166
+ throw new Error(`Skill "${skillName}" not found in procedural memory`);
1167
+ }
1168
+ const mutation = existing.mutations.find((m) => m.mutationId === mutationId);
1169
+ if (!mutation) {
1170
+ throw new Error(`Mutation "${mutationId}" not found for skill "${skillName}"`);
1171
+ }
1172
+ const updatedMutations = existing.mutations.map((m) => {
1173
+ if (m.mutationId === mutationId) {
1174
+ return {
1175
+ ...m,
1176
+ status: "reverted",
1177
+ revertedAt: (/* @__PURE__ */ new Date()).toISOString()
1178
+ };
1179
+ }
1180
+ return m;
1181
+ });
1182
+ const updated = {
1183
+ ...existing,
1184
+ mutations: updatedMutations
1185
+ };
1186
+ const index = entries.indexOf(existing);
1187
+ entries[index] = updated;
1188
+ await this.atomicWrite(entries);
1189
+ this.cache = entries;
1190
+ }
1191
+ async atomicWrite(entries) {
1192
+ await this.ensureDir();
1193
+ if (existsSync3(this.filePath)) {
1194
+ await copyFile(this.filePath, this.filePath + ".bak");
1195
+ }
1196
+ const tmpPath = this.filePath + ".tmp";
1197
+ await writeFile2(tmpPath, JSON.stringify(entries, null, 2) + "\n", "utf-8");
1198
+ await rename(tmpPath, this.filePath);
1199
+ }
1200
+ };
1201
+
1202
+ // src/memory/checkpoint.ts
1203
+ import { readFile as readFile3, writeFile as writeFile3, rename as rename2, unlink, readdir, mkdir as mkdir3 } from "fs/promises";
1204
+ import { existsSync as existsSync4 } from "fs";
1205
+ import { join as join2 } from "path";
1206
+ var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
1207
+ var CheckpointManager = class {
1208
+ dir;
1209
+ constructor(dir) {
1210
+ this.dir = dir;
1211
+ }
1212
+ async ensureDir() {
1213
+ if (!existsSync4(this.dir)) {
1214
+ await mkdir3(this.dir, { recursive: true });
1215
+ }
1216
+ }
1217
+ filePath(taskId) {
1218
+ return join2(this.dir, `${taskId}.json`);
1219
+ }
1220
+ async save(taskId, state) {
1221
+ await this.ensureDir();
1222
+ const path = this.filePath(taskId);
1223
+ const tmpPath = path + ".tmp";
1224
+ await writeFile3(tmpPath, JSON.stringify(state, null, 2) + "\n", "utf-8");
1225
+ await rename2(tmpPath, path);
1226
+ }
1227
+ async load(taskId) {
1228
+ const path = this.filePath(taskId);
1229
+ if (!existsSync4(path)) {
1230
+ return null;
1231
+ }
1232
+ const content = await readFile3(path, "utf-8");
1233
+ return JSON.parse(content);
1234
+ }
1235
+ async remove(taskId) {
1236
+ const path = this.filePath(taskId);
1237
+ if (existsSync4(path)) {
1238
+ await unlink(path);
1239
+ }
1240
+ }
1241
+ async listIncomplete() {
1242
+ await this.ensureDir();
1243
+ const files = await readdir(this.dir);
1244
+ const results = [];
1245
+ for (const file of files) {
1246
+ if (!file.endsWith(".json")) continue;
1247
+ const path = join2(this.dir, file);
1248
+ try {
1249
+ const content = await readFile3(path, "utf-8");
1250
+ const state = JSON.parse(content);
1251
+ if (state.agentStatus !== "complete" && state.agentStatus !== "failed") {
1252
+ results.push({
1253
+ taskId: state.taskId,
1254
+ description: state.goal.description,
1255
+ savedAt: state.savedAt,
1256
+ isStale: this.isStale(state)
1257
+ });
1258
+ }
1259
+ } catch {
1260
+ }
1261
+ }
1262
+ return results;
1263
+ }
1264
+ isStale(checkpoint, maxAgeMs = DEFAULT_MAX_AGE_MS) {
1265
+ const savedTime = new Date(checkpoint.savedAt).getTime();
1266
+ const now = Date.now();
1267
+ return now - savedTime > maxAgeMs;
1268
+ }
1269
+ };
1270
+
1271
+ // src/memory/context-injector.ts
1272
+ var DEFAULT_MAX_TOKENS = 2e3;
1273
+ function estimateTokens2(text) {
1274
+ return Math.ceil(text.length / 4);
1275
+ }
1276
+ function compressEpisodicEntry(entry) {
1277
+ const date = entry.timestamp.slice(0, 10);
1278
+ const skills = entry.skillsUsed.length > 0 ? ` (${entry.skillsUsed.join(", ")})` : "";
1279
+ const lesson = entry.lessonsLearned.length > 0 ? ` Lesson: ${entry.lessonsLearned[0]}` : "";
1280
+ return `[${date}] '${entry.description.slice(0, 80)}' \u2192 ${entry.outcome}${skills}${lesson}`;
1281
+ }
1282
+ function compressProceduralEntry(entry) {
1283
+ const rate = Math.round(entry.successRate * 100);
1284
+ return `[skill] ${entry.skillName}: ${rate}% success over ${entry.invocationCount} invocations`;
1285
+ }
1286
+ function scoreEpisodicEntry(entry, goalWords) {
1287
+ const text = [entry.description, ...entry.lessonsLearned, ...entry.tags].join(" ").toLowerCase();
1288
+ let score = 0;
1289
+ for (const word of goalWords) {
1290
+ if (text.includes(word)) {
1291
+ score += 1;
1292
+ }
1293
+ }
1294
+ const ageMs = Date.now() - new Date(entry.timestamp).getTime();
1295
+ const ageDays = ageMs / (24 * 60 * 60 * 1e3);
1296
+ score += Math.max(0, 10 - ageDays) / 10;
1297
+ return score;
1298
+ }
1299
+ var ContextInjector = class {
1300
+ episodic;
1301
+ procedural;
1302
+ constructor(episodic, procedural) {
1303
+ this.episodic = episodic;
1304
+ this.procedural = procedural;
1305
+ }
1306
+ async inject(goal, maxTokens = DEFAULT_MAX_TOKENS) {
1307
+ const goalWords = goal.description.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
1308
+ const allEpisodic = await this.episodic.readAll();
1309
+ const scoredEpisodic = allEpisodic.map((entry) => ({ entry, score: scoreEpisodicEntry(entry, goalWords) })).filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
1310
+ const allProcedural = await this.procedural.load();
1311
+ const relevantProcedural = allProcedural.filter((entry) => {
1312
+ const nameLower = entry.skillName.toLowerCase();
1313
+ return goalWords.some((w) => nameLower.includes(w) || w.includes(nameLower));
1314
+ }).sort((a, b) => b.successRate * b.invocationCount - a.successRate * a.invocationCount);
1315
+ const results = [];
1316
+ let totalTokens = 0;
1317
+ let eIdx = 0;
1318
+ let pIdx = 0;
1319
+ while (totalTokens < maxTokens && (eIdx < scoredEpisodic.length || pIdx < relevantProcedural.length)) {
1320
+ for (let i = 0; i < 2 && eIdx < scoredEpisodic.length; i++, eIdx++) {
1321
+ const compressed = compressEpisodicEntry(scoredEpisodic[eIdx].entry);
1322
+ const tokens = estimateTokens2(compressed);
1323
+ if (totalTokens + tokens > maxTokens) {
1324
+ return results;
1325
+ }
1326
+ results.push(compressed);
1327
+ totalTokens += tokens;
1328
+ }
1329
+ if (pIdx < relevantProcedural.length) {
1330
+ const compressed = compressProceduralEntry(relevantProcedural[pIdx]);
1331
+ const tokens = estimateTokens2(compressed);
1332
+ if (totalTokens + tokens > maxTokens) {
1333
+ return results;
1334
+ }
1335
+ results.push(compressed);
1336
+ totalTokens += tokens;
1337
+ pIdx++;
1338
+ }
1339
+ }
1340
+ return results;
1341
+ }
1342
+ };
1343
+
1344
+ // src/rsi/metrics.ts
1345
+ import { readFile as readFile4, appendFile as appendFile2, mkdir as mkdir4 } from "fs/promises";
1346
+ import { existsSync as existsSync5 } from "fs";
1347
+ import { dirname as dirname4 } from "path";
1348
+ var MetricsCollector = class {
1349
+ taskMetricsPath;
1350
+ skillMetricsPath;
1351
+ constructor(taskMetricsPath, skillMetricsPath) {
1352
+ this.taskMetricsPath = taskMetricsPath;
1353
+ this.skillMetricsPath = skillMetricsPath;
1354
+ }
1355
+ async ensureDir(filePath) {
1356
+ const dir = dirname4(filePath);
1357
+ if (!existsSync5(dir)) {
1358
+ await mkdir4(dir, { recursive: true });
1359
+ }
1360
+ }
1361
+ async recordTaskMetrics(task) {
1362
+ await this.ensureDir(this.taskMetricsPath);
1363
+ const line = JSON.stringify(task) + "\n";
1364
+ await appendFile2(this.taskMetricsPath, line, "utf-8");
1365
+ }
1366
+ async recordSkillMetrics(skill) {
1367
+ await this.ensureDir(this.skillMetricsPath);
1368
+ const line = JSON.stringify(skill) + "\n";
1369
+ await appendFile2(this.skillMetricsPath, line, "utf-8");
1370
+ }
1371
+ async getTaskHistory(limit) {
1372
+ const entries = await this.readJsonl(this.taskMetricsPath);
1373
+ if (limit !== void 0) {
1374
+ return entries.slice(-limit);
1375
+ }
1376
+ return entries;
1377
+ }
1378
+ async getSkillHistory(skillName, limit) {
1379
+ const all = await this.readJsonl(this.skillMetricsPath);
1380
+ const filtered = all.filter((s) => s.skillName === skillName);
1381
+ if (limit !== void 0) {
1382
+ return filtered.slice(-limit);
1383
+ }
1384
+ return filtered;
1385
+ }
1386
+ async getAggregatedSkillStats(skillName) {
1387
+ const history = await this.getSkillHistory(skillName);
1388
+ if (history.length === 0) {
1389
+ return {
1390
+ skillName,
1391
+ totalInvocations: 0,
1392
+ successRate: 0,
1393
+ avgDurationMs: 0,
1394
+ trendDirection: "stable"
1395
+ };
1396
+ }
1397
+ const invokedEntries = history.filter((h) => h.invoked);
1398
+ const totalInvocations = invokedEntries.length;
1399
+ const successCount = invokedEntries.filter((h) => h.succeeded).length;
1400
+ const successRate = totalInvocations > 0 ? successCount / totalInvocations : 0;
1401
+ const avgDurationMs = totalInvocations > 0 ? invokedEntries.reduce((sum, h) => sum + h.durationMs, 0) / totalInvocations : 0;
1402
+ const trendDirection = this.calculateTrend(invokedEntries);
1403
+ return {
1404
+ skillName,
1405
+ totalInvocations,
1406
+ successRate,
1407
+ avgDurationMs,
1408
+ trendDirection
1409
+ };
1410
+ }
1411
+ calculateTrend(entries) {
1412
+ if (entries.length < 4) {
1413
+ return "stable";
1414
+ }
1415
+ const mid = Math.floor(entries.length / 2);
1416
+ const firstHalf = entries.slice(0, mid);
1417
+ const secondHalf = entries.slice(mid);
1418
+ const firstRate = firstHalf.filter((e) => e.succeeded).length / firstHalf.length;
1419
+ const secondRate = secondHalf.filter((e) => e.succeeded).length / secondHalf.length;
1420
+ const diff = secondRate - firstRate;
1421
+ if (diff > 0.1) return "improving";
1422
+ if (diff < -0.1) return "declining";
1423
+ return "stable";
1424
+ }
1425
+ async readJsonl(filePath) {
1426
+ if (!existsSync5(filePath)) {
1427
+ return [];
1428
+ }
1429
+ const content = await readFile4(filePath, "utf-8");
1430
+ const lines = content.split("\n").filter((line) => line.trim().length > 0);
1431
+ const results = [];
1432
+ for (const line of lines) {
1433
+ try {
1434
+ results.push(JSON.parse(line));
1435
+ } catch {
1436
+ }
1437
+ }
1438
+ return results;
1439
+ }
1440
+ };
1441
+
1442
+ // src/rsi/hypothesis.ts
1443
+ import { randomUUID } from "crypto";
1444
+ var MIN_DATA_POINTS = 10;
1445
+ var LOW_SUCCESS_THRESHOLD = 0.6;
1446
+ var SLOW_SKILL_PERCENTILE = 0.75;
1447
+ var HypothesisEngine = class {
1448
+ analyze(skillStats, taskHistory) {
1449
+ if (taskHistory.length < MIN_DATA_POINTS) {
1450
+ return [];
1451
+ }
1452
+ const hypotheses = [];
1453
+ hypotheses.push(...this.detectLowSuccessRate(skillStats));
1454
+ hypotheses.push(...this.detectSlowSkills(skillStats));
1455
+ hypotheses.push(...this.detectFrequentlyPairedSkills(taskHistory));
1456
+ hypotheses.push(...this.detectUnusedInSuccessful(skillStats, taskHistory));
1457
+ return hypotheses;
1458
+ }
1459
+ detectLowSuccessRate(stats) {
1460
+ const hypotheses = [];
1461
+ for (const skill of stats) {
1462
+ if (skill.totalInvocations >= 5 && skill.successRate < LOW_SUCCESS_THRESHOLD) {
1463
+ const expectedImprovement = Math.round((LOW_SUCCESS_THRESHOLD - skill.successRate) * 100);
1464
+ hypotheses.push({
1465
+ hypothesisId: randomUUID(),
1466
+ skillName: skill.skillName,
1467
+ description: `Adjusting retry count for ${skill.skillName} may improve success by ${expectedImprovement}%`,
1468
+ expectedImprovement,
1469
+ tier: "T1",
1470
+ confidence: Math.min(0.9, skill.totalInvocations / 20),
1471
+ evidence: [
1472
+ `Current success rate: ${Math.round(skill.successRate * 100)}%`,
1473
+ `Total invocations: ${skill.totalInvocations}`,
1474
+ `Trend: ${skill.trendDirection}`
1475
+ ]
1476
+ });
1477
+ }
1478
+ }
1479
+ return hypotheses;
1480
+ }
1481
+ detectSlowSkills(stats) {
1482
+ const hypotheses = [];
1483
+ const withInvocations = stats.filter((s) => s.totalInvocations >= 5);
1484
+ if (withInvocations.length < 2) return hypotheses;
1485
+ const durations = withInvocations.map((s) => s.avgDurationMs).sort((a, b) => a - b);
1486
+ const percentileIdx = Math.max(0, Math.floor(durations.length * SLOW_SKILL_PERCENTILE) - 1);
1487
+ const threshold = durations[percentileIdx] ?? Infinity;
1488
+ for (const skill of withInvocations) {
1489
+ if (skill.avgDurationMs > threshold) {
1490
+ const expectedImprovement = Math.round(
1491
+ (skill.avgDurationMs - threshold) / skill.avgDurationMs * 100
1492
+ );
1493
+ hypotheses.push({
1494
+ hypothesisId: randomUUID(),
1495
+ skillName: skill.skillName,
1496
+ description: `Increasing timeout for ${skill.skillName} may reduce failures by ${expectedImprovement}%`,
1497
+ expectedImprovement,
1498
+ tier: "T1",
1499
+ confidence: Math.min(0.8, skill.totalInvocations / 25),
1500
+ evidence: [
1501
+ `Avg duration: ${Math.round(skill.avgDurationMs)}ms`,
1502
+ `75th percentile threshold: ${Math.round(threshold)}ms`
1503
+ ]
1504
+ });
1505
+ }
1506
+ }
1507
+ return hypotheses;
1508
+ }
1509
+ detectFrequentlyPairedSkills(taskHistory) {
1510
+ const hypotheses = [];
1511
+ const pairCounts = /* @__PURE__ */ new Map();
1512
+ for (const task of taskHistory) {
1513
+ const skills = task.skillsUsed;
1514
+ for (let i = 0; i < skills.length; i++) {
1515
+ for (let j = i + 1; j < skills.length; j++) {
1516
+ const pair = [skills[i], skills[j]].sort().join("+");
1517
+ pairCounts.set(pair, (pairCounts.get(pair) ?? 0) + 1);
1518
+ }
1519
+ }
1520
+ }
1521
+ for (const [pair, count] of pairCounts) {
1522
+ if (count >= 5) {
1523
+ const [skillA, skillB] = pair.split("+");
1524
+ hypotheses.push({
1525
+ hypothesisId: randomUUID(),
1526
+ skillName: `${skillA}+${skillB}`,
1527
+ description: `Creating a chain of ${skillA}+${skillB} may improve efficiency`,
1528
+ expectedImprovement: Math.round(count / taskHistory.length * 30),
1529
+ tier: "T3",
1530
+ confidence: Math.min(0.7, count / 15),
1531
+ evidence: [
1532
+ `Co-occurrence: ${count}/${taskHistory.length} tasks`
1533
+ ]
1534
+ });
1535
+ }
1536
+ }
1537
+ return hypotheses;
1538
+ }
1539
+ detectUnusedInSuccessful(stats, taskHistory) {
1540
+ const hypotheses = [];
1541
+ const successfulTasks = taskHistory.filter((t) => t.outcome === "success");
1542
+ if (successfulTasks.length < 5) return hypotheses;
1543
+ const successfulSkills = /* @__PURE__ */ new Set();
1544
+ for (const task of successfulTasks) {
1545
+ for (const skill of task.skillsUsed) {
1546
+ successfulSkills.add(skill);
1547
+ }
1548
+ }
1549
+ for (const skill of stats) {
1550
+ if (skill.totalInvocations >= 5 && !successfulSkills.has(skill.skillName) && skill.successRate < 0.5) {
1551
+ hypotheses.push({
1552
+ hypothesisId: randomUUID(),
1553
+ skillName: skill.skillName,
1554
+ description: `Deprioritizing ${skill.skillName} for current task types`,
1555
+ expectedImprovement: 10,
1556
+ tier: "T2",
1557
+ confidence: 0.5,
1558
+ evidence: [
1559
+ `Not used in any of ${successfulTasks.length} successful tasks`,
1560
+ `Success rate: ${Math.round(skill.successRate * 100)}%`
1561
+ ]
1562
+ });
1563
+ }
1564
+ }
1565
+ return hypotheses;
1566
+ }
1567
+ };
1568
+
1569
+ // src/rsi/mutation.ts
1570
+ import { readFile as readFile5, appendFile as appendFile3, mkdir as mkdir5 } from "fs/promises";
1571
+ import { existsSync as existsSync6 } from "fs";
1572
+ import { dirname as dirname5 } from "path";
1573
+ import { randomUUID as randomUUID2 } from "crypto";
1574
+ var MutationEngine = class {
1575
+ historyPath;
1576
+ constructor(historyPath) {
1577
+ this.historyPath = historyPath;
1578
+ }
1579
+ async ensureDir() {
1580
+ const dir = dirname5(this.historyPath);
1581
+ if (!existsSync6(dir)) {
1582
+ await mkdir5(dir, { recursive: true });
1583
+ }
1584
+ }
1585
+ createMutation(hypothesis) {
1586
+ this.validateSafety(hypothesis);
1587
+ const initialStatus = hypothesis.tier === "T4" ? "proposed" : "proposed";
1588
+ return {
1589
+ mutationId: randomUUID2(),
1590
+ hypothesisId: hypothesis.hypothesisId,
1591
+ skillName: hypothesis.skillName,
1592
+ tier: hypothesis.tier,
1593
+ description: hypothesis.description,
1594
+ originalValue: "",
1595
+ mutatedValue: hypothesis.description,
1596
+ status: initialStatus,
1597
+ appliedAt: null,
1598
+ revertedAt: null
1599
+ };
1600
+ }
1601
+ async applyMutation(mutation) {
1602
+ if (this.isSafetyInvariant(mutation.skillName)) {
1603
+ throw new Error(
1604
+ `Cannot mutate safety invariant: ${mutation.skillName}. Safety invariants are: ${SAFETY_INVARIANTS.join(", ")}`
1605
+ );
1606
+ }
1607
+ if (mutation.tier === "T4") {
1608
+ throw new Error(
1609
+ "T4 mutations (Architecture) cannot be auto-applied. They must be proposed and reviewed by a human."
1610
+ );
1611
+ }
1612
+ const applied = {
1613
+ ...mutation,
1614
+ status: "applied",
1615
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString()
1616
+ };
1617
+ await this.appendHistory(applied);
1618
+ }
1619
+ async revertMutation(mutation) {
1620
+ const reverted = {
1621
+ ...mutation,
1622
+ status: "reverted",
1623
+ revertedAt: (/* @__PURE__ */ new Date()).toISOString()
1624
+ };
1625
+ await this.appendHistory(reverted);
1626
+ }
1627
+ async getMutationHistory() {
1628
+ if (!existsSync6(this.historyPath)) {
1629
+ return [];
1630
+ }
1631
+ const content = await readFile5(this.historyPath, "utf-8");
1632
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
1633
+ const results = [];
1634
+ for (const line of lines) {
1635
+ try {
1636
+ results.push(JSON.parse(line));
1637
+ } catch {
1638
+ }
1639
+ }
1640
+ return results;
1641
+ }
1642
+ async appendHistory(mutation) {
1643
+ await this.ensureDir();
1644
+ const line = JSON.stringify(mutation) + "\n";
1645
+ await appendFile3(this.historyPath, line, "utf-8");
1646
+ }
1647
+ validateSafety(hypothesis) {
1648
+ if (this.isSafetyInvariant(hypothesis.skillName)) {
1649
+ throw new Error(
1650
+ `Cannot create mutation targeting safety invariant: ${hypothesis.skillName}`
1651
+ );
1652
+ }
1653
+ }
1654
+ isSafetyInvariant(name) {
1655
+ const nameLower = name.toLowerCase();
1656
+ return SAFETY_INVARIANTS.some(
1657
+ (invariant) => nameLower.includes(invariant.toLowerCase()) || invariant.toLowerCase().includes(nameLower)
1658
+ );
1659
+ }
1660
+ };
1661
+
1662
+ // src/rsi/ab-test.ts
1663
+ import { randomUUID as randomUUID3 } from "crypto";
1664
+ var DEFAULT_MIN_SAMPLE_SIZE = 5;
1665
+ var PROMOTION_THRESHOLD = 0.1;
1666
+ var ROLLBACK_THRESHOLD = -0.1;
1667
+ var ABTestManager = class {
1668
+ tests = /* @__PURE__ */ new Map();
1669
+ results = /* @__PURE__ */ new Map();
1670
+ startTest(mutation, baselineMetrics) {
1671
+ const testId = randomUUID3();
1672
+ const test = {
1673
+ testId,
1674
+ mutationId: mutation.mutationId,
1675
+ skillName: mutation.skillName,
1676
+ baselineStats: baselineMetrics,
1677
+ variantStats: {
1678
+ skillName: mutation.skillName,
1679
+ totalInvocations: 0,
1680
+ successRate: 0,
1681
+ avgDurationMs: 0,
1682
+ trendDirection: "stable"
1683
+ },
1684
+ sampleSize: 0,
1685
+ minSampleSize: DEFAULT_MIN_SAMPLE_SIZE,
1686
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1687
+ status: "running"
1688
+ };
1689
+ this.tests.set(testId, test);
1690
+ this.results.set(testId, []);
1691
+ return test;
1692
+ }
1693
+ recordResult(testId, taskMetrics) {
1694
+ const test = this.tests.get(testId);
1695
+ if (!test) {
1696
+ throw new Error(`A/B test "${testId}" not found`);
1697
+ }
1698
+ if (test.status !== "running") {
1699
+ throw new Error(`A/B test "${testId}" is not running (status: ${test.status})`);
1700
+ }
1701
+ const taskResults = this.results.get(testId) ?? [];
1702
+ taskResults.push(taskMetrics);
1703
+ this.results.set(testId, taskResults);
1704
+ const successCount = taskResults.filter((t) => t.outcome === "success").length;
1705
+ const totalDuration = taskResults.reduce((sum, t) => sum + t.durationMs, 0);
1706
+ const updatedTest = {
1707
+ ...test,
1708
+ sampleSize: taskResults.length,
1709
+ variantStats: {
1710
+ skillName: test.skillName,
1711
+ totalInvocations: taskResults.length,
1712
+ successRate: taskResults.length > 0 ? successCount / taskResults.length : 0,
1713
+ avgDurationMs: taskResults.length > 0 ? totalDuration / taskResults.length : 0,
1714
+ trendDirection: "stable"
1715
+ }
1716
+ };
1717
+ this.tests.set(testId, updatedTest);
1718
+ }
1719
+ evaluateTest(testId) {
1720
+ const test = this.tests.get(testId);
1721
+ if (!test) {
1722
+ throw new Error(`A/B test "${testId}" not found`);
1723
+ }
1724
+ if (test.sampleSize < test.minSampleSize) {
1725
+ return {
1726
+ testId,
1727
+ decision: "continue",
1728
+ improvement: 0,
1729
+ confidence: test.sampleSize / test.minSampleSize
1730
+ };
1731
+ }
1732
+ const baselineRate = test.baselineStats.successRate;
1733
+ const variantRate = test.variantStats.successRate;
1734
+ const improvement = baselineRate > 0 ? (variantRate - baselineRate) / baselineRate : variantRate > 0 ? 1 : 0;
1735
+ const confidence = Math.min(1, test.sampleSize / (test.minSampleSize * 2));
1736
+ let decision;
1737
+ if (improvement > PROMOTION_THRESHOLD) {
1738
+ decision = "promote";
1739
+ } else if (improvement < ROLLBACK_THRESHOLD) {
1740
+ decision = "rollback";
1741
+ } else {
1742
+ decision = "continue";
1743
+ }
1744
+ if (decision === "promote" || decision === "rollback") {
1745
+ const updated = {
1746
+ ...test,
1747
+ status: "completed"
1748
+ };
1749
+ this.tests.set(testId, updated);
1750
+ }
1751
+ return {
1752
+ testId,
1753
+ decision,
1754
+ improvement,
1755
+ confidence
1756
+ };
1757
+ }
1758
+ getActiveTests() {
1759
+ const active = [];
1760
+ for (const test of this.tests.values()) {
1761
+ if (test.status === "running") {
1762
+ active.push(test);
1763
+ }
1764
+ }
1765
+ return active;
1766
+ }
1767
+ };
1768
+
1769
+ // src/rsi/audit.ts
1770
+ import { readFile as readFile6, appendFile as appendFile4, mkdir as mkdir6 } from "fs/promises";
1771
+ import { existsSync as existsSync7 } from "fs";
1772
+ import { dirname as dirname6 } from "path";
1773
+ var RSIAuditLog = class {
1774
+ filePath;
1775
+ constructor(filePath) {
1776
+ this.filePath = filePath;
1777
+ }
1778
+ async ensureDir() {
1779
+ const dir = dirname6(this.filePath);
1780
+ if (!existsSync7(dir)) {
1781
+ await mkdir6(dir, { recursive: true });
1782
+ }
1783
+ }
1784
+ async log(entry) {
1785
+ await this.ensureDir();
1786
+ const line = JSON.stringify(entry) + "\n";
1787
+ await appendFile4(this.filePath, line, "utf-8");
1788
+ }
1789
+ async getHistory(limit) {
1790
+ if (!existsSync7(this.filePath)) {
1791
+ return [];
1792
+ }
1793
+ const content = await readFile6(this.filePath, "utf-8");
1794
+ const lines = content.split("\n").filter((l) => l.trim().length > 0);
1795
+ const entries = [];
1796
+ for (const line of lines) {
1797
+ try {
1798
+ entries.push(JSON.parse(line));
1799
+ } catch {
1800
+ }
1801
+ }
1802
+ if (limit !== void 0) {
1803
+ return entries.slice(-limit);
1804
+ }
1805
+ return entries;
1806
+ }
1807
+ async getByMutation(mutationId) {
1808
+ const all = await this.getHistory();
1809
+ return all.filter((e) => e.mutationId === mutationId);
1810
+ }
1811
+ };
1812
+
1813
+ // src/rsi/auto-research.ts
1814
+ import { randomUUID as randomUUID4 } from "crypto";
1815
+ import { execSync } from "child_process";
1816
+ import { mkdirSync as mkdirSync2, existsSync as existsSync8, writeFileSync as writeFileSync2 } from "fs";
1817
+ import { join as join3 } from "path";
1818
+ import { tmpdir } from "os";
1819
+ var MIN_CONFIDENCE = 0.3;
1820
+ var REQUIRED_PASSING_RUNS = 3;
1821
+ function buildSearchQuery(failure) {
1822
+ const clean = failure.error.replace(/\x1b\[[0-9;]*m/g, "");
1823
+ const firstLine = clean.split("\n")[0]?.trim() ?? clean.trim();
1824
+ const noPath = firstLine.replace(/\/[^\s:]+:\d+:\d+/g, "").trim();
1825
+ const taskSummary = failure.taskDescription.slice(0, 60).trim();
1826
+ const errorSummary = noPath.slice(0, 100).trim();
1827
+ if (!errorSummary) {
1828
+ return `${taskSummary} fix solution`;
1829
+ }
1830
+ return `${taskSummary} ${errorSummary}`.trim();
1831
+ }
1832
+ function scoreConfidence(candidate, failure) {
1833
+ let base = 0;
1834
+ switch (candidate.source) {
1835
+ case "skill-catalog":
1836
+ base = 0.7;
1837
+ break;
1838
+ case "npm-registry":
1839
+ base = 0.5;
1840
+ break;
1841
+ case "web-search":
1842
+ base = 0.4;
1843
+ break;
1844
+ }
1845
+ const failureWords = new Set(
1846
+ failure.taskDescription.toLowerCase().split(/\W+/).filter((w) => w.length > 3)
1847
+ );
1848
+ const approachWords = candidate.approach.toLowerCase().split(/\W+/);
1849
+ const matches = approachWords.filter((w) => failureWords.has(w)).length;
1850
+ const overlap = Math.min(matches / Math.max(failureWords.size, 1), 0.25);
1851
+ base += overlap;
1852
+ if (candidate.description.length < 20) {
1853
+ base -= 0.1;
1854
+ }
1855
+ return Math.max(0, Math.min(1, base));
1856
+ }
1857
+ var AutoResearcher = class {
1858
+ skillsDir;
1859
+ constructor(skillsDir) {
1860
+ this.skillsDir = skillsDir ?? join3(tmpdir(), "clawpowers-promoted-skills");
1861
+ }
1862
+ /**
1863
+ * Search for candidate solutions to a failure.
1864
+ *
1865
+ * Sources queried (in order of reliability):
1866
+ * 1. skill-catalog — skills already available that match the error domain
1867
+ * 2. npm-registry — packages that match the error keywords
1868
+ * 3. web-search — constructs a query from the failure trace
1869
+ *
1870
+ * Returns candidates sorted by confidence (highest first).
1871
+ */
1872
+ async research(failure) {
1873
+ const candidates = [];
1874
+ const skillCatalogCandidates = this.searchSkillCatalog(failure);
1875
+ candidates.push(...skillCatalogCandidates);
1876
+ const npmCandidates = await this.searchNpmRegistry(failure);
1877
+ candidates.push(...npmCandidates);
1878
+ const webCandidates = this.buildWebSearchCandidates(failure);
1879
+ candidates.push(...webCandidates);
1880
+ return candidates.filter((c) => c.confidence >= MIN_CONFIDENCE).sort((a, b) => b.confidence - a.confidence);
1881
+ }
1882
+ /**
1883
+ * Test a candidate solution in an isolated sandbox.
1884
+ * Runs the candidate's approach as a shell command in a temp directory.
1885
+ * Returns a TestResult with pass/fail, output, and duration.
1886
+ */
1887
+ async testCandidate(candidate, task) {
1888
+ const startMs = Date.now();
1889
+ const sandboxDir = join3(tmpdir(), `clawpowers-sandbox-${randomUUID4()}`);
1890
+ try {
1891
+ mkdirSync2(sandboxDir, { recursive: true });
1892
+ const testScript = this.buildTestScript(candidate, task, sandboxDir);
1893
+ const scriptPath = join3(sandboxDir, "test.sh");
1894
+ writeFileSync2(scriptPath, testScript, { mode: 493 });
1895
+ const output = execSync(`bash "${scriptPath}"`, {
1896
+ cwd: sandboxDir,
1897
+ timeout: 3e4,
1898
+ encoding: "utf-8",
1899
+ stdio: ["ignore", "pipe", "pipe"]
1900
+ });
1901
+ return {
1902
+ passed: true,
1903
+ output: output.toString().slice(0, 2e3),
1904
+ durationMs: Date.now() - startMs,
1905
+ attempt: 1
1906
+ };
1907
+ } catch (err) {
1908
+ const output = err instanceof Error ? err.message : String(err);
1909
+ return {
1910
+ passed: false,
1911
+ output: output.slice(0, 2e3),
1912
+ durationMs: Date.now() - startMs,
1913
+ attempt: 1
1914
+ };
1915
+ } finally {
1916
+ try {
1917
+ execSync(`rm -rf "${sandboxDir}"`, { timeout: 5e3, stdio: "ignore" });
1918
+ } catch {
1919
+ }
1920
+ }
1921
+ }
1922
+ /**
1923
+ * Run a candidate through REQUIRED_PASSING_RUNS sandbox tests.
1924
+ * Returns all TestResult objects. If fewer than REQUIRED_PASSING_RUNS pass,
1925
+ * the candidate is NOT promoted (caller must check).
1926
+ */
1927
+ async runSandboxTests(candidate, task) {
1928
+ const results = [];
1929
+ for (let i = 1; i <= REQUIRED_PASSING_RUNS; i++) {
1930
+ const result = await this.testCandidate(candidate, task);
1931
+ results.push({ ...result, attempt: i });
1932
+ if (!result.passed) {
1933
+ break;
1934
+ }
1935
+ }
1936
+ return results;
1937
+ }
1938
+ /**
1939
+ * Promote a candidate solution to a new skill definition.
1940
+ * Writes a minimal SKILL.md to the skills directory and returns
1941
+ * the SkillDefinition.
1942
+ */
1943
+ async promoteToSkill(candidate, testResults) {
1944
+ const passingRuns = testResults.filter((r) => r.passed).length;
1945
+ if (passingRuns < REQUIRED_PASSING_RUNS) {
1946
+ throw new Error(
1947
+ `Cannot promote candidate: only ${passingRuns}/${REQUIRED_PASSING_RUNS} test runs passed.`
1948
+ );
1949
+ }
1950
+ const skillId = `auto-${randomUUID4().slice(0, 8)}`;
1951
+ const skillName = this.deriveSkillName(candidate);
1952
+ const definition = {
1953
+ skillId,
1954
+ name: skillName,
1955
+ description: candidate.description,
1956
+ approach: candidate.approach,
1957
+ source: candidate.source,
1958
+ promotedAt: (/* @__PURE__ */ new Date()).toISOString(),
1959
+ testResults
1960
+ };
1961
+ const skillDir = join3(this.skillsDir, skillName);
1962
+ if (!existsSync8(skillDir)) {
1963
+ mkdirSync2(skillDir, { recursive: true });
1964
+ }
1965
+ const skillMd = this.renderSkillMd(definition);
1966
+ writeFileSync2(join3(skillDir, "SKILL.md"), skillMd, "utf-8");
1967
+ return definition;
1968
+ }
1969
+ // ─── Private Methods ───────────────────────────────────────────────────────
1970
+ searchSkillCatalog(failure) {
1971
+ try {
1972
+ const { SKILLS_CATALOG: SKILLS_CATALOG2 } = (init_catalog(), __toCommonJS(catalog_exports));
1973
+ const errorTokens = failure.error.toLowerCase().split(/\W+/).filter((t) => t.length > 3);
1974
+ const taskTokens = failure.taskDescription.toLowerCase().split(/\W+/).filter((t) => t.length > 3);
1975
+ const relevantTokens = /* @__PURE__ */ new Set([...errorTokens, ...taskTokens]);
1976
+ return SKILLS_CATALOG2.filter((skill) => {
1977
+ const haystack = `${skill.name} ${skill.description}`.toLowerCase();
1978
+ return [...relevantTokens].some((token) => haystack.includes(token));
1979
+ }).slice(0, 3).map((skill) => {
1980
+ const base = {
1981
+ source: "skill-catalog",
1982
+ description: skill.description,
1983
+ approach: `Use the '${skill.name}' skill (category: ${skill.category}) to address this failure.`
1984
+ };
1985
+ return {
1986
+ ...base,
1987
+ confidence: scoreConfidence(base, failure)
1988
+ };
1989
+ });
1990
+ } catch {
1991
+ return [];
1992
+ }
1993
+ }
1994
+ async searchNpmRegistry(failure) {
1995
+ const query = buildSearchQuery(failure);
1996
+ const keywords = query.split(/\s+/).filter((w) => w.length > 3).slice(0, 5).join("+");
1997
+ try {
1998
+ const raw = execSync(
1999
+ `npm search ${keywords} --json --no-description 2>/dev/null`,
2000
+ {
2001
+ timeout: 15e3,
2002
+ encoding: "utf-8",
2003
+ stdio: ["ignore", "pipe", "ignore"]
2004
+ }
2005
+ );
2006
+ const results = JSON.parse(raw);
2007
+ return results.slice(0, 3).map((pkg) => {
2008
+ const base = {
2009
+ source: "npm-registry",
2010
+ description: pkg.description ?? pkg.name,
2011
+ approach: `Install and use npm package '${pkg.name}' to resolve this failure.`
2012
+ };
2013
+ return {
2014
+ ...base,
2015
+ confidence: scoreConfidence(base, failure)
2016
+ };
2017
+ });
2018
+ } catch {
2019
+ return [];
2020
+ }
2021
+ }
2022
+ buildWebSearchCandidates(failure) {
2023
+ const query = buildSearchQuery(failure);
2024
+ const base = {
2025
+ source: "web-search",
2026
+ description: `Web search for: "${query}"`,
2027
+ approach: `Search the web for "${query}" and apply the top-ranked solution approach.`
2028
+ };
2029
+ return [
2030
+ {
2031
+ ...base,
2032
+ confidence: scoreConfidence(base, failure)
2033
+ }
2034
+ ];
2035
+ }
2036
+ /**
2037
+ * Build a sandboxed test script for a candidate solution.
2038
+ * Validates the candidate's approach by checking its core prerequisites
2039
+ * (e.g. that a package is installed, or a command is available).
2040
+ */
2041
+ buildTestScript(candidate, task, _sandboxDir) {
2042
+ const lines = [
2043
+ "#!/usr/bin/env bash",
2044
+ "set -euo pipefail",
2045
+ "",
2046
+ "# AutoResearch sandbox test",
2047
+ `# Task: ${task.description.replace(/'/g, "'\\''").slice(0, 200)}`,
2048
+ `# Candidate source: ${candidate.source}`,
2049
+ `# Candidate: ${candidate.description.replace(/'/g, "'\\''").slice(0, 200)}`,
2050
+ ""
2051
+ ];
2052
+ if (candidate.source === "npm-registry") {
2053
+ const match = /npm package '([^']+)'/.exec(candidate.approach);
2054
+ if (match) {
2055
+ const pkgName = match[1];
2056
+ lines.push(`# Check if the npm package exists in the registry`);
2057
+ lines.push(`npm view "${pkgName}" name > /dev/null 2>&1`);
2058
+ lines.push(`echo "Package '${pkgName}' exists in npm registry"`);
2059
+ } else {
2060
+ lines.push('echo "npm-registry candidate: no package name extractable"');
2061
+ lines.push("exit 0");
2062
+ }
2063
+ } else if (candidate.source === "skill-catalog") {
2064
+ const match = /skill '([^']+)'/.exec(candidate.approach);
2065
+ if (match) {
2066
+ lines.push(`# Validate skill '${match[1]}' reference`);
2067
+ lines.push(`echo "Skill catalog candidate: ${match[1]}"`);
2068
+ lines.push("exit 0");
2069
+ } else {
2070
+ lines.push('echo "skill-catalog candidate validated"');
2071
+ lines.push("exit 0");
2072
+ }
2073
+ } else {
2074
+ const q = buildSearchQuery({ error: candidate.description, taskDescription: task.description, executionSteps: [], skillsUsed: [], attemptCount: 1 });
2075
+ if (q.trim().length > 0) {
2076
+ lines.push(`echo "web-search candidate: query is '${q.replace(/'/g, "").slice(0, 100)}'"`);
2077
+ lines.push("exit 0");
2078
+ } else {
2079
+ lines.push('echo "web-search candidate: empty query" >&2');
2080
+ lines.push("exit 1");
2081
+ }
2082
+ }
2083
+ lines.push("");
2084
+ lines.push("# Success criteria check");
2085
+ for (const criterion of task.successCriteria.slice(0, 2)) {
2086
+ lines.push(`echo "Criterion: ${criterion.replace(/'/g, "").slice(0, 100)}"`);
2087
+ }
2088
+ lines.push('echo "Test passed"');
2089
+ return lines.join("\n") + "\n";
2090
+ }
2091
+ deriveSkillName(candidate) {
2092
+ const slug = candidate.description.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
2093
+ return `auto-${slug}`;
2094
+ }
2095
+ renderSkillMd(def) {
2096
+ return [
2097
+ "---",
2098
+ `name: ${def.name}`,
2099
+ `description: "${def.description.replace(/"/g, "'")}"`,
2100
+ `source: ${def.source}`,
2101
+ `skillId: ${def.skillId}`,
2102
+ `promotedAt: "${def.promotedAt}"`,
2103
+ "---",
2104
+ "",
2105
+ `# ${def.name}`,
2106
+ "",
2107
+ `**Auto-promoted by AutoResearcher on ${def.promotedAt}**`,
2108
+ "",
2109
+ `## Description`,
2110
+ "",
2111
+ def.description,
2112
+ "",
2113
+ `## Approach`,
2114
+ "",
2115
+ def.approach,
2116
+ "",
2117
+ `## Test Results`,
2118
+ "",
2119
+ `Passed ${def.testResults.filter((r) => r.passed).length}/${def.testResults.length} sandbox runs.`,
2120
+ ""
2121
+ ].join("\n");
2122
+ }
2123
+ };
2124
+ async function runAutoResearch(failure, task, skillsDir) {
2125
+ const researcher = new AutoResearcher(skillsDir);
2126
+ const candidates = await researcher.research(failure);
2127
+ if (candidates.length === 0) {
2128
+ return null;
2129
+ }
2130
+ for (const candidate of candidates) {
2131
+ const results = await researcher.runSandboxTests(candidate, task);
2132
+ const passingRuns = results.filter((r) => r.passed).length;
2133
+ if (passingRuns >= REQUIRED_PASSING_RUNS) {
2134
+ const skill = await researcher.promoteToSkill(candidate, results);
2135
+ return skill;
2136
+ }
2137
+ }
2138
+ return null;
2139
+ }
2140
+
2141
+ // src/skills/loader.ts
2142
+ import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync9, statSync } from "fs";
2143
+ import { join as join4 } from "path";
2144
+ function parseFrontmatter(content) {
2145
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
2146
+ if (!match?.[1]) {
2147
+ return {};
2148
+ }
2149
+ const yamlText = match[1];
2150
+ const result = {};
2151
+ const lines = yamlText.split("\n");
2152
+ let currentKey = "";
2153
+ let inRequires = false;
2154
+ let requiresKey = "";
2155
+ const requires = {};
2156
+ for (const line of lines) {
2157
+ const trimmed = line.trim();
2158
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
2159
+ const topLevel = line.match(/^(\w+):\s*(.*)$/);
2160
+ if (topLevel?.[1]) {
2161
+ currentKey = topLevel[1];
2162
+ const val = topLevel[2]?.trim().replace(/^["']|["']$/g, "") ?? "";
2163
+ if (currentKey === "name" && val) result.name = val;
2164
+ if (currentKey === "description" && val) result.description = val;
2165
+ inRequires = false;
2166
+ continue;
2167
+ }
2168
+ if (trimmed === "openclaw:") continue;
2169
+ if (trimmed === "requires:") {
2170
+ inRequires = true;
2171
+ continue;
2172
+ }
2173
+ if (inRequires) {
2174
+ const reqKey = trimmed.match(/^(\w+):\s*(.*)$/);
2175
+ if (reqKey?.[1]) {
2176
+ requiresKey = reqKey[1];
2177
+ const inlineArr = reqKey[2]?.match(/\[(.*)\]/);
2178
+ if (inlineArr?.[1]) {
2179
+ const items = inlineArr[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
2180
+ if (requiresKey === "bins") requires.bins = items;
2181
+ if (requiresKey === "env") requires.env = items;
2182
+ if (requiresKey === "config") requires.config = items;
2183
+ }
2184
+ continue;
2185
+ }
2186
+ const listItem = trimmed.match(/^-\s*["']?(.+?)["']?$/);
2187
+ if (listItem?.[1] && requiresKey) {
2188
+ if (requiresKey === "bins") {
2189
+ requires.bins = requires.bins ?? [];
2190
+ requires.bins.push(listItem[1]);
2191
+ }
2192
+ if (requiresKey === "env") {
2193
+ requires.env = requires.env ?? [];
2194
+ requires.env.push(listItem[1]);
2195
+ }
2196
+ if (requiresKey === "config") {
2197
+ requires.config = requires.config ?? [];
2198
+ requires.config.push(listItem[1]);
2199
+ }
2200
+ }
2201
+ }
2202
+ }
2203
+ if (Object.keys(requires).length > 0) {
2204
+ result.metadata = { openclaw: { requires } };
2205
+ }
2206
+ return result;
2207
+ }
2208
+ function loadSkillManifest(skillDir) {
2209
+ const skillMdPath = join4(skillDir, "SKILL.md");
2210
+ if (!existsSync9(skillMdPath)) {
2211
+ return null;
2212
+ }
2213
+ const content = readFileSync2(skillMdPath, "utf-8");
2214
+ const frontmatter = parseFrontmatter(content);
2215
+ if (!frontmatter.name || !frontmatter.description) {
2216
+ return null;
2217
+ }
2218
+ let requirements = null;
2219
+ const req = frontmatter.metadata?.openclaw?.requires;
2220
+ if (req) {
2221
+ requirements = {
2222
+ bins: req.bins ?? [],
2223
+ env: req.env ?? [],
2224
+ config: req.config ?? []
2225
+ };
2226
+ }
2227
+ return {
2228
+ name: frontmatter.name,
2229
+ description: frontmatter.description,
2230
+ path: skillDir,
2231
+ requirements
2232
+ };
2233
+ }
2234
+ function discoverSkills(skillsDir) {
2235
+ if (!existsSync9(skillsDir)) {
2236
+ return [];
2237
+ }
2238
+ const entries = readdirSync(skillsDir);
2239
+ const manifests = [];
2240
+ for (const entry of entries) {
2241
+ const fullPath = join4(skillsDir, entry);
2242
+ if (!statSync(fullPath).isDirectory()) continue;
2243
+ const manifest = loadSkillManifest(fullPath);
2244
+ if (manifest) {
2245
+ manifests.push(manifest);
2246
+ }
2247
+ }
2248
+ return manifests.sort((a, b) => a.name.localeCompare(b.name));
2249
+ }
2250
+ function getActiveSkills(allSkills, profile) {
2251
+ const profileSkillSet = new Set(profile.skills);
2252
+ return allSkills.filter((skill) => profileSkillSet.has(skill.name));
2253
+ }
2254
+ function listSkillsWithStatus(allSkills, profile) {
2255
+ const profileSkillSet = new Set(profile.skills);
2256
+ return allSkills.map((skill) => ({
2257
+ skill,
2258
+ active: profileSkillSet.has(skill.name)
2259
+ }));
2260
+ }
2261
+
2262
+ // src/skills/executor.ts
2263
+ var SkillExecutor = class {
2264
+ constructor(skillsDir, memory) {
2265
+ this.skillsDir = skillsDir;
2266
+ this.memory = memory;
2267
+ }
2268
+ skillsDir;
2269
+ memory;
2270
+ async execute(skillName, context) {
2271
+ const start = Date.now();
2272
+ try {
2273
+ const result = {
2274
+ success: true,
2275
+ output: `Skill ${skillName} loaded from ${this.skillsDir}`,
2276
+ durationMs: Date.now() - start
2277
+ };
2278
+ await this.memory.update(skillName, {
2279
+ succeeded: true,
2280
+ durationMs: result.durationMs,
2281
+ taskId: context.taskId
2282
+ });
2283
+ return result;
2284
+ } catch (err) {
2285
+ const errorMessage = err instanceof Error ? err.message : String(err);
2286
+ await this.memory.update(skillName, {
2287
+ succeeded: false,
2288
+ durationMs: Date.now() - start,
2289
+ taskId: context.taskId
2290
+ });
2291
+ return {
2292
+ success: false,
2293
+ output: "",
2294
+ durationMs: Date.now() - start,
2295
+ error: errorMessage
2296
+ };
2297
+ }
2298
+ }
2299
+ };
2300
+
2301
+ // src/wallet/manager.ts
2302
+ import { readdir as readdir2, readFile as readFile8 } from "fs/promises";
2303
+ import { existsSync as existsSync11 } from "fs";
2304
+ import { join as join6 } from "path";
2305
+
2306
+ // src/wallet/crypto.ts
2307
+ import { randomBytes, createCipheriv, createDecipheriv, scryptSync, createHash } from "crypto";
2308
+ import { writeFile as writeFile4, readFile as readFile7, mkdir as mkdir7 } from "fs/promises";
2309
+ import { existsSync as existsSync10 } from "fs";
2310
+ import { join as join5 } from "path";
2311
+ var SCRYPT_N = 16384;
2312
+ var SCRYPT_R = 8;
2313
+ var SCRYPT_P = 1;
2314
+ var KEY_LENGTH = 32;
2315
+ var IV_LENGTH = 12;
2316
+ var AUTH_TAG_LENGTH = 16;
2317
+ function addressHash(publicKeyBytes) {
2318
+ const hash = createHash("sha256").update(publicKeyBytes).digest();
2319
+ return "0x" + hash.subarray(hash.length - 20).toString("hex");
2320
+ }
2321
+ function deriveKey(passphrase, salt) {
2322
+ return scryptSync(passphrase, salt, KEY_LENGTH, {
2323
+ N: SCRYPT_N,
2324
+ r: SCRYPT_R,
2325
+ p: SCRYPT_P
2326
+ });
2327
+ }
2328
+ function encryptPrivateKey(privateKey, passphrase) {
2329
+ const salt = randomBytes(32);
2330
+ const key = deriveKey(passphrase, salt);
2331
+ const iv = randomBytes(IV_LENGTH);
2332
+ const cipher = createCipheriv("aes-256-gcm", key, iv, { authTagLength: AUTH_TAG_LENGTH });
2333
+ const encrypted = Buffer.concat([cipher.update(privateKey), cipher.final()]);
2334
+ const authTag = cipher.getAuthTag();
2335
+ return {
2336
+ ciphertext: encrypted.toString("hex"),
2337
+ iv: iv.toString("hex"),
2338
+ authTag: authTag.toString("hex"),
2339
+ salt: salt.toString("hex")
2340
+ };
2341
+ }
2342
+ function decryptPrivateKey(ciphertext, iv, authTag, salt, passphrase) {
2343
+ const key = deriveKey(passphrase, Buffer.from(salt, "hex"));
2344
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"), {
2345
+ authTagLength: AUTH_TAG_LENGTH
2346
+ });
2347
+ decipher.setAuthTag(Buffer.from(authTag, "hex"));
2348
+ const decrypted = Buffer.concat([
2349
+ decipher.update(Buffer.from(ciphertext, "hex")),
2350
+ decipher.final()
2351
+ ]);
2352
+ return decrypted;
2353
+ }
2354
+ function generateAddress(privateKeyHex) {
2355
+ const privBuf = Buffer.from(privateKeyHex, "hex");
2356
+ return addressHash(privBuf);
2357
+ }
2358
+ async function ensureDir(dir) {
2359
+ if (!existsSync10(dir)) {
2360
+ await mkdir7(dir, { recursive: true });
2361
+ }
2362
+ }
2363
+ async function generateWallet(config) {
2364
+ const privateKey = randomBytes(32);
2365
+ const privateKeyHex = privateKey.toString("hex");
2366
+ const address = generateAddress(privateKeyHex);
2367
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2368
+ const passphrase = randomBytes(16).toString("hex");
2369
+ const encrypted = encryptPrivateKey(privateKey, passphrase);
2370
+ const keyFileData = {
2371
+ version: 1,
2372
+ address,
2373
+ chain: config.chain,
2374
+ createdAt,
2375
+ crypto: {
2376
+ cipher: "aes-256-gcm",
2377
+ ciphertext: encrypted.ciphertext,
2378
+ iv: encrypted.iv,
2379
+ authTag: encrypted.authTag,
2380
+ salt: encrypted.salt,
2381
+ scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
2382
+ }
2383
+ };
2384
+ await ensureDir(config.dataDir);
2385
+ const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
2386
+ const keyFilePath = join5(config.dataDir, keyFileName);
2387
+ await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
2388
+ return {
2389
+ address,
2390
+ chain: config.chain,
2391
+ createdAt,
2392
+ keyFile: keyFilePath
2393
+ };
2394
+ }
2395
+ async function importWallet(privateKeyHex, config) {
2396
+ const cleaned = privateKeyHex.replace(/^0x/, "");
2397
+ if (cleaned.length !== 64 || !/^[0-9a-fA-F]+$/.test(cleaned)) {
2398
+ throw new Error("Invalid private key: must be 32 bytes (64 hex characters)");
2399
+ }
2400
+ const privateKey = Buffer.from(cleaned, "hex");
2401
+ const address = generateAddress(cleaned);
2402
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2403
+ const passphrase = randomBytes(16).toString("hex");
2404
+ const encrypted = encryptPrivateKey(privateKey, passphrase);
2405
+ const keyFileData = {
2406
+ version: 1,
2407
+ address,
2408
+ chain: config.chain,
2409
+ createdAt,
2410
+ crypto: {
2411
+ cipher: "aes-256-gcm",
2412
+ ciphertext: encrypted.ciphertext,
2413
+ iv: encrypted.iv,
2414
+ authTag: encrypted.authTag,
2415
+ salt: encrypted.salt,
2416
+ scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
2417
+ }
2418
+ };
2419
+ await ensureDir(config.dataDir);
2420
+ const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
2421
+ const keyFilePath = join5(config.dataDir, keyFileName);
2422
+ await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
2423
+ return {
2424
+ address,
2425
+ chain: config.chain,
2426
+ createdAt,
2427
+ keyFile: keyFilePath
2428
+ };
2429
+ }
2430
+ async function signMessage(message, keyFile, passphrase) {
2431
+ const content = await readFile7(keyFile, "utf-8");
2432
+ const keyFileData = JSON.parse(content);
2433
+ const privateKey = decryptPrivateKey(
2434
+ keyFileData.crypto.ciphertext,
2435
+ keyFileData.crypto.iv,
2436
+ keyFileData.crypto.authTag,
2437
+ keyFileData.crypto.salt,
2438
+ passphrase
2439
+ );
2440
+ const { createHmac } = await import("crypto");
2441
+ const signature = createHmac("sha256", privateKey).update(message).digest("hex");
2442
+ return {
2443
+ message,
2444
+ signature: "0x" + signature,
2445
+ address: keyFileData.address
2446
+ };
2447
+ }
2448
+
2449
+ // src/wallet/manager.ts
2450
+ var WalletManager = class {
2451
+ constructor(config) {
2452
+ this.config = config;
2453
+ }
2454
+ config;
2455
+ async generate() {
2456
+ return generateWallet(this.config);
2457
+ }
2458
+ async import(privateKey) {
2459
+ return importWallet(privateKey, this.config);
2460
+ }
2461
+ async sign(message, walletInfo, passphrase) {
2462
+ const result = await signMessage(message, walletInfo.keyFile, passphrase);
2463
+ return result.signature;
2464
+ }
2465
+ async listWallets() {
2466
+ if (!existsSync11(this.config.dataDir)) {
2467
+ return [];
2468
+ }
2469
+ const files = await readdir2(this.config.dataDir);
2470
+ const wallets = [];
2471
+ for (const file of files) {
2472
+ if (!file.endsWith(".json")) continue;
2473
+ try {
2474
+ const filePath = join6(this.config.dataDir, file);
2475
+ const content = await readFile8(filePath, "utf-8");
2476
+ const data = JSON.parse(content);
2477
+ wallets.push({
2478
+ address: data.address,
2479
+ chain: data.chain,
2480
+ createdAt: data.createdAt,
2481
+ keyFile: filePath
2482
+ });
2483
+ } catch {
2484
+ }
2485
+ }
2486
+ return wallets;
2487
+ }
2488
+ };
2489
+ export {
2490
+ ABTestManager,
2491
+ AutoResearcher,
2492
+ CLAWPOWERS_HOME,
2493
+ CheckpointManager,
2494
+ ContextInjector,
2495
+ DEFAULT_CONFIG,
2496
+ EpisodicMemory,
2497
+ HypothesisEngine,
2498
+ MetricsCollector,
2499
+ MutationEngine,
2500
+ PACKAGE_NAME,
2501
+ PaymentExecutor,
2502
+ ProceduralMemory,
2503
+ RSIAuditLog,
2504
+ SkillExecutor,
2505
+ SpendingPolicy,
2506
+ VERSION,
2507
+ WalletManager,
2508
+ WorkingMemoryManager,
2509
+ detect402,
2510
+ discoverSkills,
2511
+ generateWallet,
2512
+ getActiveSkills,
2513
+ getConfigValue,
2514
+ importWallet,
2515
+ initConfig,
2516
+ isPaymentRequired,
2517
+ listSkillsWithStatus,
2518
+ loadConfig,
2519
+ loadConfigSafe,
2520
+ loadSkillManifest,
2521
+ parseFrontmatter,
2522
+ runAutoResearch,
2523
+ saveConfig,
2524
+ setConfigValue,
2525
+ signMessage
2526
+ };
2527
+ /**
2528
+ * ClawPowers — Skills Library for AI Agents
2529
+ * Drop-in capability layer: payments, memory, RSI, wallet.
2530
+ * No agent control loop — bring your own agent.
2531
+ *
2532
+ * @version 2.0.0
2533
+ * @license BSL-1.1
2534
+ * @patent-pending
2535
+ */
2536
+ //# sourceMappingURL=index.js.map