clawpowers 1.1.4 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -0
- package/COMPATIBILITY.md +13 -0
- package/KNOWN_LIMITATIONS.md +19 -0
- package/LICENSE +44 -0
- package/LICENSING.md +10 -0
- package/README.md +378 -210
- package/SECURITY.md +52 -0
- package/dist/index.d.ts +1477 -0
- package/dist/index.js +3464 -0
- package/dist/index.js.map +1 -0
- package/native/Cargo.lock +4863 -0
- package/native/Cargo.toml +73 -0
- package/native/crates/canonical/Cargo.toml +24 -0
- package/native/crates/canonical/src/lib.rs +673 -0
- package/native/crates/compression/Cargo.toml +20 -0
- package/native/crates/compression/benches/compression_bench.rs +42 -0
- package/native/crates/compression/src/lib.rs +393 -0
- package/native/crates/evm-eth/Cargo.toml +13 -0
- package/native/crates/evm-eth/src/lib.rs +105 -0
- package/native/crates/fee/Cargo.toml +15 -0
- package/native/crates/fee/src/lib.rs +281 -0
- package/native/crates/index/Cargo.toml +16 -0
- package/native/crates/index/src/lib.rs +277 -0
- package/native/crates/policy/Cargo.toml +17 -0
- package/native/crates/policy/src/lib.rs +614 -0
- package/native/crates/security/Cargo.toml +22 -0
- package/native/crates/security/src/lib.rs +478 -0
- package/native/crates/tokens/Cargo.toml +13 -0
- package/native/crates/tokens/src/lib.rs +534 -0
- package/native/crates/verification/Cargo.toml +23 -0
- package/native/crates/verification/src/lib.rs +333 -0
- package/native/crates/wallet/Cargo.toml +20 -0
- package/native/crates/wallet/src/lib.rs +261 -0
- package/native/crates/x402/Cargo.toml +30 -0
- package/native/crates/x402/src/lib.rs +423 -0
- package/native/ffi/Cargo.toml +34 -0
- package/native/ffi/build.rs +4 -0
- package/native/ffi/index.node +0 -0
- package/native/ffi/src/lib.rs +352 -0
- package/native/ffi/tests/integration.rs +354 -0
- package/native/pyo3/Cargo.toml +26 -0
- package/native/pyo3/pyproject.toml +16 -0
- package/native/pyo3/src/lib.rs +407 -0
- package/native/pyo3/tests/test_smoke.py +180 -0
- package/native/wasm/Cargo.toml +44 -0
- package/native/wasm/pkg/.gitignore +6 -0
- package/native/wasm/pkg/clawpowers_wasm.d.ts +208 -0
- package/native/wasm/pkg/clawpowers_wasm.js +872 -0
- package/native/wasm/pkg/clawpowers_wasm_bg.wasm +0 -0
- package/native/wasm/pkg/clawpowers_wasm_bg.wasm.d.ts +40 -0
- package/native/wasm/pkg/package.json +17 -0
- package/native/wasm/pkg-node/.gitignore +6 -0
- package/native/wasm/pkg-node/clawpowers_wasm.d.ts +143 -0
- package/native/wasm/pkg-node/clawpowers_wasm.js +798 -0
- package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm +0 -0
- package/native/wasm/pkg-node/clawpowers_wasm_bg.wasm.d.ts +40 -0
- package/native/wasm/pkg-node/package.json +13 -0
- package/native/wasm/src/lib.rs +433 -0
- package/package.json +71 -44
- package/src/skills/catalog.ts +435 -0
- package/src/skills/executor.ts +56 -0
- package/src/skills/index.ts +3 -0
- package/src/skills/itp/SKILL.md +112 -0
- package/src/skills/loader.ts +193 -0
- package/.claude-plugin/manifest.json +0 -19
- package/.codex/INSTALL.md +0 -36
- package/.cursor-plugin/manifest.json +0 -21
- package/.opencode/INSTALL.md +0 -52
- package/ARCHITECTURE.md +0 -69
- package/bin/clawpowers.js +0 -625
- package/bin/clawpowers.sh +0 -91
- package/docs/demo/clawpowers-demo.cast +0 -197
- package/docs/demo/clawpowers-demo.gif +0 -0
- package/docs/launch-images/25-skills-breakdown.jpg +0 -0
- package/docs/launch-images/clawpowers-vs-superpowers.jpg +0 -0
- package/docs/launch-images/economic-code-optimization.jpg +0 -0
- package/docs/launch-images/native-vs-bridge-2.jpg +0 -0
- package/docs/launch-images/native-vs-bridge.jpg +0 -0
- package/docs/launch-images/post1-hero-lobster.jpg +0 -0
- package/docs/launch-images/post2-dashboard.jpg +0 -0
- package/docs/launch-images/post3-superpowers.jpg +0 -0
- package/docs/launch-images/post4-before-after.jpg +0 -0
- package/docs/launch-images/post5-install-now.jpg +0 -0
- package/docs/launch-images/ultimate-stack.jpg +0 -0
- package/docs/launch-posts.md +0 -76
- package/docs/quickstart-first-transaction.md +0 -204
- package/gemini-extension.json +0 -32
- package/hooks/session-start +0 -205
- package/hooks/session-start.cmd +0 -43
- package/hooks/session-start.js +0 -163
- package/runtime/demo/README.md +0 -78
- package/runtime/demo/x402-mock-server.js +0 -230
- package/runtime/feedback/analyze.js +0 -621
- package/runtime/feedback/analyze.sh +0 -546
- package/runtime/init.js +0 -210
- package/runtime/init.sh +0 -178
- package/runtime/metrics/collector.js +0 -361
- package/runtime/metrics/collector.sh +0 -308
- package/runtime/payments/ledger.js +0 -305
- package/runtime/payments/ledger.sh +0 -262
- package/runtime/payments/pipeline.js +0 -455
- package/runtime/persistence/store.js +0 -433
- package/runtime/persistence/store.sh +0 -303
- package/skill.json +0 -106
- package/skills/agent-bounties/SKILL.md +0 -553
- package/skills/agent-payments/SKILL.md +0 -479
- package/skills/brainstorming/SKILL.md +0 -233
- package/skills/content-pipeline/SKILL.md +0 -282
- package/skills/cross-project-knowledge/SKILL.md +0 -345
- package/skills/dispatching-parallel-agents/SKILL.md +0 -305
- package/skills/economic-code-optimization/SKILL.md +0 -265
- package/skills/executing-plans/SKILL.md +0 -255
- package/skills/finishing-a-development-branch/SKILL.md +0 -260
- package/skills/formal-verification-lite/SKILL.md +0 -441
- package/skills/learn-how-to-learn/SKILL.md +0 -235
- package/skills/market-intelligence/SKILL.md +0 -323
- package/skills/meta-skill-evolution/SKILL.md +0 -325
- package/skills/prospecting/SKILL.md +0 -454
- package/skills/receiving-code-review/SKILL.md +0 -225
- package/skills/requesting-code-review/SKILL.md +0 -206
- package/skills/security-audit/SKILL.md +0 -353
- package/skills/self-healing-code/SKILL.md +0 -369
- package/skills/subagent-driven-development/SKILL.md +0 -244
- package/skills/systematic-debugging/SKILL.md +0 -355
- package/skills/test-driven-development/SKILL.md +0 -416
- package/skills/using-clawpowers/SKILL.md +0 -160
- package/skills/using-git-worktrees/SKILL.md +0 -261
- package/skills/validator/SKILL.md +0 -281
- package/skills/verification-before-completion/SKILL.md +0 -254
- package/skills/writing-plans/SKILL.md +0 -276
- package/skills/writing-skills/SKILL.md +0 -260
package/dist/index.js
ADDED
|
@@ -0,0 +1,3464 @@
|
|
|
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.2.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/native/index.ts
|
|
636
|
+
import { createRequire } from "module";
|
|
637
|
+
import { fileURLToPath } from "url";
|
|
638
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
639
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
640
|
+
var require2 = createRequire(import.meta.url);
|
|
641
|
+
var _native = null;
|
|
642
|
+
var _wasm = null;
|
|
643
|
+
var _activeTier = "typescript";
|
|
644
|
+
var _attempted = false;
|
|
645
|
+
function tryLoadNative() {
|
|
646
|
+
const candidates = [
|
|
647
|
+
join2(__dirname, "../../native/ffi/index.node"),
|
|
648
|
+
join2(__dirname, "../../native/ffi/clawpowers_ffi.node"),
|
|
649
|
+
join2(__dirname, "../../../native/ffi/index.node"),
|
|
650
|
+
join2(__dirname, "../../../native/ffi/clawpowers_ffi.node"),
|
|
651
|
+
join2(__dirname, "../native/ffi/index.node"),
|
|
652
|
+
join2(__dirname, "../native/ffi/clawpowers_ffi.node")
|
|
653
|
+
];
|
|
654
|
+
for (const p of candidates) {
|
|
655
|
+
try {
|
|
656
|
+
const mod = require2(p);
|
|
657
|
+
console.log(`[clawpowers] Tier 1: Native acceleration enabled (${p})`);
|
|
658
|
+
return mod;
|
|
659
|
+
} catch {
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
function tryLoadWasm() {
|
|
665
|
+
const wasmCandidates = [
|
|
666
|
+
join2(__dirname, "../native/wasm/pkg-node/clawpowers_wasm.js"),
|
|
667
|
+
join2(__dirname, "../native/wasm/pkg/clawpowers_wasm.js"),
|
|
668
|
+
join2(__dirname, "../../native/wasm/pkg-node/clawpowers_wasm.js"),
|
|
669
|
+
join2(__dirname, "../../native/wasm/pkg/clawpowers_wasm.js")
|
|
670
|
+
];
|
|
671
|
+
for (const p of wasmCandidates) {
|
|
672
|
+
try {
|
|
673
|
+
const mod = require2(p);
|
|
674
|
+
console.log(`[clawpowers] Tier 2: WASM module loaded (${p})`);
|
|
675
|
+
return mod;
|
|
676
|
+
} catch {
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
function loadAll() {
|
|
682
|
+
if (_attempted) return;
|
|
683
|
+
_attempted = true;
|
|
684
|
+
_native = tryLoadNative();
|
|
685
|
+
if (_native) {
|
|
686
|
+
_activeTier = "native";
|
|
687
|
+
_wasm = tryLoadWasm();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
_wasm = tryLoadWasm();
|
|
691
|
+
if (_wasm) {
|
|
692
|
+
_activeTier = "wasm";
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
_activeTier = "typescript";
|
|
696
|
+
console.log("[clawpowers] Tier 3: TypeScript fallback active (no native or WASM)");
|
|
697
|
+
}
|
|
698
|
+
function getNative() {
|
|
699
|
+
loadAll();
|
|
700
|
+
return _native;
|
|
701
|
+
}
|
|
702
|
+
function getWasm() {
|
|
703
|
+
loadAll();
|
|
704
|
+
return _wasm;
|
|
705
|
+
}
|
|
706
|
+
function isNativeAvailable() {
|
|
707
|
+
loadAll();
|
|
708
|
+
return _native !== null;
|
|
709
|
+
}
|
|
710
|
+
function isWasmAvailable() {
|
|
711
|
+
loadAll();
|
|
712
|
+
return _wasm !== null;
|
|
713
|
+
}
|
|
714
|
+
function getActiveTier() {
|
|
715
|
+
loadAll();
|
|
716
|
+
return _activeTier;
|
|
717
|
+
}
|
|
718
|
+
function getCapabilitySummary() {
|
|
719
|
+
loadAll();
|
|
720
|
+
const nativeModules = _native ? ["wallet", "fee", "x402", "canonical", "compression", "verification", "security"] : [];
|
|
721
|
+
let wasmModules = [];
|
|
722
|
+
if (_wasm) {
|
|
723
|
+
try {
|
|
724
|
+
wasmModules = JSON.parse(_wasm.getAvailableModules());
|
|
725
|
+
} catch {
|
|
726
|
+
wasmModules = ["tokens", "fee", "compression", "canonical", "verification", "security", "index"];
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const typescriptFallback = [
|
|
730
|
+
"wallet",
|
|
731
|
+
// ethers.js / viem
|
|
732
|
+
"x402",
|
|
733
|
+
// fetch()-based HTTP client
|
|
734
|
+
"tokens",
|
|
735
|
+
// Pure TS decimal math
|
|
736
|
+
"fee",
|
|
737
|
+
// Pure TS fee calculation
|
|
738
|
+
"policy"
|
|
739
|
+
// Pure TS policy engine
|
|
740
|
+
];
|
|
741
|
+
return {
|
|
742
|
+
tier: _activeTier,
|
|
743
|
+
nativeModules,
|
|
744
|
+
wasmModules,
|
|
745
|
+
typescriptFallback
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function computeSha256(content) {
|
|
749
|
+
loadAll();
|
|
750
|
+
if (_wasm) {
|
|
751
|
+
return _wasm.computeSha256(content);
|
|
752
|
+
}
|
|
753
|
+
const { createHash } = require2("node:crypto");
|
|
754
|
+
return createHash("sha256").update(content).digest("hex");
|
|
755
|
+
}
|
|
756
|
+
function digestForWalletAddress(keyMaterial) {
|
|
757
|
+
loadAll();
|
|
758
|
+
if (_native && typeof _native.keccak256Bytes === "function") {
|
|
759
|
+
try {
|
|
760
|
+
return _native.keccak256Bytes(keyMaterial);
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (_wasm && typeof _wasm.computeKeccak256 === "function") {
|
|
765
|
+
try {
|
|
766
|
+
return _wasm.computeKeccak256(new Uint8Array(keyMaterial));
|
|
767
|
+
} catch {
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const { createHash } = require2("node:crypto");
|
|
771
|
+
return "0x" + createHash("sha256").update(keyMaterial).digest("hex");
|
|
772
|
+
}
|
|
773
|
+
function keccak256Digest(data) {
|
|
774
|
+
loadAll();
|
|
775
|
+
if (_native && typeof _native.keccak256Bytes === "function") {
|
|
776
|
+
try {
|
|
777
|
+
const hex = _native.keccak256Bytes(data);
|
|
778
|
+
return Buffer.from(hex.replace(/^0x/i, ""), "hex");
|
|
779
|
+
} catch {
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (_wasm && typeof _wasm.computeKeccak256 === "function") {
|
|
783
|
+
try {
|
|
784
|
+
const hex = _wasm.computeKeccak256(new Uint8Array(data));
|
|
785
|
+
return Buffer.from(hex.replace(/^0x/i, ""), "hex");
|
|
786
|
+
} catch {
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
function deriveEthereumAddress(privateKey) {
|
|
792
|
+
loadAll();
|
|
793
|
+
if (_native && typeof _native.deriveEthereumAddress === "function") {
|
|
794
|
+
try {
|
|
795
|
+
return _native.deriveEthereumAddress(privateKey);
|
|
796
|
+
} catch {
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (_wasm && typeof _wasm.deriveEthereumAddress === "function") {
|
|
800
|
+
try {
|
|
801
|
+
return _wasm.deriveEthereumAddress(new Uint8Array(privateKey));
|
|
802
|
+
} catch {
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
807
|
+
function derivePublicKey(privateKey) {
|
|
808
|
+
loadAll();
|
|
809
|
+
if (_native && typeof _native.derivePublicKey === "function") {
|
|
810
|
+
try {
|
|
811
|
+
return Buffer.from(_native.derivePublicKey(privateKey));
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (_wasm && typeof _wasm.derivePublicKey === "function") {
|
|
816
|
+
try {
|
|
817
|
+
return Buffer.from(_wasm.derivePublicKey(new Uint8Array(privateKey)));
|
|
818
|
+
} catch {
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
function signEcdsa(privateKey, messageHash) {
|
|
824
|
+
loadAll();
|
|
825
|
+
if (_native && typeof _native.signEcdsa === "function") {
|
|
826
|
+
try {
|
|
827
|
+
return Buffer.from(_native.signEcdsa(privateKey, messageHash));
|
|
828
|
+
} catch {
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (_wasm && typeof _wasm.signEcdsa === "function") {
|
|
832
|
+
try {
|
|
833
|
+
return Buffer.from(_wasm.signEcdsa(new Uint8Array(privateKey), new Uint8Array(messageHash)));
|
|
834
|
+
} catch {
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return null;
|
|
838
|
+
}
|
|
839
|
+
function verifyEcdsa(publicKey, messageHash, signature) {
|
|
840
|
+
loadAll();
|
|
841
|
+
try {
|
|
842
|
+
if (_native && typeof _native.verifyEcdsa === "function") {
|
|
843
|
+
return _native.verifyEcdsa(publicKey, messageHash, signature);
|
|
844
|
+
}
|
|
845
|
+
if (_wasm && typeof _wasm.verifyEcdsa === "function") {
|
|
846
|
+
return _wasm.verifyEcdsa(
|
|
847
|
+
new Uint8Array(publicKey),
|
|
848
|
+
new Uint8Array(messageHash),
|
|
849
|
+
new Uint8Array(signature)
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
} catch {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
function tokenAmountFromHuman(human, decimals) {
|
|
858
|
+
loadAll();
|
|
859
|
+
if (_wasm) {
|
|
860
|
+
return JSON.parse(_wasm.tokenAmountFromHuman(human, decimals));
|
|
861
|
+
}
|
|
862
|
+
const multiplier = Math.pow(10, decimals);
|
|
863
|
+
const raw = Math.floor(human * multiplier);
|
|
864
|
+
return { raw: raw.toString(), decimals };
|
|
865
|
+
}
|
|
866
|
+
function calculateFee(amountHuman, decimals, feeType, txFeeBps, swapFeeBps) {
|
|
867
|
+
loadAll();
|
|
868
|
+
if (_wasm) {
|
|
869
|
+
const amountJson = _wasm.tokenAmountFromHuman(amountHuman, decimals);
|
|
870
|
+
const result = _wasm.calculateFee(
|
|
871
|
+
amountJson,
|
|
872
|
+
feeType,
|
|
873
|
+
txFeeBps !== void 0 ? BigInt(txFeeBps) : void 0,
|
|
874
|
+
swapFeeBps !== void 0 ? BigInt(swapFeeBps) : void 0
|
|
875
|
+
);
|
|
876
|
+
return JSON.parse(result);
|
|
877
|
+
}
|
|
878
|
+
const bps = feeType === "transaction" ? txFeeBps ?? 77 : feeType === "swap" ? swapFeeBps ?? 30 : parseInt(feeType.replace("custom:", ""), 10) || 0;
|
|
879
|
+
const feeAmount = amountHuman * bps / 1e4;
|
|
880
|
+
return {
|
|
881
|
+
gross_amount: amountHuman,
|
|
882
|
+
fee_amount: feeAmount,
|
|
883
|
+
net_amount: amountHuman - feeAmount
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
function evaluateWriteFirewall(request) {
|
|
887
|
+
loadAll();
|
|
888
|
+
if (_wasm) {
|
|
889
|
+
return JSON.parse(_wasm.evaluateWriteFirewall(JSON.stringify(request)));
|
|
890
|
+
}
|
|
891
|
+
if (request.allowed_namespaces && request.allowed_namespaces.length > 0 && !request.allowed_namespaces.includes(request.namespace)) {
|
|
892
|
+
return {
|
|
893
|
+
decision: "deny",
|
|
894
|
+
reason: `namespace '${request.namespace}' is not in the allow-list`
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
const maxLen = request.max_content_length ?? 1024 * 1024;
|
|
898
|
+
if (request.content.length > maxLen) {
|
|
899
|
+
return {
|
|
900
|
+
decision: "deny",
|
|
901
|
+
reason: `content length ${request.content.length} exceeds maximum ${maxLen}`
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
if (request.blocked_patterns) {
|
|
905
|
+
for (const pattern of request.blocked_patterns) {
|
|
906
|
+
if (request.content.includes(pattern)) {
|
|
907
|
+
if (request.trust_level === "system" || request.trust_level === "agent") {
|
|
908
|
+
return {
|
|
909
|
+
decision: "deny",
|
|
910
|
+
reason: `content contains blocked pattern '${pattern}'`
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
return {
|
|
914
|
+
decision: "sanitize",
|
|
915
|
+
sanitized: request.content.replaceAll(pattern, "")
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return { decision: "allow" };
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// src/payments/discovery.ts
|
|
924
|
+
var REQUIRED_HEADERS = [
|
|
925
|
+
"x-payment-amount",
|
|
926
|
+
"x-payment-currency",
|
|
927
|
+
"x-payment-recipient",
|
|
928
|
+
"x-payment-network"
|
|
929
|
+
];
|
|
930
|
+
function detect402(response) {
|
|
931
|
+
if (response.status !== 402) {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
const normalizedHeaders = {};
|
|
935
|
+
for (const [key, value] of Object.entries(response.headers)) {
|
|
936
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
937
|
+
}
|
|
938
|
+
for (const header of REQUIRED_HEADERS) {
|
|
939
|
+
if (!normalizedHeaders[header] || normalizedHeaders[header].trim() === "") {
|
|
940
|
+
return null;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const amountStr = normalizedHeaders["x-payment-amount"];
|
|
944
|
+
const amount = Number(amountStr);
|
|
945
|
+
if (Number.isNaN(amount) || amount <= 0) {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
const x402Headers = {};
|
|
949
|
+
for (const [key, value] of Object.entries(normalizedHeaders)) {
|
|
950
|
+
if (key.startsWith("x-payment-")) {
|
|
951
|
+
x402Headers[key] = value;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return {
|
|
955
|
+
amount,
|
|
956
|
+
currency: normalizedHeaders["x-payment-currency"],
|
|
957
|
+
recipient: normalizedHeaders["x-payment-recipient"],
|
|
958
|
+
network: normalizedHeaders["x-payment-network"],
|
|
959
|
+
x402Headers
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
function isPaymentRequired(error) {
|
|
963
|
+
if (error === null || error === void 0) return false;
|
|
964
|
+
if (typeof error === "object") {
|
|
965
|
+
const obj = error;
|
|
966
|
+
if ("status" in obj && obj["status"] === 402) return true;
|
|
967
|
+
if ("statusCode" in obj && obj["statusCode"] === 402) return true;
|
|
968
|
+
if ("response" in obj && typeof obj["response"] === "object" && obj["response"] !== null) {
|
|
969
|
+
const response = obj["response"];
|
|
970
|
+
if ("status" in response && response["status"] === 402) return true;
|
|
971
|
+
}
|
|
972
|
+
if ("message" in obj && typeof obj["message"] === "string" && obj["message"].includes("402")) {
|
|
973
|
+
return true;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/payments/spending.ts
|
|
980
|
+
function getUtcDayStart() {
|
|
981
|
+
const now = /* @__PURE__ */ new Date();
|
|
982
|
+
const utcStart = new Date(Date.UTC(
|
|
983
|
+
now.getUTCFullYear(),
|
|
984
|
+
now.getUTCMonth(),
|
|
985
|
+
now.getUTCDate(),
|
|
986
|
+
0,
|
|
987
|
+
0,
|
|
988
|
+
0,
|
|
989
|
+
0
|
|
990
|
+
));
|
|
991
|
+
return utcStart.getTime();
|
|
992
|
+
}
|
|
993
|
+
var SpendingPolicy = class {
|
|
994
|
+
dailyLimit;
|
|
995
|
+
transactionLimit;
|
|
996
|
+
allowedDomains;
|
|
997
|
+
spendingLog = [];
|
|
998
|
+
constructor(options) {
|
|
999
|
+
this.dailyLimit = options.dailyLimit;
|
|
1000
|
+
this.transactionLimit = options.transactionLimit;
|
|
1001
|
+
this.allowedDomains = options.allowedDomains;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Get total spending for the current UTC day.
|
|
1005
|
+
*/
|
|
1006
|
+
getDailySpent() {
|
|
1007
|
+
const dayStart = getUtcDayStart();
|
|
1008
|
+
return this.spendingLog.filter((r) => r.timestamp >= dayStart).reduce((sum, r) => sum + r.amount, 0);
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Check whether a transaction is allowed under the current policy.
|
|
1012
|
+
* Fail-closed: any validation error results in rejection.
|
|
1013
|
+
*/
|
|
1014
|
+
checkTransaction(amount, domain) {
|
|
1015
|
+
try {
|
|
1016
|
+
if (amount <= 0 || !Number.isFinite(amount)) {
|
|
1017
|
+
return {
|
|
1018
|
+
allowed: false,
|
|
1019
|
+
reason: `Invalid amount: ${amount}`,
|
|
1020
|
+
remainingDaily: this.dailyLimit - this.getDailySpent()
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
if (amount > this.transactionLimit) {
|
|
1024
|
+
return {
|
|
1025
|
+
allowed: false,
|
|
1026
|
+
reason: `Amount $${amount} exceeds per-transaction limit of $${this.transactionLimit}`,
|
|
1027
|
+
remainingDaily: this.dailyLimit - this.getDailySpent()
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
if (this.allowedDomains.length > 0) {
|
|
1031
|
+
const normalizedDomain = domain.toLowerCase();
|
|
1032
|
+
const isAllowed = this.allowedDomains.some(
|
|
1033
|
+
(d) => d.toLowerCase() === normalizedDomain
|
|
1034
|
+
);
|
|
1035
|
+
if (!isAllowed) {
|
|
1036
|
+
return {
|
|
1037
|
+
allowed: false,
|
|
1038
|
+
reason: `Domain "${domain}" is not in the allowed domains list`,
|
|
1039
|
+
remainingDaily: this.dailyLimit - this.getDailySpent()
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
const dailySpent = this.getDailySpent();
|
|
1044
|
+
if (dailySpent + amount > this.dailyLimit) {
|
|
1045
|
+
return {
|
|
1046
|
+
allowed: false,
|
|
1047
|
+
reason: `Transaction of $${amount} would exceed daily limit of $${this.dailyLimit} (already spent: $${dailySpent})`,
|
|
1048
|
+
remainingDaily: this.dailyLimit - dailySpent
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
return {
|
|
1052
|
+
allowed: true,
|
|
1053
|
+
reason: "Transaction approved",
|
|
1054
|
+
remainingDaily: this.dailyLimit - dailySpent - amount
|
|
1055
|
+
};
|
|
1056
|
+
} catch {
|
|
1057
|
+
return {
|
|
1058
|
+
allowed: false,
|
|
1059
|
+
reason: "Policy check failed due to internal error \u2014 rejecting for safety",
|
|
1060
|
+
remainingDaily: 0
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Record a completed spending transaction.
|
|
1066
|
+
*/
|
|
1067
|
+
recordSpend(amount, domain) {
|
|
1068
|
+
this.spendingLog.push({
|
|
1069
|
+
amount,
|
|
1070
|
+
timestamp: Date.now(),
|
|
1071
|
+
domain
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Reset all spending records (used for testing).
|
|
1076
|
+
*/
|
|
1077
|
+
reset() {
|
|
1078
|
+
this.spendingLog = [];
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Get the full spending log (for audit purposes).
|
|
1082
|
+
*/
|
|
1083
|
+
getSpendingLog() {
|
|
1084
|
+
return [...this.spendingLog];
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// src/payments/executor.ts
|
|
1089
|
+
var PaymentExecutor = class {
|
|
1090
|
+
policy;
|
|
1091
|
+
client;
|
|
1092
|
+
auditLog = [];
|
|
1093
|
+
constructor(policy, client) {
|
|
1094
|
+
this.policy = policy;
|
|
1095
|
+
this.client = client;
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Execute a payment request.
|
|
1099
|
+
* 1. Check spending policy
|
|
1100
|
+
* 2. If allowed, execute via MCP client
|
|
1101
|
+
* 3. Log the result (success or failure)
|
|
1102
|
+
* 4. Never auto-retry on failure
|
|
1103
|
+
*/
|
|
1104
|
+
async executePayment(request) {
|
|
1105
|
+
const decision = this.policy.checkTransaction(request.amount, request.domain);
|
|
1106
|
+
if (!decision.allowed) {
|
|
1107
|
+
const result = {
|
|
1108
|
+
success: false,
|
|
1109
|
+
error: `Spending policy rejected: ${decision.reason}`
|
|
1110
|
+
};
|
|
1111
|
+
this.logAudit(request, result);
|
|
1112
|
+
return result;
|
|
1113
|
+
}
|
|
1114
|
+
try {
|
|
1115
|
+
const mcpResult = await this.client.executePayment({
|
|
1116
|
+
amount: request.amount,
|
|
1117
|
+
currency: request.currency,
|
|
1118
|
+
recipient: request.recipient,
|
|
1119
|
+
x402Headers: request.x402Headers
|
|
1120
|
+
});
|
|
1121
|
+
if (mcpResult.status === "success") {
|
|
1122
|
+
this.policy.recordSpend(request.amount, request.domain);
|
|
1123
|
+
const result2 = {
|
|
1124
|
+
success: true,
|
|
1125
|
+
txHash: mcpResult.txHash
|
|
1126
|
+
};
|
|
1127
|
+
this.logAudit(request, result2);
|
|
1128
|
+
return result2;
|
|
1129
|
+
}
|
|
1130
|
+
const result = {
|
|
1131
|
+
success: false,
|
|
1132
|
+
error: "Payment execution failed at MCP layer"
|
|
1133
|
+
};
|
|
1134
|
+
this.logAudit(request, result);
|
|
1135
|
+
return result;
|
|
1136
|
+
} catch (err) {
|
|
1137
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1138
|
+
const result = {
|
|
1139
|
+
success: false,
|
|
1140
|
+
error: `Payment execution error: ${errorMessage}`
|
|
1141
|
+
};
|
|
1142
|
+
this.logAudit(request, result);
|
|
1143
|
+
return result;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Get the full payment audit log.
|
|
1148
|
+
*/
|
|
1149
|
+
getAuditLog() {
|
|
1150
|
+
return [...this.auditLog];
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Log a payment attempt to the audit trail.
|
|
1154
|
+
*/
|
|
1155
|
+
logAudit(request, result) {
|
|
1156
|
+
this.auditLog.push({
|
|
1157
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1158
|
+
request,
|
|
1159
|
+
result,
|
|
1160
|
+
spendingSnapshot: {
|
|
1161
|
+
dailySpent: this.policy.getDailySpent(),
|
|
1162
|
+
dailyLimit: this.policy.dailyLimit
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
// src/payments/native-bridge.ts
|
|
1169
|
+
import { randomBytes } from "crypto";
|
|
1170
|
+
function calculateTransactionFee(amount, decimals = 6) {
|
|
1171
|
+
const native = getNative();
|
|
1172
|
+
if (native) {
|
|
1173
|
+
try {
|
|
1174
|
+
const schedule = native.JsFeeSchedule.withDefaults();
|
|
1175
|
+
const raw = JSON.parse(schedule.calculate(amount, decimals, "transaction"));
|
|
1176
|
+
return {
|
|
1177
|
+
gross: raw.gross,
|
|
1178
|
+
fee: raw.fee,
|
|
1179
|
+
net: raw.net,
|
|
1180
|
+
feeRecipient: raw.feeRecipient ?? raw.fee_recipient ?? "0x0000000000000000000000000000000000000000"
|
|
1181
|
+
};
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
const wasm = getWasm();
|
|
1186
|
+
if (wasm) {
|
|
1187
|
+
try {
|
|
1188
|
+
const result = calculateFee(amount, decimals, "transaction");
|
|
1189
|
+
return {
|
|
1190
|
+
gross: result.gross_amount,
|
|
1191
|
+
fee: result.fee_amount,
|
|
1192
|
+
net: result.net_amount,
|
|
1193
|
+
feeRecipient: "0x0000000000000000000000000000000000000000"
|
|
1194
|
+
};
|
|
1195
|
+
} catch {
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const fee = amount * 77e-4;
|
|
1199
|
+
return {
|
|
1200
|
+
gross: amount,
|
|
1201
|
+
fee,
|
|
1202
|
+
net: amount - fee,
|
|
1203
|
+
feeRecipient: "0x0000000000000000000000000000000000000000"
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
function createPaymentHeader(paymentJson, signature) {
|
|
1207
|
+
const native = getNative();
|
|
1208
|
+
if (native) {
|
|
1209
|
+
try {
|
|
1210
|
+
const client = new native.JsX402Client();
|
|
1211
|
+
return client.createPaymentHeader(paymentJson, signature);
|
|
1212
|
+
} catch {
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
return Buffer.from(JSON.stringify({ payment: JSON.parse(paymentJson), signature })).toString("base64");
|
|
1216
|
+
}
|
|
1217
|
+
function generateWalletAddress() {
|
|
1218
|
+
const native = getNative();
|
|
1219
|
+
if (native) {
|
|
1220
|
+
try {
|
|
1221
|
+
return native.JsAgentWallet.generate().address();
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const address = deriveEthereumAddress(randomBytes(32));
|
|
1226
|
+
if (address) {
|
|
1227
|
+
return address;
|
|
1228
|
+
}
|
|
1229
|
+
throw new Error(
|
|
1230
|
+
"Unable to derive a real Ethereum address. Native or WASM wallet support is required for generateWalletAddress()."
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// src/memory/working.ts
|
|
1235
|
+
function estimateTokens(text) {
|
|
1236
|
+
return Math.ceil(text.length / 4);
|
|
1237
|
+
}
|
|
1238
|
+
var WorkingMemoryManager = class {
|
|
1239
|
+
memory = null;
|
|
1240
|
+
create(taskId, goal) {
|
|
1241
|
+
const emptyPlan = {
|
|
1242
|
+
taskId,
|
|
1243
|
+
steps: [],
|
|
1244
|
+
status: "draft",
|
|
1245
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1246
|
+
approvedAt: null,
|
|
1247
|
+
parallelizable: false
|
|
1248
|
+
};
|
|
1249
|
+
this.memory = {
|
|
1250
|
+
taskId,
|
|
1251
|
+
goal,
|
|
1252
|
+
plan: emptyPlan,
|
|
1253
|
+
currentStepId: null,
|
|
1254
|
+
intermediateOutputs: {},
|
|
1255
|
+
contextWindow: []
|
|
1256
|
+
};
|
|
1257
|
+
return this.memory;
|
|
1258
|
+
}
|
|
1259
|
+
updateCurrentStep(stepId) {
|
|
1260
|
+
if (!this.memory) {
|
|
1261
|
+
throw new Error("Working memory not initialized. Call create() first.");
|
|
1262
|
+
}
|
|
1263
|
+
this.memory = {
|
|
1264
|
+
...this.memory,
|
|
1265
|
+
currentStepId: stepId
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
addIntermediateOutput(stepId, output) {
|
|
1269
|
+
if (!this.memory) {
|
|
1270
|
+
throw new Error("Working memory not initialized. Call create() first.");
|
|
1271
|
+
}
|
|
1272
|
+
this.memory = {
|
|
1273
|
+
...this.memory,
|
|
1274
|
+
intermediateOutputs: {
|
|
1275
|
+
...this.memory.intermediateOutputs,
|
|
1276
|
+
[stepId]: output
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Inject context entries into working memory, enforcing token budget.
|
|
1282
|
+
* Entries are added until the budget is exhausted, then truncated.
|
|
1283
|
+
*/
|
|
1284
|
+
injectContext(entries) {
|
|
1285
|
+
if (!this.memory) {
|
|
1286
|
+
throw new Error("Working memory not initialized. Call create() first.");
|
|
1287
|
+
}
|
|
1288
|
+
const maxTokens = PERFORMANCE.maxContextTokens;
|
|
1289
|
+
const injected = [];
|
|
1290
|
+
let totalTokens = 0;
|
|
1291
|
+
for (const entry of entries) {
|
|
1292
|
+
const entryTokens = estimateTokens(entry);
|
|
1293
|
+
if (totalTokens + entryTokens > maxTokens) {
|
|
1294
|
+
const remainingTokens = maxTokens - totalTokens;
|
|
1295
|
+
if (remainingTokens > 10) {
|
|
1296
|
+
const truncatedLength = remainingTokens * 4;
|
|
1297
|
+
injected.push(entry.slice(0, truncatedLength) + "...");
|
|
1298
|
+
}
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
totalTokens += entryTokens;
|
|
1302
|
+
injected.push(entry);
|
|
1303
|
+
}
|
|
1304
|
+
this.memory = {
|
|
1305
|
+
...this.memory,
|
|
1306
|
+
contextWindow: injected
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
getSnapshot() {
|
|
1310
|
+
if (!this.memory) {
|
|
1311
|
+
throw new Error("Working memory not initialized. Call create() first.");
|
|
1312
|
+
}
|
|
1313
|
+
return this.memory;
|
|
1314
|
+
}
|
|
1315
|
+
clear() {
|
|
1316
|
+
this.memory = null;
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
// src/memory/episodic.ts
|
|
1321
|
+
import { readFile, appendFile, writeFile, mkdir } from "fs/promises";
|
|
1322
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1323
|
+
import { dirname as dirname3 } from "path";
|
|
1324
|
+
var EpisodicMemory = class {
|
|
1325
|
+
filePath;
|
|
1326
|
+
constructor(filePath) {
|
|
1327
|
+
this.filePath = filePath;
|
|
1328
|
+
}
|
|
1329
|
+
async ensureDir() {
|
|
1330
|
+
const dir = dirname3(this.filePath);
|
|
1331
|
+
if (!existsSync2(dir)) {
|
|
1332
|
+
await mkdir(dir, { recursive: true });
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
async append(entry) {
|
|
1336
|
+
await this.ensureDir();
|
|
1337
|
+
const line = JSON.stringify(entry) + "\n";
|
|
1338
|
+
await appendFile(this.filePath, line, "utf-8");
|
|
1339
|
+
}
|
|
1340
|
+
async readAll() {
|
|
1341
|
+
if (!existsSync2(this.filePath)) {
|
|
1342
|
+
return [];
|
|
1343
|
+
}
|
|
1344
|
+
const content = await readFile(this.filePath, "utf-8");
|
|
1345
|
+
return this.parseLines(content);
|
|
1346
|
+
}
|
|
1347
|
+
async search(query, limit = 10) {
|
|
1348
|
+
const entries = await this.readAll();
|
|
1349
|
+
const queryLower = query.toLowerCase();
|
|
1350
|
+
const queryWords = queryLower.split(/\s+/).filter(Boolean);
|
|
1351
|
+
const scored = [];
|
|
1352
|
+
for (const entry of entries) {
|
|
1353
|
+
const searchText = [
|
|
1354
|
+
entry.description,
|
|
1355
|
+
...entry.lessonsLearned,
|
|
1356
|
+
...entry.tags
|
|
1357
|
+
].join(" ").toLowerCase();
|
|
1358
|
+
let score = 0;
|
|
1359
|
+
for (const word of queryWords) {
|
|
1360
|
+
if (searchText.includes(word)) {
|
|
1361
|
+
score += 1;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
if (searchText.includes(queryLower)) {
|
|
1365
|
+
score += queryWords.length;
|
|
1366
|
+
}
|
|
1367
|
+
if (score > 0) {
|
|
1368
|
+
scored.push({ entry, score });
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
scored.sort((a, b) => b.score - a.score);
|
|
1372
|
+
return scored.slice(0, limit).map((s) => s.entry);
|
|
1373
|
+
}
|
|
1374
|
+
async readRecent(count) {
|
|
1375
|
+
const entries = await this.readAll();
|
|
1376
|
+
return entries.slice(-count);
|
|
1377
|
+
}
|
|
1378
|
+
async recoverFromCorruption() {
|
|
1379
|
+
if (!existsSync2(this.filePath)) {
|
|
1380
|
+
return { recovered: 0, lost: 0 };
|
|
1381
|
+
}
|
|
1382
|
+
const content = await readFile(this.filePath, "utf-8");
|
|
1383
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
1384
|
+
const validLines = [];
|
|
1385
|
+
let lost = 0;
|
|
1386
|
+
for (const line of lines) {
|
|
1387
|
+
try {
|
|
1388
|
+
JSON.parse(line);
|
|
1389
|
+
validLines.push(line);
|
|
1390
|
+
} catch {
|
|
1391
|
+
lost++;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
if (lost > 0) {
|
|
1395
|
+
await writeFile(this.filePath, validLines.map((l) => l + "\n").join(""), "utf-8");
|
|
1396
|
+
}
|
|
1397
|
+
return { recovered: validLines.length, lost };
|
|
1398
|
+
}
|
|
1399
|
+
parseLines(content) {
|
|
1400
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
1401
|
+
const entries = [];
|
|
1402
|
+
for (const line of lines) {
|
|
1403
|
+
try {
|
|
1404
|
+
entries.push(JSON.parse(line));
|
|
1405
|
+
} catch {
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return entries;
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
// src/memory/procedural.ts
|
|
1413
|
+
import { readFile as readFile2, writeFile as writeFile2, rename, mkdir as mkdir2, copyFile } from "fs/promises";
|
|
1414
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1415
|
+
import { dirname as dirname4 } from "path";
|
|
1416
|
+
var ProceduralMemory = class {
|
|
1417
|
+
filePath;
|
|
1418
|
+
cache = null;
|
|
1419
|
+
constructor(filePath) {
|
|
1420
|
+
this.filePath = filePath;
|
|
1421
|
+
}
|
|
1422
|
+
async ensureDir() {
|
|
1423
|
+
const dir = dirname4(this.filePath);
|
|
1424
|
+
if (!existsSync3(dir)) {
|
|
1425
|
+
await mkdir2(dir, { recursive: true });
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
async load() {
|
|
1429
|
+
if (!existsSync3(this.filePath)) {
|
|
1430
|
+
this.cache = [];
|
|
1431
|
+
return [];
|
|
1432
|
+
}
|
|
1433
|
+
const content = await readFile2(this.filePath, "utf-8");
|
|
1434
|
+
const entries = JSON.parse(content);
|
|
1435
|
+
this.cache = entries;
|
|
1436
|
+
return entries;
|
|
1437
|
+
}
|
|
1438
|
+
async update(skillName, result) {
|
|
1439
|
+
const entries = await this.load();
|
|
1440
|
+
const existing = entries.find((e) => e.skillName === skillName);
|
|
1441
|
+
if (existing) {
|
|
1442
|
+
const newCount = existing.invocationCount + 1;
|
|
1443
|
+
const successCount = Math.round(existing.successRate * existing.invocationCount) + (result.succeeded ? 1 : 0);
|
|
1444
|
+
const newSuccessRate = successCount / newCount;
|
|
1445
|
+
const newAvgContribution = (existing.avgContribution * existing.invocationCount + result.durationMs) / newCount;
|
|
1446
|
+
const updated = {
|
|
1447
|
+
...existing,
|
|
1448
|
+
invocationCount: newCount,
|
|
1449
|
+
successRate: newSuccessRate,
|
|
1450
|
+
avgContribution: newAvgContribution,
|
|
1451
|
+
lastUsed: (/* @__PURE__ */ new Date()).toISOString()
|
|
1452
|
+
};
|
|
1453
|
+
const index = entries.indexOf(existing);
|
|
1454
|
+
entries[index] = updated;
|
|
1455
|
+
} else {
|
|
1456
|
+
const newEntry = {
|
|
1457
|
+
skillName,
|
|
1458
|
+
invocationCount: 1,
|
|
1459
|
+
successRate: result.succeeded ? 1 : 0,
|
|
1460
|
+
avgContribution: result.durationMs,
|
|
1461
|
+
preferredContexts: [],
|
|
1462
|
+
lastUsed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1463
|
+
mutations: []
|
|
1464
|
+
};
|
|
1465
|
+
entries.push(newEntry);
|
|
1466
|
+
}
|
|
1467
|
+
await this.atomicWrite(entries);
|
|
1468
|
+
this.cache = entries;
|
|
1469
|
+
}
|
|
1470
|
+
getSkillScore(skillName) {
|
|
1471
|
+
if (!this.cache) {
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
return this.cache.find((e) => e.skillName === skillName) ?? null;
|
|
1475
|
+
}
|
|
1476
|
+
getTopSkills(context, limit) {
|
|
1477
|
+
if (!this.cache) {
|
|
1478
|
+
return [];
|
|
1479
|
+
}
|
|
1480
|
+
const contextLower = context.toLowerCase();
|
|
1481
|
+
const contextWords = contextLower.split(/\s+/).filter(Boolean);
|
|
1482
|
+
const scored = [];
|
|
1483
|
+
for (const entry of this.cache) {
|
|
1484
|
+
let score = entry.successRate * entry.invocationCount;
|
|
1485
|
+
for (const preferred of entry.preferredContexts) {
|
|
1486
|
+
const prefLower = preferred.toLowerCase();
|
|
1487
|
+
for (const word of contextWords) {
|
|
1488
|
+
if (prefLower.includes(word)) {
|
|
1489
|
+
score += 2;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
if (contextLower.includes(entry.skillName.toLowerCase())) {
|
|
1494
|
+
score += 5;
|
|
1495
|
+
}
|
|
1496
|
+
scored.push({ entry, score });
|
|
1497
|
+
}
|
|
1498
|
+
scored.sort((a, b) => b.score - a.score);
|
|
1499
|
+
return scored.slice(0, limit).map((s) => s.entry);
|
|
1500
|
+
}
|
|
1501
|
+
async recordMutation(skillName, mutation) {
|
|
1502
|
+
const entries = await this.load();
|
|
1503
|
+
const existing = entries.find((e) => e.skillName === skillName);
|
|
1504
|
+
if (!existing) {
|
|
1505
|
+
throw new Error(`Skill "${skillName}" not found in procedural memory`);
|
|
1506
|
+
}
|
|
1507
|
+
const updated = {
|
|
1508
|
+
...existing,
|
|
1509
|
+
mutations: [...existing.mutations, mutation]
|
|
1510
|
+
};
|
|
1511
|
+
const index = entries.indexOf(existing);
|
|
1512
|
+
entries[index] = updated;
|
|
1513
|
+
await this.atomicWrite(entries);
|
|
1514
|
+
this.cache = entries;
|
|
1515
|
+
}
|
|
1516
|
+
async rollbackMutation(skillName, mutationId) {
|
|
1517
|
+
const entries = await this.load();
|
|
1518
|
+
const existing = entries.find((e) => e.skillName === skillName);
|
|
1519
|
+
if (!existing) {
|
|
1520
|
+
throw new Error(`Skill "${skillName}" not found in procedural memory`);
|
|
1521
|
+
}
|
|
1522
|
+
const mutation = existing.mutations.find((m) => m.mutationId === mutationId);
|
|
1523
|
+
if (!mutation) {
|
|
1524
|
+
throw new Error(`Mutation "${mutationId}" not found for skill "${skillName}"`);
|
|
1525
|
+
}
|
|
1526
|
+
const updatedMutations = existing.mutations.map((m) => {
|
|
1527
|
+
if (m.mutationId === mutationId) {
|
|
1528
|
+
return {
|
|
1529
|
+
...m,
|
|
1530
|
+
status: "reverted",
|
|
1531
|
+
revertedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
return m;
|
|
1535
|
+
});
|
|
1536
|
+
const updated = {
|
|
1537
|
+
...existing,
|
|
1538
|
+
mutations: updatedMutations
|
|
1539
|
+
};
|
|
1540
|
+
const index = entries.indexOf(existing);
|
|
1541
|
+
entries[index] = updated;
|
|
1542
|
+
await this.atomicWrite(entries);
|
|
1543
|
+
this.cache = entries;
|
|
1544
|
+
}
|
|
1545
|
+
async atomicWrite(entries) {
|
|
1546
|
+
await this.ensureDir();
|
|
1547
|
+
if (existsSync3(this.filePath)) {
|
|
1548
|
+
await copyFile(this.filePath, this.filePath + ".bak");
|
|
1549
|
+
}
|
|
1550
|
+
const tmpPath = this.filePath + ".tmp";
|
|
1551
|
+
await writeFile2(tmpPath, JSON.stringify(entries, null, 2) + "\n", "utf-8");
|
|
1552
|
+
await rename(tmpPath, this.filePath);
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
|
|
1556
|
+
// src/memory/checkpoint.ts
|
|
1557
|
+
import { readFile as readFile3, writeFile as writeFile3, rename as rename2, unlink, readdir, mkdir as mkdir3 } from "fs/promises";
|
|
1558
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1559
|
+
import { join as join3 } from "path";
|
|
1560
|
+
var DEFAULT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
1561
|
+
var CheckpointManager = class {
|
|
1562
|
+
dir;
|
|
1563
|
+
constructor(dir) {
|
|
1564
|
+
this.dir = dir;
|
|
1565
|
+
}
|
|
1566
|
+
async ensureDir() {
|
|
1567
|
+
if (!existsSync4(this.dir)) {
|
|
1568
|
+
await mkdir3(this.dir, { recursive: true });
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
filePath(taskId) {
|
|
1572
|
+
return join3(this.dir, `${taskId}.json`);
|
|
1573
|
+
}
|
|
1574
|
+
async save(taskId, state) {
|
|
1575
|
+
await this.ensureDir();
|
|
1576
|
+
const path = this.filePath(taskId);
|
|
1577
|
+
const tmpPath = path + ".tmp";
|
|
1578
|
+
await writeFile3(tmpPath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
1579
|
+
await rename2(tmpPath, path);
|
|
1580
|
+
}
|
|
1581
|
+
async load(taskId) {
|
|
1582
|
+
const path = this.filePath(taskId);
|
|
1583
|
+
if (!existsSync4(path)) {
|
|
1584
|
+
return null;
|
|
1585
|
+
}
|
|
1586
|
+
const content = await readFile3(path, "utf-8");
|
|
1587
|
+
return JSON.parse(content);
|
|
1588
|
+
}
|
|
1589
|
+
async remove(taskId) {
|
|
1590
|
+
const path = this.filePath(taskId);
|
|
1591
|
+
if (existsSync4(path)) {
|
|
1592
|
+
await unlink(path);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
async listIncomplete() {
|
|
1596
|
+
await this.ensureDir();
|
|
1597
|
+
const files = await readdir(this.dir);
|
|
1598
|
+
const results = [];
|
|
1599
|
+
for (const file of files) {
|
|
1600
|
+
if (!file.endsWith(".json")) continue;
|
|
1601
|
+
const path = join3(this.dir, file);
|
|
1602
|
+
try {
|
|
1603
|
+
const content = await readFile3(path, "utf-8");
|
|
1604
|
+
const state = JSON.parse(content);
|
|
1605
|
+
if (state.agentStatus !== "complete" && state.agentStatus !== "failed") {
|
|
1606
|
+
results.push({
|
|
1607
|
+
taskId: state.taskId,
|
|
1608
|
+
description: state.goal.description,
|
|
1609
|
+
savedAt: state.savedAt,
|
|
1610
|
+
isStale: this.isStale(state)
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
} catch {
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
return results;
|
|
1617
|
+
}
|
|
1618
|
+
isStale(checkpoint, maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
1619
|
+
const savedTime = new Date(checkpoint.savedAt).getTime();
|
|
1620
|
+
const now = Date.now();
|
|
1621
|
+
return now - savedTime > maxAgeMs;
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
// src/memory/context-injector.ts
|
|
1626
|
+
var DEFAULT_MAX_TOKENS = 2e3;
|
|
1627
|
+
function estimateTokens2(text) {
|
|
1628
|
+
return Math.ceil(text.length / 4);
|
|
1629
|
+
}
|
|
1630
|
+
function compressEpisodicEntry(entry) {
|
|
1631
|
+
const date = entry.timestamp.slice(0, 10);
|
|
1632
|
+
const skills = entry.skillsUsed.length > 0 ? ` (${entry.skillsUsed.join(", ")})` : "";
|
|
1633
|
+
const lesson = entry.lessonsLearned.length > 0 ? ` Lesson: ${entry.lessonsLearned[0]}` : "";
|
|
1634
|
+
return `[${date}] '${entry.description.slice(0, 80)}' \u2192 ${entry.outcome}${skills}${lesson}`;
|
|
1635
|
+
}
|
|
1636
|
+
function compressProceduralEntry(entry) {
|
|
1637
|
+
const rate = Math.round(entry.successRate * 100);
|
|
1638
|
+
return `[skill] ${entry.skillName}: ${rate}% success over ${entry.invocationCount} invocations`;
|
|
1639
|
+
}
|
|
1640
|
+
function scoreEpisodicEntry(entry, goalWords) {
|
|
1641
|
+
const text = [entry.description, ...entry.lessonsLearned, ...entry.tags].join(" ").toLowerCase();
|
|
1642
|
+
let score = 0;
|
|
1643
|
+
for (const word of goalWords) {
|
|
1644
|
+
if (text.includes(word)) {
|
|
1645
|
+
score += 1;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
const ageMs = Date.now() - new Date(entry.timestamp).getTime();
|
|
1649
|
+
const ageDays = ageMs / (24 * 60 * 60 * 1e3);
|
|
1650
|
+
score += Math.max(0, 10 - ageDays) / 10;
|
|
1651
|
+
return score;
|
|
1652
|
+
}
|
|
1653
|
+
var ContextInjector = class {
|
|
1654
|
+
episodic;
|
|
1655
|
+
procedural;
|
|
1656
|
+
constructor(episodic, procedural) {
|
|
1657
|
+
this.episodic = episodic;
|
|
1658
|
+
this.procedural = procedural;
|
|
1659
|
+
}
|
|
1660
|
+
async inject(goal, maxTokens = DEFAULT_MAX_TOKENS) {
|
|
1661
|
+
const goalWords = goal.description.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
1662
|
+
const allEpisodic = await this.episodic.readAll();
|
|
1663
|
+
const scoredEpisodic = allEpisodic.map((entry) => ({ entry, score: scoreEpisodicEntry(entry, goalWords) })).filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
|
|
1664
|
+
const allProcedural = await this.procedural.load();
|
|
1665
|
+
const relevantProcedural = allProcedural.filter((entry) => {
|
|
1666
|
+
const nameLower = entry.skillName.toLowerCase();
|
|
1667
|
+
return goalWords.some((w) => nameLower.includes(w) || w.includes(nameLower));
|
|
1668
|
+
}).sort((a, b) => b.successRate * b.invocationCount - a.successRate * a.invocationCount);
|
|
1669
|
+
const results = [];
|
|
1670
|
+
let totalTokens = 0;
|
|
1671
|
+
let eIdx = 0;
|
|
1672
|
+
let pIdx = 0;
|
|
1673
|
+
while (totalTokens < maxTokens && (eIdx < scoredEpisodic.length || pIdx < relevantProcedural.length)) {
|
|
1674
|
+
for (let i = 0; i < 2 && eIdx < scoredEpisodic.length; i++, eIdx++) {
|
|
1675
|
+
const compressed = compressEpisodicEntry(scoredEpisodic[eIdx].entry);
|
|
1676
|
+
const tokens = estimateTokens2(compressed);
|
|
1677
|
+
if (totalTokens + tokens > maxTokens) {
|
|
1678
|
+
return results;
|
|
1679
|
+
}
|
|
1680
|
+
results.push(compressed);
|
|
1681
|
+
totalTokens += tokens;
|
|
1682
|
+
}
|
|
1683
|
+
if (pIdx < relevantProcedural.length) {
|
|
1684
|
+
const compressed = compressProceduralEntry(relevantProcedural[pIdx]);
|
|
1685
|
+
const tokens = estimateTokens2(compressed);
|
|
1686
|
+
if (totalTokens + tokens > maxTokens) {
|
|
1687
|
+
return results;
|
|
1688
|
+
}
|
|
1689
|
+
results.push(compressed);
|
|
1690
|
+
totalTokens += tokens;
|
|
1691
|
+
pIdx++;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
return results;
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1697
|
+
|
|
1698
|
+
// src/memory/native-store.ts
|
|
1699
|
+
function getNativeCanonicalStore(dbPath) {
|
|
1700
|
+
const native = getNative();
|
|
1701
|
+
if (!native) return null;
|
|
1702
|
+
try {
|
|
1703
|
+
return native.JsCanonicalStore.open(dbPath);
|
|
1704
|
+
} catch {
|
|
1705
|
+
return null;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
function getNativeCanonicalStoreInMemory() {
|
|
1709
|
+
const native = getNative();
|
|
1710
|
+
if (!native) return null;
|
|
1711
|
+
try {
|
|
1712
|
+
return native.JsCanonicalStore.inMemory();
|
|
1713
|
+
} catch {
|
|
1714
|
+
return null;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
function getWasmCanonicalStore() {
|
|
1718
|
+
const wasm = getWasm();
|
|
1719
|
+
if (!wasm) return null;
|
|
1720
|
+
try {
|
|
1721
|
+
return new wasm.WasmCanonicalStore();
|
|
1722
|
+
} catch {
|
|
1723
|
+
return null;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
function getBestCanonicalStore() {
|
|
1727
|
+
return getNativeCanonicalStoreInMemory() ?? getWasmCanonicalStore();
|
|
1728
|
+
}
|
|
1729
|
+
function compressVector(vector, bits = 8) {
|
|
1730
|
+
const native = getNative();
|
|
1731
|
+
if (native) {
|
|
1732
|
+
try {
|
|
1733
|
+
const compressor = new native.JsTurboCompressor(vector.length, bits);
|
|
1734
|
+
const compressed = compressor.compress(vector);
|
|
1735
|
+
return {
|
|
1736
|
+
compressed,
|
|
1737
|
+
originalSize: vector.length * 4,
|
|
1738
|
+
compressedSize: compressed.length
|
|
1739
|
+
};
|
|
1740
|
+
} catch {
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
const wasm = getWasm();
|
|
1744
|
+
if (wasm) {
|
|
1745
|
+
try {
|
|
1746
|
+
const vectorJson = JSON.stringify(Array.from(vector));
|
|
1747
|
+
const compressed = wasm.compressVector(vectorJson, vector.length);
|
|
1748
|
+
return {
|
|
1749
|
+
compressed,
|
|
1750
|
+
originalSize: vector.length * 4,
|
|
1751
|
+
compressedSize: compressed.length
|
|
1752
|
+
};
|
|
1753
|
+
} catch {
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return null;
|
|
1757
|
+
}
|
|
1758
|
+
function decompressVector(compressedJson, dimensions, bits = 8) {
|
|
1759
|
+
const native = getNative();
|
|
1760
|
+
if (native) {
|
|
1761
|
+
try {
|
|
1762
|
+
const compressor = new native.JsTurboCompressor(dimensions, bits);
|
|
1763
|
+
return compressor.decompress(compressedJson);
|
|
1764
|
+
} catch {
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
const wasm = getWasm();
|
|
1768
|
+
if (wasm) {
|
|
1769
|
+
try {
|
|
1770
|
+
const arrayJson = wasm.decompressVector(compressedJson, dimensions);
|
|
1771
|
+
const arr = JSON.parse(arrayJson);
|
|
1772
|
+
return new Float32Array(arr);
|
|
1773
|
+
} catch {
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
return null;
|
|
1777
|
+
}
|
|
1778
|
+
function approximateDistance(aJson, bJson, dimensions) {
|
|
1779
|
+
const wasm = getWasm();
|
|
1780
|
+
if (wasm) {
|
|
1781
|
+
try {
|
|
1782
|
+
return wasm.approximateDistance(aJson, bJson, dimensions);
|
|
1783
|
+
} catch {
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return null;
|
|
1787
|
+
}
|
|
1788
|
+
function evaluateWriteSecurity(namespace, content, allowedNamespaces, source = "agent", trustLevel = "agent") {
|
|
1789
|
+
const native = getNative();
|
|
1790
|
+
if (native) {
|
|
1791
|
+
try {
|
|
1792
|
+
const firewall = new native.JsWriteFirewall(
|
|
1793
|
+
JSON.stringify({ allowed_namespaces: allowedNamespaces })
|
|
1794
|
+
);
|
|
1795
|
+
const result = JSON.parse(
|
|
1796
|
+
firewall.evaluate(JSON.stringify({ namespace, content, trust_level: trustLevel }))
|
|
1797
|
+
);
|
|
1798
|
+
return result;
|
|
1799
|
+
} catch {
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
try {
|
|
1803
|
+
const result = evaluateWriteFirewall({
|
|
1804
|
+
namespace,
|
|
1805
|
+
content,
|
|
1806
|
+
trust_level: trustLevel,
|
|
1807
|
+
source,
|
|
1808
|
+
allowed_namespaces: allowedNamespaces.length > 0 ? allowedNamespaces : void 0
|
|
1809
|
+
});
|
|
1810
|
+
return {
|
|
1811
|
+
allowed: result.decision === "allow" || result.decision === "sanitize",
|
|
1812
|
+
reason: result.reason
|
|
1813
|
+
};
|
|
1814
|
+
} catch {
|
|
1815
|
+
return { allowed: true };
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// src/rsi/metrics.ts
|
|
1820
|
+
import { readFile as readFile4, appendFile as appendFile2, mkdir as mkdir4 } from "fs/promises";
|
|
1821
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1822
|
+
import { dirname as dirname5 } from "path";
|
|
1823
|
+
var MetricsCollector = class {
|
|
1824
|
+
taskMetricsPath;
|
|
1825
|
+
skillMetricsPath;
|
|
1826
|
+
constructor(taskMetricsPath, skillMetricsPath) {
|
|
1827
|
+
this.taskMetricsPath = taskMetricsPath;
|
|
1828
|
+
this.skillMetricsPath = skillMetricsPath;
|
|
1829
|
+
}
|
|
1830
|
+
async ensureDir(filePath) {
|
|
1831
|
+
const dir = dirname5(filePath);
|
|
1832
|
+
if (!existsSync5(dir)) {
|
|
1833
|
+
await mkdir4(dir, { recursive: true });
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
async recordTaskMetrics(task) {
|
|
1837
|
+
await this.ensureDir(this.taskMetricsPath);
|
|
1838
|
+
const line = JSON.stringify(task) + "\n";
|
|
1839
|
+
await appendFile2(this.taskMetricsPath, line, "utf-8");
|
|
1840
|
+
}
|
|
1841
|
+
async recordSkillMetrics(skill) {
|
|
1842
|
+
await this.ensureDir(this.skillMetricsPath);
|
|
1843
|
+
const line = JSON.stringify(skill) + "\n";
|
|
1844
|
+
await appendFile2(this.skillMetricsPath, line, "utf-8");
|
|
1845
|
+
}
|
|
1846
|
+
async getTaskHistory(limit) {
|
|
1847
|
+
const entries = await this.readJsonl(this.taskMetricsPath);
|
|
1848
|
+
if (limit !== void 0) {
|
|
1849
|
+
return entries.slice(-limit);
|
|
1850
|
+
}
|
|
1851
|
+
return entries;
|
|
1852
|
+
}
|
|
1853
|
+
async getSkillHistory(skillName, limit) {
|
|
1854
|
+
const all = await this.readJsonl(this.skillMetricsPath);
|
|
1855
|
+
const filtered = all.filter((s) => s.skillName === skillName);
|
|
1856
|
+
if (limit !== void 0) {
|
|
1857
|
+
return filtered.slice(-limit);
|
|
1858
|
+
}
|
|
1859
|
+
return filtered;
|
|
1860
|
+
}
|
|
1861
|
+
async getAggregatedSkillStats(skillName) {
|
|
1862
|
+
const history = await this.getSkillHistory(skillName);
|
|
1863
|
+
if (history.length === 0) {
|
|
1864
|
+
return {
|
|
1865
|
+
skillName,
|
|
1866
|
+
totalInvocations: 0,
|
|
1867
|
+
successRate: 0,
|
|
1868
|
+
avgDurationMs: 0,
|
|
1869
|
+
trendDirection: "stable"
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
const invokedEntries = history.filter((h) => h.invoked);
|
|
1873
|
+
const totalInvocations = invokedEntries.length;
|
|
1874
|
+
const successCount = invokedEntries.filter((h) => h.succeeded).length;
|
|
1875
|
+
const successRate = totalInvocations > 0 ? successCount / totalInvocations : 0;
|
|
1876
|
+
const avgDurationMs = totalInvocations > 0 ? invokedEntries.reduce((sum, h) => sum + h.durationMs, 0) / totalInvocations : 0;
|
|
1877
|
+
const trendDirection = this.calculateTrend(invokedEntries);
|
|
1878
|
+
return {
|
|
1879
|
+
skillName,
|
|
1880
|
+
totalInvocations,
|
|
1881
|
+
successRate,
|
|
1882
|
+
avgDurationMs,
|
|
1883
|
+
trendDirection
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
calculateTrend(entries) {
|
|
1887
|
+
if (entries.length < 4) {
|
|
1888
|
+
return "stable";
|
|
1889
|
+
}
|
|
1890
|
+
const mid = Math.floor(entries.length / 2);
|
|
1891
|
+
const firstHalf = entries.slice(0, mid);
|
|
1892
|
+
const secondHalf = entries.slice(mid);
|
|
1893
|
+
const firstRate = firstHalf.filter((e) => e.succeeded).length / firstHalf.length;
|
|
1894
|
+
const secondRate = secondHalf.filter((e) => e.succeeded).length / secondHalf.length;
|
|
1895
|
+
const diff = secondRate - firstRate;
|
|
1896
|
+
if (diff > 0.1) return "improving";
|
|
1897
|
+
if (diff < -0.1) return "declining";
|
|
1898
|
+
return "stable";
|
|
1899
|
+
}
|
|
1900
|
+
async readJsonl(filePath) {
|
|
1901
|
+
if (!existsSync5(filePath)) {
|
|
1902
|
+
return [];
|
|
1903
|
+
}
|
|
1904
|
+
const content = await readFile4(filePath, "utf-8");
|
|
1905
|
+
const lines = content.split("\n").filter((line) => line.trim().length > 0);
|
|
1906
|
+
const results = [];
|
|
1907
|
+
for (const line of lines) {
|
|
1908
|
+
try {
|
|
1909
|
+
results.push(JSON.parse(line));
|
|
1910
|
+
} catch {
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return results;
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
// src/rsi/hypothesis.ts
|
|
1918
|
+
import { randomUUID } from "crypto";
|
|
1919
|
+
var MIN_DATA_POINTS = 10;
|
|
1920
|
+
var LOW_SUCCESS_THRESHOLD = 0.6;
|
|
1921
|
+
var SLOW_SKILL_PERCENTILE = 0.75;
|
|
1922
|
+
var HypothesisEngine = class {
|
|
1923
|
+
analyze(skillStats, taskHistory) {
|
|
1924
|
+
if (taskHistory.length < MIN_DATA_POINTS) {
|
|
1925
|
+
return [];
|
|
1926
|
+
}
|
|
1927
|
+
const hypotheses = [];
|
|
1928
|
+
hypotheses.push(...this.detectLowSuccessRate(skillStats));
|
|
1929
|
+
hypotheses.push(...this.detectSlowSkills(skillStats));
|
|
1930
|
+
hypotheses.push(...this.detectFrequentlyPairedSkills(taskHistory));
|
|
1931
|
+
hypotheses.push(...this.detectUnusedInSuccessful(skillStats, taskHistory));
|
|
1932
|
+
return hypotheses;
|
|
1933
|
+
}
|
|
1934
|
+
detectLowSuccessRate(stats) {
|
|
1935
|
+
const hypotheses = [];
|
|
1936
|
+
for (const skill of stats) {
|
|
1937
|
+
if (skill.totalInvocations >= 5 && skill.successRate < LOW_SUCCESS_THRESHOLD) {
|
|
1938
|
+
const expectedImprovement = Math.round((LOW_SUCCESS_THRESHOLD - skill.successRate) * 100);
|
|
1939
|
+
hypotheses.push({
|
|
1940
|
+
hypothesisId: randomUUID(),
|
|
1941
|
+
skillName: skill.skillName,
|
|
1942
|
+
description: `Adjusting retry count for ${skill.skillName} may improve success by ${expectedImprovement}%`,
|
|
1943
|
+
expectedImprovement,
|
|
1944
|
+
tier: "T1",
|
|
1945
|
+
confidence: Math.min(0.9, skill.totalInvocations / 20),
|
|
1946
|
+
evidence: [
|
|
1947
|
+
`Current success rate: ${Math.round(skill.successRate * 100)}%`,
|
|
1948
|
+
`Total invocations: ${skill.totalInvocations}`,
|
|
1949
|
+
`Trend: ${skill.trendDirection}`
|
|
1950
|
+
]
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return hypotheses;
|
|
1955
|
+
}
|
|
1956
|
+
detectSlowSkills(stats) {
|
|
1957
|
+
const hypotheses = [];
|
|
1958
|
+
const withInvocations = stats.filter((s) => s.totalInvocations >= 5);
|
|
1959
|
+
if (withInvocations.length < 2) return hypotheses;
|
|
1960
|
+
const durations = withInvocations.map((s) => s.avgDurationMs).sort((a, b) => a - b);
|
|
1961
|
+
const percentileIdx = Math.max(0, Math.floor(durations.length * SLOW_SKILL_PERCENTILE) - 1);
|
|
1962
|
+
const threshold = durations[percentileIdx] ?? Infinity;
|
|
1963
|
+
for (const skill of withInvocations) {
|
|
1964
|
+
if (skill.avgDurationMs > threshold) {
|
|
1965
|
+
const expectedImprovement = Math.round(
|
|
1966
|
+
(skill.avgDurationMs - threshold) / skill.avgDurationMs * 100
|
|
1967
|
+
);
|
|
1968
|
+
hypotheses.push({
|
|
1969
|
+
hypothesisId: randomUUID(),
|
|
1970
|
+
skillName: skill.skillName,
|
|
1971
|
+
description: `Increasing timeout for ${skill.skillName} may reduce failures by ${expectedImprovement}%`,
|
|
1972
|
+
expectedImprovement,
|
|
1973
|
+
tier: "T1",
|
|
1974
|
+
confidence: Math.min(0.8, skill.totalInvocations / 25),
|
|
1975
|
+
evidence: [
|
|
1976
|
+
`Avg duration: ${Math.round(skill.avgDurationMs)}ms`,
|
|
1977
|
+
`75th percentile threshold: ${Math.round(threshold)}ms`
|
|
1978
|
+
]
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return hypotheses;
|
|
1983
|
+
}
|
|
1984
|
+
detectFrequentlyPairedSkills(taskHistory) {
|
|
1985
|
+
const hypotheses = [];
|
|
1986
|
+
const pairCounts = /* @__PURE__ */ new Map();
|
|
1987
|
+
for (const task of taskHistory) {
|
|
1988
|
+
const skills = task.skillsUsed;
|
|
1989
|
+
for (let i = 0; i < skills.length; i++) {
|
|
1990
|
+
for (let j = i + 1; j < skills.length; j++) {
|
|
1991
|
+
const pair = [skills[i], skills[j]].sort().join("+");
|
|
1992
|
+
pairCounts.set(pair, (pairCounts.get(pair) ?? 0) + 1);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
for (const [pair, count] of pairCounts) {
|
|
1997
|
+
if (count >= 5) {
|
|
1998
|
+
const [skillA, skillB] = pair.split("+");
|
|
1999
|
+
hypotheses.push({
|
|
2000
|
+
hypothesisId: randomUUID(),
|
|
2001
|
+
skillName: `${skillA}+${skillB}`,
|
|
2002
|
+
description: `Creating a chain of ${skillA}+${skillB} may improve efficiency`,
|
|
2003
|
+
expectedImprovement: Math.round(count / taskHistory.length * 30),
|
|
2004
|
+
tier: "T3",
|
|
2005
|
+
confidence: Math.min(0.7, count / 15),
|
|
2006
|
+
evidence: [
|
|
2007
|
+
`Co-occurrence: ${count}/${taskHistory.length} tasks`
|
|
2008
|
+
]
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
return hypotheses;
|
|
2013
|
+
}
|
|
2014
|
+
detectUnusedInSuccessful(stats, taskHistory) {
|
|
2015
|
+
const hypotheses = [];
|
|
2016
|
+
const successfulTasks = taskHistory.filter((t) => t.outcome === "success");
|
|
2017
|
+
if (successfulTasks.length < 5) return hypotheses;
|
|
2018
|
+
const successfulSkills = /* @__PURE__ */ new Set();
|
|
2019
|
+
for (const task of successfulTasks) {
|
|
2020
|
+
for (const skill of task.skillsUsed) {
|
|
2021
|
+
successfulSkills.add(skill);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
for (const skill of stats) {
|
|
2025
|
+
if (skill.totalInvocations >= 5 && !successfulSkills.has(skill.skillName) && skill.successRate < 0.5) {
|
|
2026
|
+
hypotheses.push({
|
|
2027
|
+
hypothesisId: randomUUID(),
|
|
2028
|
+
skillName: skill.skillName,
|
|
2029
|
+
description: `Deprioritizing ${skill.skillName} for current task types`,
|
|
2030
|
+
expectedImprovement: 10,
|
|
2031
|
+
tier: "T2",
|
|
2032
|
+
confidence: 0.5,
|
|
2033
|
+
evidence: [
|
|
2034
|
+
`Not used in any of ${successfulTasks.length} successful tasks`,
|
|
2035
|
+
`Success rate: ${Math.round(skill.successRate * 100)}%`
|
|
2036
|
+
]
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
return hypotheses;
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
|
|
2044
|
+
// src/rsi/mutation.ts
|
|
2045
|
+
import { readFile as readFile5, appendFile as appendFile3, mkdir as mkdir5 } from "fs/promises";
|
|
2046
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2047
|
+
import { dirname as dirname6 } from "path";
|
|
2048
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2049
|
+
var MutationEngine = class {
|
|
2050
|
+
historyPath;
|
|
2051
|
+
constructor(historyPath) {
|
|
2052
|
+
this.historyPath = historyPath;
|
|
2053
|
+
}
|
|
2054
|
+
async ensureDir() {
|
|
2055
|
+
const dir = dirname6(this.historyPath);
|
|
2056
|
+
if (!existsSync6(dir)) {
|
|
2057
|
+
await mkdir5(dir, { recursive: true });
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
createMutation(hypothesis) {
|
|
2061
|
+
this.validateSafety(hypothesis);
|
|
2062
|
+
const initialStatus = hypothesis.tier === "T4" ? "proposed" : "proposed";
|
|
2063
|
+
return {
|
|
2064
|
+
mutationId: randomUUID2(),
|
|
2065
|
+
hypothesisId: hypothesis.hypothesisId,
|
|
2066
|
+
skillName: hypothesis.skillName,
|
|
2067
|
+
tier: hypothesis.tier,
|
|
2068
|
+
description: hypothesis.description,
|
|
2069
|
+
originalValue: "",
|
|
2070
|
+
mutatedValue: hypothesis.description,
|
|
2071
|
+
status: initialStatus,
|
|
2072
|
+
appliedAt: null,
|
|
2073
|
+
revertedAt: null
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
async applyMutation(mutation) {
|
|
2077
|
+
if (this.isSafetyInvariant(mutation.skillName)) {
|
|
2078
|
+
throw new Error(
|
|
2079
|
+
`Cannot mutate safety invariant: ${mutation.skillName}. Safety invariants are: ${SAFETY_INVARIANTS.join(", ")}`
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
2082
|
+
if (mutation.tier === "T4") {
|
|
2083
|
+
throw new Error(
|
|
2084
|
+
"T4 mutations (Architecture) cannot be auto-applied. They must be proposed and reviewed by a human."
|
|
2085
|
+
);
|
|
2086
|
+
}
|
|
2087
|
+
const applied = {
|
|
2088
|
+
...mutation,
|
|
2089
|
+
status: "applied",
|
|
2090
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2091
|
+
};
|
|
2092
|
+
await this.appendHistory(applied);
|
|
2093
|
+
}
|
|
2094
|
+
async revertMutation(mutation) {
|
|
2095
|
+
const reverted = {
|
|
2096
|
+
...mutation,
|
|
2097
|
+
status: "reverted",
|
|
2098
|
+
revertedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2099
|
+
};
|
|
2100
|
+
await this.appendHistory(reverted);
|
|
2101
|
+
}
|
|
2102
|
+
async getMutationHistory() {
|
|
2103
|
+
if (!existsSync6(this.historyPath)) {
|
|
2104
|
+
return [];
|
|
2105
|
+
}
|
|
2106
|
+
const content = await readFile5(this.historyPath, "utf-8");
|
|
2107
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
2108
|
+
const results = [];
|
|
2109
|
+
for (const line of lines) {
|
|
2110
|
+
try {
|
|
2111
|
+
results.push(JSON.parse(line));
|
|
2112
|
+
} catch {
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return results;
|
|
2116
|
+
}
|
|
2117
|
+
async appendHistory(mutation) {
|
|
2118
|
+
await this.ensureDir();
|
|
2119
|
+
const line = JSON.stringify(mutation) + "\n";
|
|
2120
|
+
await appendFile3(this.historyPath, line, "utf-8");
|
|
2121
|
+
}
|
|
2122
|
+
validateSafety(hypothesis) {
|
|
2123
|
+
if (this.isSafetyInvariant(hypothesis.skillName)) {
|
|
2124
|
+
throw new Error(
|
|
2125
|
+
`Cannot create mutation targeting safety invariant: ${hypothesis.skillName}`
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
isSafetyInvariant(name) {
|
|
2130
|
+
const nameLower = name.toLowerCase();
|
|
2131
|
+
return SAFETY_INVARIANTS.some(
|
|
2132
|
+
(invariant) => nameLower.includes(invariant.toLowerCase()) || invariant.toLowerCase().includes(nameLower)
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
|
|
2137
|
+
// src/rsi/ab-test.ts
|
|
2138
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2139
|
+
var DEFAULT_MIN_SAMPLE_SIZE = 5;
|
|
2140
|
+
var PROMOTION_THRESHOLD = 0.1;
|
|
2141
|
+
var ROLLBACK_THRESHOLD = -0.1;
|
|
2142
|
+
var ABTestManager = class {
|
|
2143
|
+
tests = /* @__PURE__ */ new Map();
|
|
2144
|
+
results = /* @__PURE__ */ new Map();
|
|
2145
|
+
startTest(mutation, baselineMetrics) {
|
|
2146
|
+
const testId = randomUUID3();
|
|
2147
|
+
const test = {
|
|
2148
|
+
testId,
|
|
2149
|
+
mutationId: mutation.mutationId,
|
|
2150
|
+
skillName: mutation.skillName,
|
|
2151
|
+
baselineStats: baselineMetrics,
|
|
2152
|
+
variantStats: {
|
|
2153
|
+
skillName: mutation.skillName,
|
|
2154
|
+
totalInvocations: 0,
|
|
2155
|
+
successRate: 0,
|
|
2156
|
+
avgDurationMs: 0,
|
|
2157
|
+
trendDirection: "stable"
|
|
2158
|
+
},
|
|
2159
|
+
sampleSize: 0,
|
|
2160
|
+
minSampleSize: DEFAULT_MIN_SAMPLE_SIZE,
|
|
2161
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2162
|
+
status: "running"
|
|
2163
|
+
};
|
|
2164
|
+
this.tests.set(testId, test);
|
|
2165
|
+
this.results.set(testId, []);
|
|
2166
|
+
return test;
|
|
2167
|
+
}
|
|
2168
|
+
recordResult(testId, taskMetrics) {
|
|
2169
|
+
const test = this.tests.get(testId);
|
|
2170
|
+
if (!test) {
|
|
2171
|
+
throw new Error(`A/B test "${testId}" not found`);
|
|
2172
|
+
}
|
|
2173
|
+
if (test.status !== "running") {
|
|
2174
|
+
throw new Error(`A/B test "${testId}" is not running (status: ${test.status})`);
|
|
2175
|
+
}
|
|
2176
|
+
const taskResults = this.results.get(testId) ?? [];
|
|
2177
|
+
taskResults.push(taskMetrics);
|
|
2178
|
+
this.results.set(testId, taskResults);
|
|
2179
|
+
const successCount = taskResults.filter((t) => t.outcome === "success").length;
|
|
2180
|
+
const totalDuration = taskResults.reduce((sum, t) => sum + t.durationMs, 0);
|
|
2181
|
+
const updatedTest = {
|
|
2182
|
+
...test,
|
|
2183
|
+
sampleSize: taskResults.length,
|
|
2184
|
+
variantStats: {
|
|
2185
|
+
skillName: test.skillName,
|
|
2186
|
+
totalInvocations: taskResults.length,
|
|
2187
|
+
successRate: taskResults.length > 0 ? successCount / taskResults.length : 0,
|
|
2188
|
+
avgDurationMs: taskResults.length > 0 ? totalDuration / taskResults.length : 0,
|
|
2189
|
+
trendDirection: "stable"
|
|
2190
|
+
}
|
|
2191
|
+
};
|
|
2192
|
+
this.tests.set(testId, updatedTest);
|
|
2193
|
+
}
|
|
2194
|
+
evaluateTest(testId) {
|
|
2195
|
+
const test = this.tests.get(testId);
|
|
2196
|
+
if (!test) {
|
|
2197
|
+
throw new Error(`A/B test "${testId}" not found`);
|
|
2198
|
+
}
|
|
2199
|
+
if (test.sampleSize < test.minSampleSize) {
|
|
2200
|
+
return {
|
|
2201
|
+
testId,
|
|
2202
|
+
decision: "continue",
|
|
2203
|
+
improvement: 0,
|
|
2204
|
+
confidence: test.sampleSize / test.minSampleSize
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
const baselineRate = test.baselineStats.successRate;
|
|
2208
|
+
const variantRate = test.variantStats.successRate;
|
|
2209
|
+
const improvement = baselineRate > 0 ? (variantRate - baselineRate) / baselineRate : variantRate > 0 ? 1 : 0;
|
|
2210
|
+
const confidence = Math.min(1, test.sampleSize / (test.minSampleSize * 2));
|
|
2211
|
+
let decision;
|
|
2212
|
+
if (improvement > PROMOTION_THRESHOLD) {
|
|
2213
|
+
decision = "promote";
|
|
2214
|
+
} else if (improvement < ROLLBACK_THRESHOLD) {
|
|
2215
|
+
decision = "rollback";
|
|
2216
|
+
} else {
|
|
2217
|
+
decision = "continue";
|
|
2218
|
+
}
|
|
2219
|
+
if (decision === "promote" || decision === "rollback") {
|
|
2220
|
+
const updated = {
|
|
2221
|
+
...test,
|
|
2222
|
+
status: "completed"
|
|
2223
|
+
};
|
|
2224
|
+
this.tests.set(testId, updated);
|
|
2225
|
+
}
|
|
2226
|
+
return {
|
|
2227
|
+
testId,
|
|
2228
|
+
decision,
|
|
2229
|
+
improvement,
|
|
2230
|
+
confidence
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
getActiveTests() {
|
|
2234
|
+
const active = [];
|
|
2235
|
+
for (const test of this.tests.values()) {
|
|
2236
|
+
if (test.status === "running") {
|
|
2237
|
+
active.push(test);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
return active;
|
|
2241
|
+
}
|
|
2242
|
+
};
|
|
2243
|
+
|
|
2244
|
+
// src/rsi/audit.ts
|
|
2245
|
+
import { readFile as readFile6, appendFile as appendFile4, mkdir as mkdir6 } from "fs/promises";
|
|
2246
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2247
|
+
import { dirname as dirname7 } from "path";
|
|
2248
|
+
var RSIAuditLog = class {
|
|
2249
|
+
filePath;
|
|
2250
|
+
constructor(filePath) {
|
|
2251
|
+
this.filePath = filePath;
|
|
2252
|
+
}
|
|
2253
|
+
async ensureDir() {
|
|
2254
|
+
const dir = dirname7(this.filePath);
|
|
2255
|
+
if (!existsSync7(dir)) {
|
|
2256
|
+
await mkdir6(dir, { recursive: true });
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
async log(entry) {
|
|
2260
|
+
await this.ensureDir();
|
|
2261
|
+
const line = JSON.stringify(entry) + "\n";
|
|
2262
|
+
await appendFile4(this.filePath, line, "utf-8");
|
|
2263
|
+
}
|
|
2264
|
+
async getHistory(limit) {
|
|
2265
|
+
if (!existsSync7(this.filePath)) {
|
|
2266
|
+
return [];
|
|
2267
|
+
}
|
|
2268
|
+
const content = await readFile6(this.filePath, "utf-8");
|
|
2269
|
+
const lines = content.split("\n").filter((l) => l.trim().length > 0);
|
|
2270
|
+
const entries = [];
|
|
2271
|
+
for (const line of lines) {
|
|
2272
|
+
try {
|
|
2273
|
+
entries.push(JSON.parse(line));
|
|
2274
|
+
} catch {
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
if (limit !== void 0) {
|
|
2278
|
+
return entries.slice(-limit);
|
|
2279
|
+
}
|
|
2280
|
+
return entries;
|
|
2281
|
+
}
|
|
2282
|
+
async getByMutation(mutationId) {
|
|
2283
|
+
const all = await this.getHistory();
|
|
2284
|
+
return all.filter((e) => e.mutationId === mutationId);
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
|
|
2288
|
+
// src/rsi/auto-research.ts
|
|
2289
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
2290
|
+
import { execSync } from "child_process";
|
|
2291
|
+
import { mkdirSync as mkdirSync2, existsSync as existsSync8, writeFileSync as writeFileSync2 } from "fs";
|
|
2292
|
+
import { join as join4 } from "path";
|
|
2293
|
+
import { tmpdir } from "os";
|
|
2294
|
+
var MIN_CONFIDENCE = 0.3;
|
|
2295
|
+
var REQUIRED_PASSING_RUNS = 3;
|
|
2296
|
+
function buildSearchQuery(failure) {
|
|
2297
|
+
const clean = failure.error.replace(/\x1b\[[0-9;]*m/g, "");
|
|
2298
|
+
const firstLine = clean.split("\n")[0]?.trim() ?? clean.trim();
|
|
2299
|
+
const noPath = firstLine.replace(/\/[^\s:]+:\d+:\d+/g, "").trim();
|
|
2300
|
+
const taskSummary = failure.taskDescription.slice(0, 60).trim();
|
|
2301
|
+
const errorSummary = noPath.slice(0, 100).trim();
|
|
2302
|
+
if (!errorSummary) {
|
|
2303
|
+
return `${taskSummary} fix solution`;
|
|
2304
|
+
}
|
|
2305
|
+
return `${taskSummary} ${errorSummary}`.trim();
|
|
2306
|
+
}
|
|
2307
|
+
function scoreConfidence(candidate, failure) {
|
|
2308
|
+
let base = 0;
|
|
2309
|
+
switch (candidate.source) {
|
|
2310
|
+
case "skill-catalog":
|
|
2311
|
+
base = 0.7;
|
|
2312
|
+
break;
|
|
2313
|
+
case "npm-registry":
|
|
2314
|
+
base = 0.5;
|
|
2315
|
+
break;
|
|
2316
|
+
case "web-search":
|
|
2317
|
+
base = 0.4;
|
|
2318
|
+
break;
|
|
2319
|
+
}
|
|
2320
|
+
const failureWords = new Set(
|
|
2321
|
+
failure.taskDescription.toLowerCase().split(/\W+/).filter((w) => w.length > 3)
|
|
2322
|
+
);
|
|
2323
|
+
const approachWords = candidate.approach.toLowerCase().split(/\W+/);
|
|
2324
|
+
const matches = approachWords.filter((w) => failureWords.has(w)).length;
|
|
2325
|
+
const overlap = Math.min(matches / Math.max(failureWords.size, 1), 0.25);
|
|
2326
|
+
base += overlap;
|
|
2327
|
+
if (candidate.description.length < 20) {
|
|
2328
|
+
base -= 0.1;
|
|
2329
|
+
}
|
|
2330
|
+
return Math.max(0, Math.min(1, base));
|
|
2331
|
+
}
|
|
2332
|
+
var AutoResearcher = class {
|
|
2333
|
+
skillsDir;
|
|
2334
|
+
constructor(skillsDir) {
|
|
2335
|
+
this.skillsDir = skillsDir ?? join4(tmpdir(), "clawpowers-promoted-skills");
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Search for candidate solutions to a failure.
|
|
2339
|
+
*
|
|
2340
|
+
* Sources queried (in order of reliability):
|
|
2341
|
+
* 1. skill-catalog — skills already available that match the error domain
|
|
2342
|
+
* 2. npm-registry — packages that match the error keywords
|
|
2343
|
+
* 3. web-search — constructs a query from the failure trace
|
|
2344
|
+
*
|
|
2345
|
+
* Returns candidates sorted by confidence (highest first).
|
|
2346
|
+
*/
|
|
2347
|
+
async research(failure) {
|
|
2348
|
+
const candidates = [];
|
|
2349
|
+
const skillCatalogCandidates = this.searchSkillCatalog(failure);
|
|
2350
|
+
candidates.push(...skillCatalogCandidates);
|
|
2351
|
+
const npmCandidates = await this.searchNpmRegistry(failure);
|
|
2352
|
+
candidates.push(...npmCandidates);
|
|
2353
|
+
const webCandidates = this.buildWebSearchCandidates(failure);
|
|
2354
|
+
candidates.push(...webCandidates);
|
|
2355
|
+
return candidates.filter((c) => c.confidence >= MIN_CONFIDENCE).sort((a, b) => b.confidence - a.confidence);
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* Test a candidate solution in an isolated sandbox.
|
|
2359
|
+
* Runs the candidate's approach as a shell command in a temp directory.
|
|
2360
|
+
* Returns a TestResult with pass/fail, output, and duration.
|
|
2361
|
+
*/
|
|
2362
|
+
async testCandidate(candidate, task) {
|
|
2363
|
+
const startMs = Date.now();
|
|
2364
|
+
const sandboxDir = join4(tmpdir(), `clawpowers-sandbox-${randomUUID4()}`);
|
|
2365
|
+
try {
|
|
2366
|
+
mkdirSync2(sandboxDir, { recursive: true });
|
|
2367
|
+
const testScript = this.buildTestScript(candidate, task, sandboxDir);
|
|
2368
|
+
const scriptPath = join4(sandboxDir, "test.sh");
|
|
2369
|
+
writeFileSync2(scriptPath, testScript, { mode: 493 });
|
|
2370
|
+
const output = execSync(`bash "${scriptPath}"`, {
|
|
2371
|
+
cwd: sandboxDir,
|
|
2372
|
+
timeout: 3e4,
|
|
2373
|
+
encoding: "utf-8",
|
|
2374
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2375
|
+
});
|
|
2376
|
+
return {
|
|
2377
|
+
passed: true,
|
|
2378
|
+
output: output.toString().slice(0, 2e3),
|
|
2379
|
+
durationMs: Date.now() - startMs,
|
|
2380
|
+
attempt: 1
|
|
2381
|
+
};
|
|
2382
|
+
} catch (err) {
|
|
2383
|
+
const output = err instanceof Error ? err.message : String(err);
|
|
2384
|
+
return {
|
|
2385
|
+
passed: false,
|
|
2386
|
+
output: output.slice(0, 2e3),
|
|
2387
|
+
durationMs: Date.now() - startMs,
|
|
2388
|
+
attempt: 1
|
|
2389
|
+
};
|
|
2390
|
+
} finally {
|
|
2391
|
+
try {
|
|
2392
|
+
execSync(`rm -rf "${sandboxDir}"`, { timeout: 5e3, stdio: "ignore" });
|
|
2393
|
+
} catch {
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Run a candidate through REQUIRED_PASSING_RUNS sandbox tests.
|
|
2399
|
+
* Returns all TestResult objects. If fewer than REQUIRED_PASSING_RUNS pass,
|
|
2400
|
+
* the candidate is NOT promoted (caller must check).
|
|
2401
|
+
*/
|
|
2402
|
+
async runSandboxTests(candidate, task) {
|
|
2403
|
+
const results = [];
|
|
2404
|
+
for (let i = 1; i <= REQUIRED_PASSING_RUNS; i++) {
|
|
2405
|
+
const result = await this.testCandidate(candidate, task);
|
|
2406
|
+
results.push({ ...result, attempt: i });
|
|
2407
|
+
if (!result.passed) {
|
|
2408
|
+
break;
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
return results;
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Promote a candidate solution to a new skill definition.
|
|
2415
|
+
* Writes a minimal SKILL.md to the skills directory and returns
|
|
2416
|
+
* the SkillDefinition.
|
|
2417
|
+
*/
|
|
2418
|
+
async promoteToSkill(candidate, testResults) {
|
|
2419
|
+
const passingRuns = testResults.filter((r) => r.passed).length;
|
|
2420
|
+
if (passingRuns < REQUIRED_PASSING_RUNS) {
|
|
2421
|
+
throw new Error(
|
|
2422
|
+
`Cannot promote candidate: only ${passingRuns}/${REQUIRED_PASSING_RUNS} test runs passed.`
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
const skillId = `auto-${randomUUID4().slice(0, 8)}`;
|
|
2426
|
+
const skillName = this.deriveSkillName(candidate);
|
|
2427
|
+
const definition = {
|
|
2428
|
+
skillId,
|
|
2429
|
+
name: skillName,
|
|
2430
|
+
description: candidate.description,
|
|
2431
|
+
approach: candidate.approach,
|
|
2432
|
+
source: candidate.source,
|
|
2433
|
+
promotedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2434
|
+
testResults
|
|
2435
|
+
};
|
|
2436
|
+
const skillDir = join4(this.skillsDir, skillName);
|
|
2437
|
+
if (!existsSync8(skillDir)) {
|
|
2438
|
+
mkdirSync2(skillDir, { recursive: true });
|
|
2439
|
+
}
|
|
2440
|
+
const skillMd = this.renderSkillMd(definition);
|
|
2441
|
+
writeFileSync2(join4(skillDir, "SKILL.md"), skillMd, "utf-8");
|
|
2442
|
+
return definition;
|
|
2443
|
+
}
|
|
2444
|
+
// ─── Private Methods ───────────────────────────────────────────────────────
|
|
2445
|
+
searchSkillCatalog(failure) {
|
|
2446
|
+
try {
|
|
2447
|
+
const { SKILLS_CATALOG: SKILLS_CATALOG2 } = (init_catalog(), __toCommonJS(catalog_exports));
|
|
2448
|
+
const errorTokens = failure.error.toLowerCase().split(/\W+/).filter((t) => t.length > 3);
|
|
2449
|
+
const taskTokens = failure.taskDescription.toLowerCase().split(/\W+/).filter((t) => t.length > 3);
|
|
2450
|
+
const relevantTokens = /* @__PURE__ */ new Set([...errorTokens, ...taskTokens]);
|
|
2451
|
+
return SKILLS_CATALOG2.filter((skill) => {
|
|
2452
|
+
const haystack = `${skill.name} ${skill.description}`.toLowerCase();
|
|
2453
|
+
return [...relevantTokens].some((token) => haystack.includes(token));
|
|
2454
|
+
}).slice(0, 3).map((skill) => {
|
|
2455
|
+
const base = {
|
|
2456
|
+
source: "skill-catalog",
|
|
2457
|
+
description: skill.description,
|
|
2458
|
+
approach: `Use the '${skill.name}' skill (category: ${skill.category}) to address this failure.`
|
|
2459
|
+
};
|
|
2460
|
+
return {
|
|
2461
|
+
...base,
|
|
2462
|
+
confidence: scoreConfidence(base, failure)
|
|
2463
|
+
};
|
|
2464
|
+
});
|
|
2465
|
+
} catch {
|
|
2466
|
+
return [];
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
async searchNpmRegistry(failure) {
|
|
2470
|
+
const query = buildSearchQuery(failure);
|
|
2471
|
+
const keywords = query.split(/\s+/).filter((w) => w.length > 3).slice(0, 5).join("+");
|
|
2472
|
+
try {
|
|
2473
|
+
const raw = execSync(
|
|
2474
|
+
`npm search ${keywords} --json --no-description 2>/dev/null`,
|
|
2475
|
+
{
|
|
2476
|
+
timeout: 15e3,
|
|
2477
|
+
encoding: "utf-8",
|
|
2478
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
2479
|
+
}
|
|
2480
|
+
);
|
|
2481
|
+
const results = JSON.parse(raw);
|
|
2482
|
+
return results.slice(0, 3).map((pkg) => {
|
|
2483
|
+
const base = {
|
|
2484
|
+
source: "npm-registry",
|
|
2485
|
+
description: pkg.description ?? pkg.name,
|
|
2486
|
+
approach: `Install and use npm package '${pkg.name}' to resolve this failure.`
|
|
2487
|
+
};
|
|
2488
|
+
return {
|
|
2489
|
+
...base,
|
|
2490
|
+
confidence: scoreConfidence(base, failure)
|
|
2491
|
+
};
|
|
2492
|
+
});
|
|
2493
|
+
} catch {
|
|
2494
|
+
return [];
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
buildWebSearchCandidates(failure) {
|
|
2498
|
+
const query = buildSearchQuery(failure);
|
|
2499
|
+
const base = {
|
|
2500
|
+
source: "web-search",
|
|
2501
|
+
description: `Web search for: "${query}"`,
|
|
2502
|
+
approach: `Search the web for "${query}" and apply the top-ranked solution approach.`
|
|
2503
|
+
};
|
|
2504
|
+
return [
|
|
2505
|
+
{
|
|
2506
|
+
...base,
|
|
2507
|
+
confidence: scoreConfidence(base, failure)
|
|
2508
|
+
}
|
|
2509
|
+
];
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Build a sandboxed test script for a candidate solution.
|
|
2513
|
+
* Validates the candidate's approach by checking its core prerequisites
|
|
2514
|
+
* (e.g. that a package is installed, or a command is available).
|
|
2515
|
+
*/
|
|
2516
|
+
buildTestScript(candidate, task, _sandboxDir) {
|
|
2517
|
+
const lines = [
|
|
2518
|
+
"#!/usr/bin/env bash",
|
|
2519
|
+
"set -euo pipefail",
|
|
2520
|
+
"",
|
|
2521
|
+
"# AutoResearch sandbox test",
|
|
2522
|
+
`# Task: ${task.description.replace(/'/g, "'\\''").slice(0, 200)}`,
|
|
2523
|
+
`# Candidate source: ${candidate.source}`,
|
|
2524
|
+
`# Candidate: ${candidate.description.replace(/'/g, "'\\''").slice(0, 200)}`,
|
|
2525
|
+
""
|
|
2526
|
+
];
|
|
2527
|
+
if (candidate.source === "npm-registry") {
|
|
2528
|
+
const match = /npm package '([^']+)'/.exec(candidate.approach);
|
|
2529
|
+
if (match) {
|
|
2530
|
+
const pkgName = match[1];
|
|
2531
|
+
lines.push(`# Check if the npm package exists in the registry`);
|
|
2532
|
+
lines.push(`npm view "${pkgName}" name > /dev/null 2>&1`);
|
|
2533
|
+
lines.push(`echo "Package '${pkgName}' exists in npm registry"`);
|
|
2534
|
+
} else {
|
|
2535
|
+
lines.push('echo "npm-registry candidate: no package name extractable"');
|
|
2536
|
+
lines.push("exit 0");
|
|
2537
|
+
}
|
|
2538
|
+
} else if (candidate.source === "skill-catalog") {
|
|
2539
|
+
const match = /skill '([^']+)'/.exec(candidate.approach);
|
|
2540
|
+
if (match) {
|
|
2541
|
+
lines.push(`# Validate skill '${match[1]}' reference`);
|
|
2542
|
+
lines.push(`echo "Skill catalog candidate: ${match[1]}"`);
|
|
2543
|
+
lines.push("exit 0");
|
|
2544
|
+
} else {
|
|
2545
|
+
lines.push('echo "skill-catalog candidate validated"');
|
|
2546
|
+
lines.push("exit 0");
|
|
2547
|
+
}
|
|
2548
|
+
} else {
|
|
2549
|
+
const q = buildSearchQuery({ error: candidate.description, taskDescription: task.description, executionSteps: [], skillsUsed: [], attemptCount: 1 });
|
|
2550
|
+
if (q.trim().length > 0) {
|
|
2551
|
+
lines.push(`echo "web-search candidate: query is '${q.replace(/'/g, "").slice(0, 100)}'"`);
|
|
2552
|
+
lines.push("exit 0");
|
|
2553
|
+
} else {
|
|
2554
|
+
lines.push('echo "web-search candidate: empty query" >&2');
|
|
2555
|
+
lines.push("exit 1");
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
lines.push("");
|
|
2559
|
+
lines.push("# Success criteria check");
|
|
2560
|
+
for (const criterion of task.successCriteria.slice(0, 2)) {
|
|
2561
|
+
lines.push(`echo "Criterion: ${criterion.replace(/'/g, "").slice(0, 100)}"`);
|
|
2562
|
+
}
|
|
2563
|
+
lines.push('echo "Test passed"');
|
|
2564
|
+
return lines.join("\n") + "\n";
|
|
2565
|
+
}
|
|
2566
|
+
deriveSkillName(candidate) {
|
|
2567
|
+
const slug = candidate.description.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
2568
|
+
return `auto-${slug}`;
|
|
2569
|
+
}
|
|
2570
|
+
renderSkillMd(def) {
|
|
2571
|
+
return [
|
|
2572
|
+
"---",
|
|
2573
|
+
`name: ${def.name}`,
|
|
2574
|
+
`description: "${def.description.replace(/"/g, "'")}"`,
|
|
2575
|
+
`source: ${def.source}`,
|
|
2576
|
+
`skillId: ${def.skillId}`,
|
|
2577
|
+
`promotedAt: "${def.promotedAt}"`,
|
|
2578
|
+
"---",
|
|
2579
|
+
"",
|
|
2580
|
+
`# ${def.name}`,
|
|
2581
|
+
"",
|
|
2582
|
+
`**Auto-promoted by AutoResearcher on ${def.promotedAt}**`,
|
|
2583
|
+
"",
|
|
2584
|
+
`## Description`,
|
|
2585
|
+
"",
|
|
2586
|
+
def.description,
|
|
2587
|
+
"",
|
|
2588
|
+
`## Approach`,
|
|
2589
|
+
"",
|
|
2590
|
+
def.approach,
|
|
2591
|
+
"",
|
|
2592
|
+
`## Test Results`,
|
|
2593
|
+
"",
|
|
2594
|
+
`Passed ${def.testResults.filter((r) => r.passed).length}/${def.testResults.length} sandbox runs.`,
|
|
2595
|
+
""
|
|
2596
|
+
].join("\n");
|
|
2597
|
+
}
|
|
2598
|
+
};
|
|
2599
|
+
async function runAutoResearch(failure, task, skillsDir) {
|
|
2600
|
+
const researcher = new AutoResearcher(skillsDir);
|
|
2601
|
+
const candidates = await researcher.research(failure);
|
|
2602
|
+
if (candidates.length === 0) {
|
|
2603
|
+
return null;
|
|
2604
|
+
}
|
|
2605
|
+
for (const candidate of candidates) {
|
|
2606
|
+
const results = await researcher.runSandboxTests(candidate, task);
|
|
2607
|
+
const passingRuns = results.filter((r) => r.passed).length;
|
|
2608
|
+
if (passingRuns >= REQUIRED_PASSING_RUNS) {
|
|
2609
|
+
const skill = await researcher.promoteToSkill(candidate, results);
|
|
2610
|
+
return skill;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
return null;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
// src/skills/loader.ts
|
|
2617
|
+
import { readdirSync, readFileSync as readFileSync2, existsSync as existsSync9, statSync } from "fs";
|
|
2618
|
+
import { join as join5 } from "path";
|
|
2619
|
+
function parseFrontmatter(content) {
|
|
2620
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2621
|
+
if (!match?.[1]) {
|
|
2622
|
+
return {};
|
|
2623
|
+
}
|
|
2624
|
+
const yamlText = match[1];
|
|
2625
|
+
const result = {};
|
|
2626
|
+
const lines = yamlText.split("\n");
|
|
2627
|
+
let currentKey = "";
|
|
2628
|
+
let inRequires = false;
|
|
2629
|
+
let requiresKey = "";
|
|
2630
|
+
const requires = {};
|
|
2631
|
+
for (const line of lines) {
|
|
2632
|
+
const trimmed = line.trim();
|
|
2633
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
2634
|
+
const topLevel = line.match(/^(\w+):\s*(.*)$/);
|
|
2635
|
+
if (topLevel?.[1]) {
|
|
2636
|
+
currentKey = topLevel[1];
|
|
2637
|
+
const val = topLevel[2]?.trim().replace(/^["']|["']$/g, "") ?? "";
|
|
2638
|
+
if (currentKey === "name" && val) result.name = val;
|
|
2639
|
+
if (currentKey === "description" && val) result.description = val;
|
|
2640
|
+
inRequires = false;
|
|
2641
|
+
continue;
|
|
2642
|
+
}
|
|
2643
|
+
if (trimmed === "openclaw:") continue;
|
|
2644
|
+
if (trimmed === "requires:") {
|
|
2645
|
+
inRequires = true;
|
|
2646
|
+
continue;
|
|
2647
|
+
}
|
|
2648
|
+
if (inRequires) {
|
|
2649
|
+
const reqKey = trimmed.match(/^(\w+):\s*(.*)$/);
|
|
2650
|
+
if (reqKey?.[1]) {
|
|
2651
|
+
requiresKey = reqKey[1];
|
|
2652
|
+
const inlineArr = reqKey[2]?.match(/\[(.*)\]/);
|
|
2653
|
+
if (inlineArr?.[1]) {
|
|
2654
|
+
const items = inlineArr[1].split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
2655
|
+
if (requiresKey === "bins") requires.bins = items;
|
|
2656
|
+
if (requiresKey === "env") requires.env = items;
|
|
2657
|
+
if (requiresKey === "config") requires.config = items;
|
|
2658
|
+
}
|
|
2659
|
+
continue;
|
|
2660
|
+
}
|
|
2661
|
+
const listItem = trimmed.match(/^-\s*["']?(.+?)["']?$/);
|
|
2662
|
+
if (listItem?.[1] && requiresKey) {
|
|
2663
|
+
if (requiresKey === "bins") {
|
|
2664
|
+
requires.bins = requires.bins ?? [];
|
|
2665
|
+
requires.bins.push(listItem[1]);
|
|
2666
|
+
}
|
|
2667
|
+
if (requiresKey === "env") {
|
|
2668
|
+
requires.env = requires.env ?? [];
|
|
2669
|
+
requires.env.push(listItem[1]);
|
|
2670
|
+
}
|
|
2671
|
+
if (requiresKey === "config") {
|
|
2672
|
+
requires.config = requires.config ?? [];
|
|
2673
|
+
requires.config.push(listItem[1]);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
if (Object.keys(requires).length > 0) {
|
|
2679
|
+
result.metadata = { openclaw: { requires } };
|
|
2680
|
+
}
|
|
2681
|
+
return result;
|
|
2682
|
+
}
|
|
2683
|
+
function loadSkillManifest(skillDir) {
|
|
2684
|
+
const skillMdPath = join5(skillDir, "SKILL.md");
|
|
2685
|
+
if (!existsSync9(skillMdPath)) {
|
|
2686
|
+
return null;
|
|
2687
|
+
}
|
|
2688
|
+
const content = readFileSync2(skillMdPath, "utf-8");
|
|
2689
|
+
const frontmatter = parseFrontmatter(content);
|
|
2690
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
2691
|
+
return null;
|
|
2692
|
+
}
|
|
2693
|
+
let requirements = null;
|
|
2694
|
+
const req = frontmatter.metadata?.openclaw?.requires;
|
|
2695
|
+
if (req) {
|
|
2696
|
+
requirements = {
|
|
2697
|
+
bins: req.bins ?? [],
|
|
2698
|
+
env: req.env ?? [],
|
|
2699
|
+
config: req.config ?? []
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2702
|
+
return {
|
|
2703
|
+
name: frontmatter.name,
|
|
2704
|
+
description: frontmatter.description,
|
|
2705
|
+
path: skillDir,
|
|
2706
|
+
requirements
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
function discoverSkills(skillsDir) {
|
|
2710
|
+
if (!existsSync9(skillsDir)) {
|
|
2711
|
+
return [];
|
|
2712
|
+
}
|
|
2713
|
+
const entries = readdirSync(skillsDir);
|
|
2714
|
+
const manifests = [];
|
|
2715
|
+
for (const entry of entries) {
|
|
2716
|
+
const fullPath = join5(skillsDir, entry);
|
|
2717
|
+
if (!statSync(fullPath).isDirectory()) continue;
|
|
2718
|
+
const manifest = loadSkillManifest(fullPath);
|
|
2719
|
+
if (manifest) {
|
|
2720
|
+
manifests.push(manifest);
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
return manifests.sort((a, b) => a.name.localeCompare(b.name));
|
|
2724
|
+
}
|
|
2725
|
+
function getActiveSkills(allSkills, profile) {
|
|
2726
|
+
const profileSkillSet = new Set(profile.skills);
|
|
2727
|
+
return allSkills.filter((skill) => profileSkillSet.has(skill.name));
|
|
2728
|
+
}
|
|
2729
|
+
function listSkillsWithStatus(allSkills, profile) {
|
|
2730
|
+
const profileSkillSet = new Set(profile.skills);
|
|
2731
|
+
return allSkills.map((skill) => ({
|
|
2732
|
+
skill,
|
|
2733
|
+
active: profileSkillSet.has(skill.name)
|
|
2734
|
+
}));
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
// src/skills/executor.ts
|
|
2738
|
+
var SkillExecutor = class {
|
|
2739
|
+
constructor(skillsDir, memory) {
|
|
2740
|
+
this.skillsDir = skillsDir;
|
|
2741
|
+
this.memory = memory;
|
|
2742
|
+
}
|
|
2743
|
+
skillsDir;
|
|
2744
|
+
memory;
|
|
2745
|
+
async execute(skillName, context) {
|
|
2746
|
+
const start = Date.now();
|
|
2747
|
+
try {
|
|
2748
|
+
const result = {
|
|
2749
|
+
success: true,
|
|
2750
|
+
output: `Skill ${skillName} loaded from ${this.skillsDir}`,
|
|
2751
|
+
durationMs: Date.now() - start
|
|
2752
|
+
};
|
|
2753
|
+
await this.memory.update(skillName, {
|
|
2754
|
+
succeeded: true,
|
|
2755
|
+
durationMs: result.durationMs,
|
|
2756
|
+
taskId: context.taskId
|
|
2757
|
+
});
|
|
2758
|
+
return result;
|
|
2759
|
+
} catch (err) {
|
|
2760
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
2761
|
+
await this.memory.update(skillName, {
|
|
2762
|
+
succeeded: false,
|
|
2763
|
+
durationMs: Date.now() - start,
|
|
2764
|
+
taskId: context.taskId
|
|
2765
|
+
});
|
|
2766
|
+
return {
|
|
2767
|
+
success: false,
|
|
2768
|
+
output: "",
|
|
2769
|
+
durationMs: Date.now() - start,
|
|
2770
|
+
error: errorMessage
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
};
|
|
2775
|
+
|
|
2776
|
+
// src/wallet/manager.ts
|
|
2777
|
+
import { readdir as readdir2, readFile as readFile8 } from "fs/promises";
|
|
2778
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2779
|
+
import { join as join7 } from "path";
|
|
2780
|
+
|
|
2781
|
+
// src/wallet/crypto.ts
|
|
2782
|
+
import { randomBytes as randomBytes2, createCipheriv, createDecipheriv, scryptSync } from "crypto";
|
|
2783
|
+
import { debuglog } from "util";
|
|
2784
|
+
import { writeFile as writeFile4, readFile as readFile7, mkdir as mkdir7 } from "fs/promises";
|
|
2785
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2786
|
+
import { join as join6 } from "path";
|
|
2787
|
+
var dlog = debuglog("clawpowers:wallet");
|
|
2788
|
+
var SCRYPT_N = 16384;
|
|
2789
|
+
var SCRYPT_R = 8;
|
|
2790
|
+
var SCRYPT_P = 1;
|
|
2791
|
+
var KEY_LENGTH = 32;
|
|
2792
|
+
var IV_LENGTH = 12;
|
|
2793
|
+
var AUTH_TAG_LENGTH = 16;
|
|
2794
|
+
function addressFromKeyMaterial(keyMaterial) {
|
|
2795
|
+
const digestHex = digestForWalletAddress(keyMaterial);
|
|
2796
|
+
const hash = Buffer.from(digestHex.replace(/^0x/, ""), "hex");
|
|
2797
|
+
return "0x" + hash.subarray(hash.length - 20).toString("hex");
|
|
2798
|
+
}
|
|
2799
|
+
function deriveKey(passphrase, salt) {
|
|
2800
|
+
return scryptSync(passphrase, salt, KEY_LENGTH, {
|
|
2801
|
+
N: SCRYPT_N,
|
|
2802
|
+
r: SCRYPT_R,
|
|
2803
|
+
p: SCRYPT_P
|
|
2804
|
+
});
|
|
2805
|
+
}
|
|
2806
|
+
function encryptPrivateKey(privateKey, passphrase) {
|
|
2807
|
+
const salt = randomBytes2(32);
|
|
2808
|
+
const key = deriveKey(passphrase, salt);
|
|
2809
|
+
const iv = randomBytes2(IV_LENGTH);
|
|
2810
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv, { authTagLength: AUTH_TAG_LENGTH });
|
|
2811
|
+
const encrypted = Buffer.concat([cipher.update(privateKey), cipher.final()]);
|
|
2812
|
+
const authTag = cipher.getAuthTag();
|
|
2813
|
+
return {
|
|
2814
|
+
ciphertext: encrypted.toString("hex"),
|
|
2815
|
+
iv: iv.toString("hex"),
|
|
2816
|
+
authTag: authTag.toString("hex"),
|
|
2817
|
+
salt: salt.toString("hex")
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
function decryptPrivateKey(ciphertext, iv, authTag, salt, passphrase) {
|
|
2821
|
+
const key = deriveKey(passphrase, Buffer.from(salt, "hex"));
|
|
2822
|
+
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "hex"), {
|
|
2823
|
+
authTagLength: AUTH_TAG_LENGTH
|
|
2824
|
+
});
|
|
2825
|
+
decipher.setAuthTag(Buffer.from(authTag, "hex"));
|
|
2826
|
+
const decrypted = Buffer.concat([
|
|
2827
|
+
decipher.update(Buffer.from(ciphertext, "hex")),
|
|
2828
|
+
decipher.final()
|
|
2829
|
+
]);
|
|
2830
|
+
return decrypted;
|
|
2831
|
+
}
|
|
2832
|
+
function generateAddress(privateKeyHex) {
|
|
2833
|
+
const cleaned = privateKeyHex.replace(/^0x/i, "");
|
|
2834
|
+
const privBuf = Buffer.from(cleaned, "hex");
|
|
2835
|
+
const eth = deriveEthereumAddress(privBuf);
|
|
2836
|
+
if (eth) {
|
|
2837
|
+
dlog("address derivation: secp256k1+keccak (tier %s)", getActiveTier());
|
|
2838
|
+
return eth;
|
|
2839
|
+
}
|
|
2840
|
+
dlog("address derivation: tier3 legacy digest (tier %s)", getActiveTier());
|
|
2841
|
+
return addressFromKeyMaterial(privBuf);
|
|
2842
|
+
}
|
|
2843
|
+
async function ensureDir(dir) {
|
|
2844
|
+
if (!existsSync10(dir)) {
|
|
2845
|
+
await mkdir7(dir, { recursive: true });
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
async function signMessageFromKeyFile(message, keyFile, passphrase) {
|
|
2849
|
+
const content = await readFile7(keyFile, "utf-8");
|
|
2850
|
+
const keyFileData = JSON.parse(content);
|
|
2851
|
+
const privateKey = decryptPrivateKey(
|
|
2852
|
+
keyFileData.crypto.ciphertext,
|
|
2853
|
+
keyFileData.crypto.iv,
|
|
2854
|
+
keyFileData.crypto.authTag,
|
|
2855
|
+
keyFileData.crypto.salt,
|
|
2856
|
+
passphrase
|
|
2857
|
+
);
|
|
2858
|
+
const msgBuf = Buffer.from(message, "utf8");
|
|
2859
|
+
const hash = keccak256Digest(msgBuf);
|
|
2860
|
+
const sigEcdsa = hash ? signEcdsa(privateKey, hash) : null;
|
|
2861
|
+
if (sigEcdsa) {
|
|
2862
|
+
dlog("signMessage (keyfile): secp256k1 ECDSA (tier %s)", getActiveTier());
|
|
2863
|
+
return {
|
|
2864
|
+
message,
|
|
2865
|
+
signature: "0x" + sigEcdsa.toString("hex"),
|
|
2866
|
+
address: keyFileData.address
|
|
2867
|
+
};
|
|
2868
|
+
}
|
|
2869
|
+
const { createHmac } = await import("crypto");
|
|
2870
|
+
dlog("signMessage (keyfile): HMAC-SHA256 legacy (no secp256k1/keccak tier)");
|
|
2871
|
+
const signature = createHmac("sha256", privateKey).update(message).digest("hex");
|
|
2872
|
+
return {
|
|
2873
|
+
message,
|
|
2874
|
+
signature: "0x" + signature,
|
|
2875
|
+
address: keyFileData.address
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
async function signMessageFromPrivateKey(privateKeyHex, message) {
|
|
2879
|
+
const cleaned = privateKeyHex.replace(/^0x/i, "");
|
|
2880
|
+
if (cleaned.length !== 64 || !/^[0-9a-fA-F]+$/.test(cleaned)) {
|
|
2881
|
+
throw new Error("Invalid private key: must be 32 bytes (64 hex characters)");
|
|
2882
|
+
}
|
|
2883
|
+
const priv = Buffer.from(cleaned, "hex");
|
|
2884
|
+
const hash = keccak256Digest(Buffer.from(message, "utf8"));
|
|
2885
|
+
if (!hash) {
|
|
2886
|
+
throw new Error(
|
|
2887
|
+
"Ethereum signing requires Keccak-256 (Tier 1 native or Tier 2 WASM). Pure TypeScript tier has no Keccak."
|
|
2888
|
+
);
|
|
2889
|
+
}
|
|
2890
|
+
const sig = signEcdsa(priv, hash);
|
|
2891
|
+
if (!sig) {
|
|
2892
|
+
throw new Error("Ethereum signing requires secp256k1 (Tier 1 native or Tier 2 WASM).");
|
|
2893
|
+
}
|
|
2894
|
+
return "0x" + sig.toString("hex");
|
|
2895
|
+
}
|
|
2896
|
+
async function generateWallet(config) {
|
|
2897
|
+
const privateKey = randomBytes2(32);
|
|
2898
|
+
const privateKeyHex = privateKey.toString("hex");
|
|
2899
|
+
const address = generateAddress(privateKeyHex);
|
|
2900
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2901
|
+
const passphrase = randomBytes2(16).toString("hex");
|
|
2902
|
+
const encrypted = encryptPrivateKey(privateKey, passphrase);
|
|
2903
|
+
const keyFileData = {
|
|
2904
|
+
version: 1,
|
|
2905
|
+
address,
|
|
2906
|
+
chain: config.chain,
|
|
2907
|
+
createdAt,
|
|
2908
|
+
crypto: {
|
|
2909
|
+
cipher: "aes-256-gcm",
|
|
2910
|
+
ciphertext: encrypted.ciphertext,
|
|
2911
|
+
iv: encrypted.iv,
|
|
2912
|
+
authTag: encrypted.authTag,
|
|
2913
|
+
salt: encrypted.salt,
|
|
2914
|
+
scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
|
|
2915
|
+
}
|
|
2916
|
+
};
|
|
2917
|
+
await ensureDir(config.dataDir);
|
|
2918
|
+
const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
|
|
2919
|
+
const keyFilePath = join6(config.dataDir, keyFileName);
|
|
2920
|
+
await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
|
|
2921
|
+
return {
|
|
2922
|
+
address,
|
|
2923
|
+
chain: config.chain,
|
|
2924
|
+
createdAt,
|
|
2925
|
+
keyFile: keyFilePath
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
async function importWallet(privateKeyHex, config) {
|
|
2929
|
+
const cleaned = privateKeyHex.replace(/^0x/i, "");
|
|
2930
|
+
if (cleaned.length !== 64 || !/^[0-9a-fA-F]+$/.test(cleaned)) {
|
|
2931
|
+
throw new Error("Invalid private key: must be 32 bytes (64 hex characters)");
|
|
2932
|
+
}
|
|
2933
|
+
const privateKey = Buffer.from(cleaned, "hex");
|
|
2934
|
+
const address = generateAddress(cleaned);
|
|
2935
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2936
|
+
const passphrase = randomBytes2(16).toString("hex");
|
|
2937
|
+
const encrypted = encryptPrivateKey(privateKey, passphrase);
|
|
2938
|
+
const keyFileData = {
|
|
2939
|
+
version: 1,
|
|
2940
|
+
address,
|
|
2941
|
+
chain: config.chain,
|
|
2942
|
+
createdAt,
|
|
2943
|
+
crypto: {
|
|
2944
|
+
cipher: "aes-256-gcm",
|
|
2945
|
+
ciphertext: encrypted.ciphertext,
|
|
2946
|
+
iv: encrypted.iv,
|
|
2947
|
+
authTag: encrypted.authTag,
|
|
2948
|
+
salt: encrypted.salt,
|
|
2949
|
+
scryptParams: { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
await ensureDir(config.dataDir);
|
|
2953
|
+
const keyFileName = `${address.slice(2, 10)}-${Date.now()}.json`;
|
|
2954
|
+
const keyFilePath = join6(config.dataDir, keyFileName);
|
|
2955
|
+
await writeFile4(keyFilePath, JSON.stringify(keyFileData, null, 2) + "\n", "utf-8");
|
|
2956
|
+
return {
|
|
2957
|
+
address,
|
|
2958
|
+
chain: config.chain,
|
|
2959
|
+
createdAt,
|
|
2960
|
+
keyFile: keyFilePath
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
async function signMessage(a, b, c) {
|
|
2964
|
+
if (c !== void 0) {
|
|
2965
|
+
return signMessageFromKeyFile(a, b, c);
|
|
2966
|
+
}
|
|
2967
|
+
return signMessageFromPrivateKey(a, b);
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// src/wallet/manager.ts
|
|
2971
|
+
var WalletManager = class {
|
|
2972
|
+
constructor(config) {
|
|
2973
|
+
this.config = config;
|
|
2974
|
+
}
|
|
2975
|
+
config;
|
|
2976
|
+
async generate() {
|
|
2977
|
+
return generateWallet(this.config);
|
|
2978
|
+
}
|
|
2979
|
+
async import(privateKey) {
|
|
2980
|
+
return importWallet(privateKey, this.config);
|
|
2981
|
+
}
|
|
2982
|
+
async sign(message, walletInfo, passphrase) {
|
|
2983
|
+
const result = await signMessage(message, walletInfo.keyFile, passphrase);
|
|
2984
|
+
return result.signature;
|
|
2985
|
+
}
|
|
2986
|
+
async listWallets() {
|
|
2987
|
+
if (!existsSync11(this.config.dataDir)) {
|
|
2988
|
+
return [];
|
|
2989
|
+
}
|
|
2990
|
+
const files = await readdir2(this.config.dataDir);
|
|
2991
|
+
const wallets = [];
|
|
2992
|
+
for (const file of files) {
|
|
2993
|
+
if (!file.endsWith(".json")) continue;
|
|
2994
|
+
try {
|
|
2995
|
+
const filePath = join7(this.config.dataDir, file);
|
|
2996
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2997
|
+
const data = JSON.parse(content);
|
|
2998
|
+
wallets.push({
|
|
2999
|
+
address: data.address,
|
|
3000
|
+
chain: data.chain,
|
|
3001
|
+
createdAt: data.createdAt,
|
|
3002
|
+
keyFile: filePath
|
|
3003
|
+
});
|
|
3004
|
+
} catch {
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
return wallets;
|
|
3008
|
+
}
|
|
3009
|
+
};
|
|
3010
|
+
|
|
3011
|
+
// src/itp/index.ts
|
|
3012
|
+
var ITP_BASE_URL = "http://localhost:8100";
|
|
3013
|
+
var TIMEOUT_MS = 3e3;
|
|
3014
|
+
async function encode(message, sourceAgent) {
|
|
3015
|
+
try {
|
|
3016
|
+
const controller = new AbortController();
|
|
3017
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
3018
|
+
const response = await fetch(`${ITP_BASE_URL}/tools/encode`, {
|
|
3019
|
+
method: "POST",
|
|
3020
|
+
headers: { "Content-Type": "application/json" },
|
|
3021
|
+
body: JSON.stringify({
|
|
3022
|
+
message,
|
|
3023
|
+
source_agent: sourceAgent ?? "unknown",
|
|
3024
|
+
target_agent: "unknown"
|
|
3025
|
+
}),
|
|
3026
|
+
signal: controller.signal
|
|
3027
|
+
});
|
|
3028
|
+
clearTimeout(timeout);
|
|
3029
|
+
if (!response.ok) {
|
|
3030
|
+
return { encoded: message, wasCompressed: false, savingsPct: 0 };
|
|
3031
|
+
}
|
|
3032
|
+
const data = await response.json();
|
|
3033
|
+
return {
|
|
3034
|
+
encoded: data.encoded ?? message,
|
|
3035
|
+
wasCompressed: Boolean(data.was_compressed),
|
|
3036
|
+
savingsPct: typeof data.savings_pct === "number" ? data.savings_pct : 0
|
|
3037
|
+
};
|
|
3038
|
+
} catch {
|
|
3039
|
+
return { encoded: message, wasCompressed: false, savingsPct: 0 };
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
async function decode(message) {
|
|
3043
|
+
try {
|
|
3044
|
+
const controller = new AbortController();
|
|
3045
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
3046
|
+
const response = await fetch(`${ITP_BASE_URL}/tools/decode`, {
|
|
3047
|
+
method: "POST",
|
|
3048
|
+
headers: { "Content-Type": "application/json" },
|
|
3049
|
+
body: JSON.stringify({ message }),
|
|
3050
|
+
signal: controller.signal
|
|
3051
|
+
});
|
|
3052
|
+
clearTimeout(timeout);
|
|
3053
|
+
if (!response.ok) {
|
|
3054
|
+
return { decoded: message, wasItp: false };
|
|
3055
|
+
}
|
|
3056
|
+
const data = await response.json();
|
|
3057
|
+
return {
|
|
3058
|
+
decoded: data.decoded ?? message,
|
|
3059
|
+
wasItp: Boolean(data.was_itp)
|
|
3060
|
+
};
|
|
3061
|
+
} catch {
|
|
3062
|
+
return { decoded: message, wasItp: false };
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
async function healthCheck() {
|
|
3066
|
+
try {
|
|
3067
|
+
const controller = new AbortController();
|
|
3068
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
3069
|
+
const response = await fetch(`${ITP_BASE_URL}/health`, {
|
|
3070
|
+
signal: controller.signal
|
|
3071
|
+
});
|
|
3072
|
+
clearTimeout(timeout);
|
|
3073
|
+
return response.ok;
|
|
3074
|
+
} catch {
|
|
3075
|
+
return false;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
// src/itp/swarm-bridge.ts
|
|
3080
|
+
async function encodeTaskDescription(task) {
|
|
3081
|
+
try {
|
|
3082
|
+
const [descResult, msgResult] = await Promise.all([
|
|
3083
|
+
encode(task.description, "swarm-orchestrator"),
|
|
3084
|
+
encode(task.message, "swarm-orchestrator")
|
|
3085
|
+
]);
|
|
3086
|
+
return {
|
|
3087
|
+
...task,
|
|
3088
|
+
description: descResult.encoded,
|
|
3089
|
+
message: msgResult.encoded
|
|
3090
|
+
};
|
|
3091
|
+
} catch {
|
|
3092
|
+
return task;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
async function decodeSwarmResult(result) {
|
|
3096
|
+
try {
|
|
3097
|
+
if (result.result === null) {
|
|
3098
|
+
return result;
|
|
3099
|
+
}
|
|
3100
|
+
const decoded = await decode(result.result);
|
|
3101
|
+
return {
|
|
3102
|
+
...result,
|
|
3103
|
+
result: decoded.decoded
|
|
3104
|
+
};
|
|
3105
|
+
} catch {
|
|
3106
|
+
return result;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
// src/swarm/concurrency.ts
|
|
3111
|
+
var ConcurrencyManager = class {
|
|
3112
|
+
maxConcurrency;
|
|
3113
|
+
backpressureThreshold;
|
|
3114
|
+
activeCount = 0;
|
|
3115
|
+
throttleDelayMs = 0;
|
|
3116
|
+
lastErrorTime = 0;
|
|
3117
|
+
// Semaphore queue — each entry is a resolver that grants a slot
|
|
3118
|
+
queue = [];
|
|
3119
|
+
constructor(maxConcurrency = 5, backpressureThreshold = 0.8) {
|
|
3120
|
+
this.maxConcurrency = maxConcurrency;
|
|
3121
|
+
this.backpressureThreshold = backpressureThreshold;
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Acquire a concurrency slot. Resolves when a slot is available.
|
|
3125
|
+
* Applies throttle delay if rate limits have been hit recently.
|
|
3126
|
+
*/
|
|
3127
|
+
async acquire() {
|
|
3128
|
+
if (this.activeCount < this.maxConcurrency) {
|
|
3129
|
+
this.activeCount++;
|
|
3130
|
+
} else {
|
|
3131
|
+
await new Promise((resolve) => {
|
|
3132
|
+
this.queue.push(resolve);
|
|
3133
|
+
});
|
|
3134
|
+
this.activeCount++;
|
|
3135
|
+
}
|
|
3136
|
+
if (this.throttleDelayMs > 0) {
|
|
3137
|
+
await new Promise((resolve) => setTimeout(resolve, this.throttleDelayMs));
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* Release a concurrency slot. Unblocks next queued waiter if any.
|
|
3142
|
+
*/
|
|
3143
|
+
release() {
|
|
3144
|
+
this.activeCount = Math.max(0, this.activeCount - 1);
|
|
3145
|
+
const next = this.queue.shift();
|
|
3146
|
+
if (next) next();
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Number of currently active tasks.
|
|
3150
|
+
*/
|
|
3151
|
+
get active() {
|
|
3152
|
+
return this.activeCount;
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* Number of tasks waiting for a slot.
|
|
3156
|
+
*/
|
|
3157
|
+
get pending() {
|
|
3158
|
+
return this.queue.length;
|
|
3159
|
+
}
|
|
3160
|
+
/**
|
|
3161
|
+
* Whether system has capacity for another task.
|
|
3162
|
+
*/
|
|
3163
|
+
hasCapacity() {
|
|
3164
|
+
return this.activeCount < this.maxConcurrency;
|
|
3165
|
+
}
|
|
3166
|
+
/**
|
|
3167
|
+
* Increase throttle delay on rate-limit / transient errors.
|
|
3168
|
+
* Multiple errors in quick succession cause exponential backoff.
|
|
3169
|
+
*/
|
|
3170
|
+
adaptiveThrottle(_errorType = "rate_limit") {
|
|
3171
|
+
const now = Date.now();
|
|
3172
|
+
if (now - this.lastErrorTime < 5e3) {
|
|
3173
|
+
this.throttleDelayMs = Math.min(this.throttleDelayMs * 2 + 500, 3e4);
|
|
3174
|
+
} else {
|
|
3175
|
+
this.throttleDelayMs = 500;
|
|
3176
|
+
}
|
|
3177
|
+
this.lastErrorTime = now;
|
|
3178
|
+
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Gradually reduce throttle delay after a successful operation.
|
|
3181
|
+
*/
|
|
3182
|
+
resetThrottle() {
|
|
3183
|
+
this.throttleDelayMs = Math.max(0, this.throttleDelayMs - 100);
|
|
3184
|
+
}
|
|
3185
|
+
/**
|
|
3186
|
+
* Check if system is above backpressure threshold.
|
|
3187
|
+
*/
|
|
3188
|
+
isUnderPressure() {
|
|
3189
|
+
return this.activeCount / this.maxConcurrency >= this.backpressureThreshold;
|
|
3190
|
+
}
|
|
3191
|
+
};
|
|
3192
|
+
|
|
3193
|
+
// src/swarm/token_pool.ts
|
|
3194
|
+
var TokenPool = class {
|
|
3195
|
+
totalBudget;
|
|
3196
|
+
perTaskDefault;
|
|
3197
|
+
allocations = /* @__PURE__ */ new Map();
|
|
3198
|
+
constructor(totalBudget = 1e5, perTaskDefault = 2e4) {
|
|
3199
|
+
this.totalBudget = totalBudget;
|
|
3200
|
+
this.perTaskDefault = perTaskDefault;
|
|
3201
|
+
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Reserve tokens for a task.
|
|
3204
|
+
* Returns false if the pool doesn't have enough remaining budget.
|
|
3205
|
+
*/
|
|
3206
|
+
allocate(taskId, budget) {
|
|
3207
|
+
const requested = budget ?? this.perTaskDefault;
|
|
3208
|
+
const currentAllocated = this.totalAllocated();
|
|
3209
|
+
if (currentAllocated + requested > this.totalBudget) {
|
|
3210
|
+
return false;
|
|
3211
|
+
}
|
|
3212
|
+
this.allocations.set(taskId, {
|
|
3213
|
+
task_id: taskId,
|
|
3214
|
+
budget: requested,
|
|
3215
|
+
consumed: 0,
|
|
3216
|
+
allocated_at: Date.now()
|
|
3217
|
+
});
|
|
3218
|
+
return true;
|
|
3219
|
+
}
|
|
3220
|
+
/**
|
|
3221
|
+
* Record actual token usage for a task (cumulative).
|
|
3222
|
+
*/
|
|
3223
|
+
consume(taskId, tokens) {
|
|
3224
|
+
const alloc = this.allocations.get(taskId);
|
|
3225
|
+
if (alloc) {
|
|
3226
|
+
alloc.consumed += tokens;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Free the allocation when a task completes.
|
|
3231
|
+
* Returns tokens consumed by that task.
|
|
3232
|
+
*/
|
|
3233
|
+
release(taskId) {
|
|
3234
|
+
const alloc = this.allocations.get(taskId);
|
|
3235
|
+
this.allocations.delete(taskId);
|
|
3236
|
+
return alloc?.consumed ?? 0;
|
|
3237
|
+
}
|
|
3238
|
+
/**
|
|
3239
|
+
* Total remaining budget (total - allocated).
|
|
3240
|
+
*/
|
|
3241
|
+
remaining() {
|
|
3242
|
+
return this.totalBudget - this.totalAllocated();
|
|
3243
|
+
}
|
|
3244
|
+
/**
|
|
3245
|
+
* Total tokens consumed across all active tasks.
|
|
3246
|
+
*/
|
|
3247
|
+
consumed() {
|
|
3248
|
+
let total = 0;
|
|
3249
|
+
for (const alloc of this.allocations.values()) {
|
|
3250
|
+
total += alloc.consumed;
|
|
3251
|
+
}
|
|
3252
|
+
return total;
|
|
3253
|
+
}
|
|
3254
|
+
/**
|
|
3255
|
+
* Total tokens currently allocated (reserved but not necessarily consumed).
|
|
3256
|
+
*/
|
|
3257
|
+
totalAllocated() {
|
|
3258
|
+
let total = 0;
|
|
3259
|
+
for (const alloc of this.allocations.values()) {
|
|
3260
|
+
total += alloc.budget;
|
|
3261
|
+
}
|
|
3262
|
+
return total;
|
|
3263
|
+
}
|
|
3264
|
+
/**
|
|
3265
|
+
* Check if a specific task has exceeded its allocation.
|
|
3266
|
+
*/
|
|
3267
|
+
isTaskOverBudget(taskId) {
|
|
3268
|
+
const alloc = this.allocations.get(taskId);
|
|
3269
|
+
if (!alloc) return false;
|
|
3270
|
+
return alloc.consumed >= alloc.budget;
|
|
3271
|
+
}
|
|
3272
|
+
/**
|
|
3273
|
+
* Remaining budget for a specific task.
|
|
3274
|
+
*/
|
|
3275
|
+
taskBudgetRemaining(taskId) {
|
|
3276
|
+
const alloc = this.allocations.get(taskId);
|
|
3277
|
+
if (!alloc) return 0;
|
|
3278
|
+
return Math.max(0, alloc.budget - alloc.consumed);
|
|
3279
|
+
}
|
|
3280
|
+
/**
|
|
3281
|
+
* Per-task token consumption summary for observability.
|
|
3282
|
+
*/
|
|
3283
|
+
usageReport() {
|
|
3284
|
+
const tasks = {};
|
|
3285
|
+
let totalConsumed = 0;
|
|
3286
|
+
let totalAllocated = 0;
|
|
3287
|
+
for (const [taskId, alloc] of this.allocations.entries()) {
|
|
3288
|
+
tasks[taskId] = {
|
|
3289
|
+
budget: alloc.budget,
|
|
3290
|
+
consumed: alloc.consumed,
|
|
3291
|
+
remaining: Math.max(0, alloc.budget - alloc.consumed),
|
|
3292
|
+
over_budget: alloc.consumed >= alloc.budget
|
|
3293
|
+
};
|
|
3294
|
+
totalConsumed += alloc.consumed;
|
|
3295
|
+
totalAllocated += alloc.budget;
|
|
3296
|
+
}
|
|
3297
|
+
return {
|
|
3298
|
+
total_budget: this.totalBudget,
|
|
3299
|
+
total_allocated: totalAllocated,
|
|
3300
|
+
total_consumed: totalConsumed,
|
|
3301
|
+
total_remaining: this.totalBudget - totalAllocated,
|
|
3302
|
+
tasks
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
/**
|
|
3306
|
+
* Clear all allocations (reset between runs).
|
|
3307
|
+
*/
|
|
3308
|
+
reset() {
|
|
3309
|
+
this.allocations.clear();
|
|
3310
|
+
}
|
|
3311
|
+
};
|
|
3312
|
+
|
|
3313
|
+
// src/swarm/model_router.ts
|
|
3314
|
+
var DEFAULT_MODELS = {
|
|
3315
|
+
simple: "claude-3-haiku-20240307",
|
|
3316
|
+
moderate: "claude-3-5-sonnet-20241022",
|
|
3317
|
+
complex: "claude-opus-4-5"
|
|
3318
|
+
};
|
|
3319
|
+
var COMPLEX_KEYWORDS = [
|
|
3320
|
+
"architect",
|
|
3321
|
+
"design",
|
|
3322
|
+
"refactor",
|
|
3323
|
+
"debug complex",
|
|
3324
|
+
"optimize",
|
|
3325
|
+
"cross-domain",
|
|
3326
|
+
"integrate multiple",
|
|
3327
|
+
"security audit",
|
|
3328
|
+
"performance",
|
|
3329
|
+
"distributed",
|
|
3330
|
+
"concurrent requests",
|
|
3331
|
+
"migration",
|
|
3332
|
+
"system design",
|
|
3333
|
+
"multi-step",
|
|
3334
|
+
"synthesize",
|
|
3335
|
+
"reasoning",
|
|
3336
|
+
"trade-off"
|
|
3337
|
+
];
|
|
3338
|
+
var SIMPLE_KEYWORDS = [
|
|
3339
|
+
"format",
|
|
3340
|
+
"list",
|
|
3341
|
+
"count",
|
|
3342
|
+
"lookup",
|
|
3343
|
+
"translate",
|
|
3344
|
+
"summarize briefly",
|
|
3345
|
+
"extract",
|
|
3346
|
+
"convert",
|
|
3347
|
+
"rename",
|
|
3348
|
+
"simple",
|
|
3349
|
+
"trivial",
|
|
3350
|
+
"basic",
|
|
3351
|
+
"fetch",
|
|
3352
|
+
"get",
|
|
3353
|
+
"retrieve",
|
|
3354
|
+
"find the"
|
|
3355
|
+
];
|
|
3356
|
+
function classifyHeuristic(description) {
|
|
3357
|
+
const lower = description.toLowerCase();
|
|
3358
|
+
for (const kw of COMPLEX_KEYWORDS) {
|
|
3359
|
+
if (lower.includes(kw)) return "complex";
|
|
3360
|
+
}
|
|
3361
|
+
for (const kw of SIMPLE_KEYWORDS) {
|
|
3362
|
+
if (lower.includes(kw)) return "simple";
|
|
3363
|
+
}
|
|
3364
|
+
const len = description.length;
|
|
3365
|
+
if (len < 100) return "simple";
|
|
3366
|
+
if (len > 500) return "complex";
|
|
3367
|
+
return "moderate";
|
|
3368
|
+
}
|
|
3369
|
+
function selectModel(complexity, config) {
|
|
3370
|
+
const overrides = config?.models ?? {};
|
|
3371
|
+
return overrides[complexity] ?? DEFAULT_MODELS[complexity];
|
|
3372
|
+
}
|
|
3373
|
+
function classifyTasks(tasks) {
|
|
3374
|
+
const result = /* @__PURE__ */ new Map();
|
|
3375
|
+
for (const task of tasks) {
|
|
3376
|
+
result.set(task.id, task.complexity ?? classifyHeuristic(task.description));
|
|
3377
|
+
}
|
|
3378
|
+
return result;
|
|
3379
|
+
}
|
|
3380
|
+
export {
|
|
3381
|
+
ABTestManager,
|
|
3382
|
+
AutoResearcher,
|
|
3383
|
+
CLAWPOWERS_HOME,
|
|
3384
|
+
CheckpointManager,
|
|
3385
|
+
ConcurrencyManager,
|
|
3386
|
+
ContextInjector,
|
|
3387
|
+
DEFAULT_CONFIG,
|
|
3388
|
+
EpisodicMemory,
|
|
3389
|
+
HypothesisEngine,
|
|
3390
|
+
MetricsCollector,
|
|
3391
|
+
MutationEngine,
|
|
3392
|
+
PACKAGE_NAME,
|
|
3393
|
+
PaymentExecutor,
|
|
3394
|
+
ProceduralMemory,
|
|
3395
|
+
RSIAuditLog,
|
|
3396
|
+
SkillExecutor,
|
|
3397
|
+
SpendingPolicy,
|
|
3398
|
+
TokenPool,
|
|
3399
|
+
VERSION,
|
|
3400
|
+
WalletManager,
|
|
3401
|
+
WorkingMemoryManager,
|
|
3402
|
+
approximateDistance,
|
|
3403
|
+
calculateFee,
|
|
3404
|
+
calculateTransactionFee,
|
|
3405
|
+
classifyHeuristic,
|
|
3406
|
+
classifyTasks,
|
|
3407
|
+
compressVector,
|
|
3408
|
+
computeSha256,
|
|
3409
|
+
createPaymentHeader,
|
|
3410
|
+
decodeSwarmResult,
|
|
3411
|
+
decompressVector,
|
|
3412
|
+
deriveEthereumAddress,
|
|
3413
|
+
derivePublicKey,
|
|
3414
|
+
detect402,
|
|
3415
|
+
digestForWalletAddress,
|
|
3416
|
+
discoverSkills,
|
|
3417
|
+
encodeTaskDescription,
|
|
3418
|
+
evaluateWriteFirewall,
|
|
3419
|
+
evaluateWriteSecurity,
|
|
3420
|
+
generateWallet,
|
|
3421
|
+
generateWalletAddress,
|
|
3422
|
+
getActiveSkills,
|
|
3423
|
+
getActiveTier,
|
|
3424
|
+
getBestCanonicalStore,
|
|
3425
|
+
getCapabilitySummary,
|
|
3426
|
+
getConfigValue,
|
|
3427
|
+
getNative,
|
|
3428
|
+
getNativeCanonicalStore,
|
|
3429
|
+
getNativeCanonicalStoreInMemory,
|
|
3430
|
+
getWasm,
|
|
3431
|
+
getWasmCanonicalStore,
|
|
3432
|
+
importWallet,
|
|
3433
|
+
initConfig,
|
|
3434
|
+
isNativeAvailable,
|
|
3435
|
+
isPaymentRequired,
|
|
3436
|
+
isWasmAvailable,
|
|
3437
|
+
decode as itpDecode,
|
|
3438
|
+
encode as itpEncode,
|
|
3439
|
+
healthCheck as itpHealthCheck,
|
|
3440
|
+
keccak256Digest,
|
|
3441
|
+
listSkillsWithStatus,
|
|
3442
|
+
loadConfig,
|
|
3443
|
+
loadConfigSafe,
|
|
3444
|
+
loadSkillManifest,
|
|
3445
|
+
parseFrontmatter,
|
|
3446
|
+
runAutoResearch,
|
|
3447
|
+
saveConfig,
|
|
3448
|
+
selectModel,
|
|
3449
|
+
setConfigValue,
|
|
3450
|
+
signEcdsa,
|
|
3451
|
+
signMessage,
|
|
3452
|
+
tokenAmountFromHuman,
|
|
3453
|
+
verifyEcdsa
|
|
3454
|
+
};
|
|
3455
|
+
/**
|
|
3456
|
+
* ClawPowers — Skills Library for AI Agents
|
|
3457
|
+
* Drop-in capability layer: payments, memory, RSI, wallet.
|
|
3458
|
+
* No agent control loop — bring your own agent.
|
|
3459
|
+
*
|
|
3460
|
+
* @version 2.2.0
|
|
3461
|
+
* @license BSL-1.1
|
|
3462
|
+
* @patent-pending
|
|
3463
|
+
*/
|
|
3464
|
+
//# sourceMappingURL=index.js.map
|