solana-traderclaw 1.0.40 → 1.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +67 -87
  2. package/bin/installer-step-engine.mjs +108 -5
  3. package/bin/openclaw-trader.mjs +60 -0
  4. package/config/gateway-v1-upgraded.json5 +160 -0
  5. package/config/gateway-v1.json5 +71 -57
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-AI6MTHUN.js +88 -0
  8. package/dist/chunk-CMZLPU3Z.js +164 -0
  9. package/dist/{chunk-A7UG5RGA.js → chunk-F3UKCPA4.js} +0 -9
  10. package/dist/chunk-IYD3TCSE.js +366 -0
  11. package/dist/chunk-NZTB624I.js +366 -0
  12. package/dist/chunk-Q5AWKXY3.js +97 -0
  13. package/dist/chunk-YBURTADE.js +169 -0
  14. package/dist/index.js +797 -531
  15. package/dist/src/intelligence-lab.js +7 -0
  16. package/dist/src/prompt-scrub.js +6 -0
  17. package/dist/src/runtime-layout.js +28 -0
  18. package/dist/src/tool-envelope.js +12 -0
  19. package/lib/x-client.mjs +2 -69
  20. package/lib/x-tools.mjs +19 -84
  21. package/openclaw.plugin.json +53 -2
  22. package/package.json +4 -4
  23. package/skills/solana-trader/HEARTBEAT.md +383 -41
  24. package/skills/solana-trader/SKILL.md +240 -2541
  25. package/skills/solana-trader/refs/alpha-signals.md +172 -0
  26. package/skills/solana-trader/refs/api-reference.md +158 -0
  27. package/skills/solana-trader/refs/bitquery-intelligence.md +75 -0
  28. package/skills/solana-trader/refs/cron-jobs.md +140 -0
  29. package/skills/solana-trader/refs/decision-framework.md +157 -0
  30. package/skills/solana-trader/refs/memory-tags.md +90 -0
  31. package/skills/solana-trader/refs/position-management.md +88 -0
  32. package/skills/solana-trader/refs/review-learning.md +117 -0
  33. package/skills/solana-trader/refs/strategy-evolution.md +160 -0
  34. package/skills/solana-trader/refs/trade-execution.md +148 -0
  35. package/skills/solana-trader/workspace/AGENTS.md +113 -0
  36. package/skills/solana-trader/workspace/BOOTSTRAP.md +22 -0
  37. package/skills/solana-trader/workspace/DEPLOYMENT.md +69 -0
  38. package/skills/solana-trader/workspace/IDENTITY.md +21 -0
  39. package/skills/solana-trader/workspace/MEMORY.md +6 -0
  40. package/skills/solana-trader/workspace/SOUL.md +46 -0
  41. package/skills/solana-trader/workspace/TOOLS.md +209 -0
  42. package/skills/solana-trader/workspace/USER.md +17 -0
  43. package/skills/solana-trader/refs/x-credentials.md +0 -169
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # solana-traderclaw (TraderClaw V1 — team features)
1
+ # solana-traderclaw (V1-Upgraded)
2
2
 
3
- Team edition of the TraderClaw V1 plugin for autonomous Solana memecoin trading. Full V1 trading capabilities plus 5 X/Twitter tools for journaling and community engagement. Connects OpenClaw to a trading orchestrator that handles market data, risk enforcement, and trade execution. Includes a full memory layer with local persistence, episodic logging, deterministic compute tools, and OpenClaw-native memory integration.
3
+ Public edition of the upgraded TraderClaw V1 plugin for autonomous Solana memecoin trading. Identical to the team edition with one difference: X/Twitter tools are read-only (social intel only, no posting). 94 Solana tools + 3 X/Twitter read tools = 97 total. Full trading lifecycle. Connects OpenClaw to a trading orchestrator that handles market data, risk enforcement, and trade execution. Includes a full memory layer with local persistence, episodic logging, deterministic compute tools, intelligence lab, standardized tool envelopes, prompt scrubbing, and OpenClaw-native memory integration.
4
4
 
5
5
  ## Architecture
6
6
 
7
7
  ```
8
8
  OpenClaw Agent (brain: reasoning, decisions, strategy evolution)
9
9
 
10
- │ calls 72 typed tools (67 trading + 5 X)
10
+ │ calls 97 typed tools (94 trading + 3 X read-only)
11
11
 
12
12
  Plugin (this package)
13
13
  ├── HTTP ──→ Orchestrator (data + risk + execution)
@@ -18,9 +18,8 @@ Plugin (this package)
18
18
  ├── Local persistence (state, decisions, bulletin, patterns)
19
19
  │ └── .traderclaw-v1-data/
20
20
 
21
- └── OpenClaw workspace (default ~/.openclaw/workspace/, auto-loaded every session)
22
- ├── STATE.md (machine-written durable state from solana_state_save)
23
- ├── MEMORY.md (optional — your narrative notes; not overwritten by state save)
21
+ └── OpenClaw native memory (auto-loaded every session)
22
+ ├── MEMORY.md (durable facts always in context)
24
23
  └── memory/YYYY-MM-DD.md (daily logs — today + yesterday)
25
24
  ```
26
25
 
@@ -35,19 +34,20 @@ The plugin gives OpenClaw tools to interact with the Solana trading orchestrator
35
34
 
36
35
  ### 1. Install the plugin
37
36
 
38
- Install the **npm package** **`solana-traderclaw`**. The **OpenClaw plugin id** in `openclaw.json` stays **`solana-trader`** (same as `openclaw.plugin.json`). The global CLI binary is **`traderclaw`**.
39
-
40
37
  ```bash
41
- npm install -g solana-traderclaw@1.0.20
38
+ npm install -g solana-traderclaw
42
39
  ```
43
40
 
44
41
  Or install directly into OpenClaw:
45
42
 
46
43
  ```bash
47
- openclaw plugins install solana-traderclaw@1.0.20
44
+ openclaw plugins install solana-traderclaw
48
45
  ```
49
46
 
50
- **Names:** `solana-traderclaw` is the canonical npm package (this release includes X/Twitter journal tools). Older names may still resolve on npm for a time; prefer **`solana-traderclaw`**. OpenClaw may log a benign id hint if the npm package name and manifest `id` differ config keys remain **`plugins.entries.solana-trader`**.
47
+ Install name and config id are intentionally different:
48
+ - npm package / install command: `solana-traderclaw`
49
+ - OpenClaw plugin id in `openclaw.json`: `solana-trader`
50
+ - OpenClaw allowlist entry: `plugins.allow: ["solana-trader"]`
51
51
 
52
52
  ### 2. Run setup
53
53
 
@@ -73,7 +73,7 @@ openclaw gateway restart
73
73
  traderclaw install --wizard
74
74
  ```
75
75
 
76
- This opens a localhost UI that runs prechecks, lane-aware setup, gateway validation, optional Telegram setup, and final verification. **X (Twitter) OAuth fields in the wizard are optional** — leave all four blank to run without X; you can add credentials later in `openclaw.json` or via env (`X_CONSUMER_KEY`, `X_CONSUMER_SECRET`, per-agent `X_ACCESS_TOKEN_*`). It also installs **`HEARTBEAT.md` into your OpenClaw agent workspace root** (default `~/.openclaw/workspace/HEARTBEAT.md`) from the packaged skill so heartbeats load the TraderClaw checklist without manual `cp`. `traderclaw setup` does the same.
76
+ This opens a localhost UI that runs prechecks, lane-aware setup, gateway validation, optional Telegram setup, and final verification.
77
77
 
78
78
  ### Optional: Run CLI prechecks directly
79
79
 
@@ -148,15 +148,7 @@ traderclaw config set <key> <v> # Update a value
148
148
  traderclaw config reset # Remove all plugin config
149
149
  ```
150
150
 
151
- Available config keys: `orchestratorUrl`, `walletId`, `apiKey`, `apiTimeout`, `refreshToken`, `walletPublicKey`, `walletPrivateKey`, `gatewayBaseUrl`, `gatewayToken`, `agentId`
152
-
153
- Wallet proof note: if login/session challenge requires wallet ownership proof, provide the key at runtime with `--wallet-private-key` or `TRADERCLAW_WALLET_PRIVATE_KEY`. It is used for local signing only and is not stored in `~/.openclaw/openclaw.json`.
154
-
155
- **Gateway process:** Telegram and other channels talk to the **OpenClaw gateway** process. That process must be able to read `TRADERCLAW_WALLET_PRIVATE_KEY` when the session refresh fails and a new challenge runs. Exporting the variable only in your SSH session does **not** set it for **systemd** (or Docker, etc.). Configure it in the gateway unit’s environment per the troubleshooting guide below.
156
-
157
- **`traderclaw login`:** Uses the saved refresh token when valid (no wallet key needed). Use `traderclaw login --force-reauth` when you intentionally want a full API challenge (e.g. after `traderclaw logout`).
158
-
159
- **Session / auth / startup issues:** follow the official guide — [Installation → Troubleshooting (session expired, auth, logged out)](https://docs.traderclaw.ai/docs/installation#troubleshooting-session-expired-auth-errors-or-the-agent-logged-out).
151
+ Available config keys: `orchestratorUrl`, `walletId`, `apiKey`, `apiTimeout`, `refreshToken`, `recoverySecret`, `walletPublicKey`, `walletPrivateKey`, `gatewayBaseUrl`, `gatewayToken`, `agentId`
160
152
 
161
153
  ### `traderclaw --help`
162
154
 
@@ -173,6 +165,7 @@ If you prefer to configure manually instead of using the CLI, add to `~/.opencla
173
165
  ```json5
174
166
  {
175
167
  plugins: {
168
+ allow: ["solana-trader"],
176
169
  entries: {
177
170
  "solana-trader": {
178
171
  enabled: true,
@@ -199,9 +192,9 @@ openclaw gateway restart
199
192
 
200
193
  The plugin implements a 3-layer memory architecture that uses OpenClaw's native infrastructure plus custom tools to eliminate amnesia between sessions.
201
194
 
202
- ### Layer 1: Durable state (`STATE.md`) and narrative (`MEMORY.md`)
195
+ ### Layer 1: Durable Facts (`MEMORY.md`)
203
196
 
204
- OpenClaw loads workspace markdown from the agent workspace root. **`STATE.md`** is updated by `solana_state_save` with a human-readable mirror of durable facts (tier, wallet, mode, strategy version, watchlist, permanent learnings, regime canary). **`MEMORY.md`** is for your own notes — the plugin does not overwrite it. JSON state still lives under `.traderclaw-v1-data/state/` as before.
197
+ OpenClaw automatically loads `MEMORY.md` into agent context at every session start zero tool calls needed. When `solana_state_save` is called, it writes both a JSON state file AND updates `MEMORY.md` with curated durable facts: tier, wallet, mode, strategy version, watchlist, permanent learnings, and regime canary.
205
198
 
206
199
  ### Layer 2: Episodic Memory (Daily Logs + Bootstrap Injection)
207
200
 
@@ -215,7 +208,7 @@ Unlimited retention via the orchestrator API. `solana_memory_write` / `solana_me
215
208
 
216
209
  ### Memory Flush Hook
217
210
 
218
- The `memory:flush` hook fires automatically when OpenClaw is about to trim context. It syncs **`STATE.md`** from the last persisted JSON state and writes a compaction marker to the daily log. This is an automatic safety net — no agent action needed.
211
+ The `memory:flush` hook fires automatically when OpenClaw is about to trim context. It syncs `MEMORY.md` from the last persisted state and writes a compaction marker to the daily log. This is an automatic safety net — no agent action needed.
219
212
 
220
213
  ### Bootstrap Hook (`agent:bootstrap`)
221
214
 
@@ -241,29 +234,20 @@ Entitlement fallback chain: live API fetch → cached file → durable state →
241
234
  │ └── shared/ # Team bulletin (JSONL)
242
235
  ```
243
236
 
244
- Plus OpenClaw workspace paths (default `~/.openclaw/workspace/`):
237
+ Plus OpenClaw-native paths at project root:
245
238
  ```
246
- STATE.md # Machine-written durable state mirror (from solana_state_save)
247
- MEMORY.md # Optional agent narrative (not overwritten by the plugin)
239
+ MEMORY.md # Curated durable facts (auto-loaded by OpenClaw)
248
240
  memory/
249
241
  ├── 2026-03-19.md # Today's daily log (auto-loaded by OpenClaw)
250
242
  ├── 2026-03-18.md # Yesterday's daily log (auto-loaded by OpenClaw)
251
243
  └── ... # Auto-pruned after 7 days
252
244
  ```
253
245
 
254
- ## X/Twitter Setup
255
-
256
- The team edition includes 5 X/Twitter tools for trade journaling and community engagement. Setup requires an X Developer App.
257
-
258
- ### 1. Create an X Developer App
246
+ ## X/Twitter Setup (Read-Only Social Intel)
259
247
 
260
- 1. Go to [developer.x.com](https://developer.x.com) and sign in
261
- 2. Create a new App (Free tier is sufficient for posting — 1,500 tweets/month)
262
- 3. Note your **Consumer Key** and **Consumer Secret**
263
- 4. Under "User authentication settings", enable OAuth 1.0a with Read and Write permissions
264
- 5. Generate **Access Token** and **Access Token Secret** for the account that will post
248
+ The public edition includes 3 read-only X/Twitter tools for social intelligence gathering (search tweets, read mentions, get threads). X posting is not included in the public edition.
265
249
 
266
- ### 2. Configure via Environment Variables
250
+ ### Configure via Environment Variables
267
251
 
268
252
  ```bash
269
253
  export X_CONSUMER_KEY="your-app-consumer-key"
@@ -272,9 +256,7 @@ export X_ACCESS_TOKEN_MAIN="your-access-token"
272
256
  export X_ACCESS_TOKEN_MAIN_SECRET="your-access-token-secret"
273
257
  ```
274
258
 
275
- The installer will pick these up automatically during the `x_credentials` step.
276
-
277
- ### 3. Or Configure via Plugin Config
259
+ ### Or Configure via Plugin Config
278
260
 
279
261
  Add to `~/.openclaw/openclaw.json` under the plugin entry:
280
262
 
@@ -297,17 +279,17 @@ Add to `~/.openclaw/openclaw.json` under the plugin entry:
297
279
 
298
280
  | Tier | Cost | Capabilities |
299
281
  |------|------|-------------|
300
- | Free | $0 | 1,500 posts/month (write-only) |
301
282
  | Pay-as-you-go | Per-credit | Read access (mentions, search, threads) |
302
283
  | Basic | $200/month | Higher limits, more read access |
303
284
 
304
- Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended if you want to read mentions and search.
285
+ Pay-as-you-go or Basic tier is required for the read-only social intel tools.
305
286
 
306
- ## Available Tools (7267 trading + 5 X)
287
+ ## Available Tools (9794 trading + 3 X read-only)
307
288
 
308
289
  ### Scanning
309
290
  | Tool | Description |
310
291
  |------|-------------|
292
+ | `solana_scan` | Broad market scan — launches + hot pairs combined |
311
293
  | `solana_scan_launches` | Find new Solana token launches |
312
294
  | `solana_scan_hot_pairs` | Find high-volume trading pairs |
313
295
  | `solana_market_regime` | Get macro market state (bullish/bearish/neutral) |
@@ -330,7 +312,8 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
330
312
  | Tool | Description |
331
313
  |------|-------------|
332
314
  | `solana_trade_precheck` | Pre-trade risk validation |
333
- | `solana_trade_execute` | Execute trade via SpyFly bot |
315
+ | `solana_trade_execute` | Execute trade via SpyFly bot (full params) |
316
+ | `solana_trade` | Execute trade (shorthand) |
334
317
 
335
318
  ### Reflection & Server-Side Memory
336
319
  | Tool | Description |
@@ -361,6 +344,8 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
361
344
  | `solana_funding_instructions` | Deposit instructions |
362
345
  | `solana_wallets` | List all wallets |
363
346
  | `solana_wallet_create` | Create a new wallet |
347
+ | `solana_wallet_token_balance` | Get SPL token balance for a specific mint |
348
+ | `solana_sweep_dead_tokens` | Sell losing positions below loss threshold to cut losses and reclaim SOL |
364
349
 
365
350
  ### Entitlements
366
351
  | Tool | Description |
@@ -385,6 +370,13 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
385
370
  | `solana_alpha_signals` | Retrieve buffered alpha signals |
386
371
  | `solana_alpha_history` | Query historical alpha signals |
387
372
  | `solana_alpha_sources` | Get source reputation statistics |
373
+ | `solana_alpha_submit` | Submit candidate to alpha buffer for next heartbeat evaluation |
374
+
375
+ ### Firehose
376
+ | Tool | Description |
377
+ |------|-------------|
378
+ | `solana_firehose_config` | Configure firehose filter parameters (volume, buyers, whale detection) |
379
+ | `solana_firehose_status` | Check firehose health, throughput, and connection state |
388
380
 
389
381
  ### Bitquery Deep Scans
390
382
  | Tool | Description |
@@ -412,7 +404,7 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
412
404
  ### Local Durable State
413
405
  | Tool | Description |
414
406
  |------|-------------|
415
- | `solana_state_save` | Save agent state to local JSON (also writes STATE.md under the workspace) |
407
+ | `solana_state_save` | Save agent state to local JSON (also writes MEMORY.md) |
416
408
  | `solana_state_read` | Read agent state from local JSON |
417
409
 
418
410
  ### Episodic Decision Log
@@ -438,7 +430,29 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
438
430
  | `solana_compute_confidence` | Weighted confidence score (on-chain, signal, social, smart money, risk penalty) |
439
431
  | `solana_compute_freshness_decay` | Freshness decay factor by signal age |
440
432
  | `solana_compute_position_limits` | Full position sizing ladder with reduction breakdown |
441
- | `solana_classify_deployer_risk` | Deployer wallet risk classification (LOW/MODERATE/HIGH/CRITICAL) |
433
+ | `solana_compute_deployer_risk` | Deployer wallet risk classification (LOW/MEDIUM/HIGH) |
434
+ | `solana_classify_deployer_risk` | Backward-compatible alias for solana_compute_deployer_risk |
435
+
436
+ ### Intelligence Lab (New in V1-Upgraded)
437
+ | Tool | Description |
438
+ |------|-------------|
439
+ | `solana_candidate_write` | Write a trading candidate for evaluation |
440
+ | `solana_candidate_get` | Get a trading candidate by ID |
441
+ | `solana_candidate_label_outcome` | Label a candidate with actual outcome |
442
+ | `solana_candidate_delta` | Compute prediction delta for a candidate |
443
+ | `solana_source_trust_refresh` | Refresh trust scores for intelligence sources |
444
+ | `solana_source_trust_get` | Get current trust score for a source |
445
+ | `solana_model_score_candidate` | Score a candidate using the active model |
446
+ | `solana_model_registry` | List registered scoring models |
447
+ | `solana_model_promote` | Promote a challenger model to active |
448
+ | `solana_contradiction_check` | Check for contradictions in signal data |
449
+ | `solana_dataset_export` | Export labeled dataset for analysis |
450
+ | `solana_deployer_trust_get` | Get deployer trust profile |
451
+ | `solana_deployer_trust_refresh` | Refresh deployer trust from on-chain data |
452
+ | `solana_evaluation_report` | Generate intelligence evaluation report |
453
+ | `solana_replay_run` | Run a replay of historical decisions |
454
+ | `solana_replay_report` | Get results of a replay run |
455
+ | `solana_scrub_untrusted_text` | Scrub untrusted text for prompt injection |
442
456
 
443
457
  ### Deep Analysis
444
458
  | Tool | Description |
@@ -451,13 +465,11 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
451
465
  |------|-------------|
452
466
  | `solana_daily_log` | Append to today's daily log (auto-loaded by OpenClaw next session, 7-day prune) |
453
467
 
454
- ### X/Twitter
468
+ ### X/Twitter (Read-Only Social Intel)
455
469
  | Tool | Description |
456
470
  |------|-------------|
457
- | `x_post_tweet` | Post a tweet from the agent's configured X profile (max 280 chars) |
458
- | `x_reply_tweet` | Reply to a specific tweet |
459
- | `x_read_mentions` | Read recent @mentions (pay-as-you-go tier) |
460
471
  | `x_search_tweets` | Search recent tweets by keyword/hashtag |
472
+ | `x_read_mentions` | Read recent @mentions (pay-as-you-go tier) |
461
473
  | `x_get_thread` | Read a full conversation thread |
462
474
 
463
475
  ## Hooks (2)
@@ -465,7 +477,7 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
465
477
  | Hook | Trigger | What It Does |
466
478
  |------|---------|--------------|
467
479
  | `agent:bootstrap` | Every session start | Injects durable state, decisions, bulletin, snapshot, and entitlements into context |
468
- | `memory:flush` | Before OpenClaw context compaction | Syncs STATE.md from persisted state, writes compaction marker to daily log |
480
+ | `memory:flush` | Before OpenClaw context compaction | Syncs MEMORY.md from persisted state, writes compaction marker to daily log |
469
481
 
470
482
  ## Skills
471
483
 
@@ -473,7 +485,7 @@ Free tier is sufficient for daily trade journaling. Pay-as-you-go is recommended
473
485
  The primary skill that teaches OpenClaw the complete trading lifecycle:
474
486
 
475
487
  1. **SCAN** — Find opportunities with launch/hot-pair scanners
476
- 2. **ANALYZE** — Deep dive with 5 token analysis tools
488
+ 2. **ANALYZE** — Deep dive with 6 token analysis tools
477
489
  3. **THESIS** — Assemble full context with build_thesis
478
490
  4. **DECIDE** — Agent reasons over data using confidence scoring
479
491
  5. **PRECHECK** — Validate against risk rules
@@ -527,13 +539,6 @@ Trade executed. TradeId: 15, PositionId: 4, TX: 5xK...
527
539
  I'll monitor this position and review after exit.
528
540
  ```
529
541
 
530
- ## Session and file reload
531
-
532
- - **Telegram:** use **`/new`** to start a fresh chat thread or **`/reset`** to reset session state with your provider, as supported by your Telegram bot integration.
533
- - **After editing workspace files** (`HEARTBEAT.md`, `STATE.md`, `MEMORY.md`, skills): restart the gateway so changes are picked up consistently: `openclaw gateway restart`.
534
- - **History:** OpenClaw may not ship `openclaw agents clear-history` on your build — do not rely on that subcommand; prefer Telegram session commands and gateway restart.
535
- - **Heartbeats:** fresh installs set `heartbeat.target: "telegram"`, `isolatedSession: true`, `lightContext: true`, and `channels.defaults.heartbeat.showOk: true` so scheduled cycles deliver reliably (see installer `configureGatewayScheduling`).
536
-
537
542
  ## Troubleshooting
538
543
 
539
544
  **Plugin won't load:**
@@ -554,29 +559,6 @@ I'll monitor this position and review after exit.
554
559
  - Run `traderclaw setup` to create or select a wallet
555
560
  - Verify the wallet ID: `traderclaw config show`
556
561
 
557
- **Wizard (`traderclaw install --wizard`) fails on `install_plugin_package` with `ENOENT` / `…/solana-traderclaw/package.json` / `file:solana-traderclaw`:**
558
- - Older installers used `spawn` with `shell: true`, which on Linux could drop npm’s argv so npm treated the package as a **local folder** under cwd (`/root` or `/tmp`). Current installers use **`shell: false`**, explicit **`--registry https://registry.npmjs.org/`** for registry installs, **`solana-traderclaw@latest`**, and a temp **`cwd`**. Upgrade `solana-traderclaw` and retry. If a stray directory `./solana-traderclaw` exists under that cwd, remove it: `rm -rf /tmp/solana-traderclaw` (and under `/root` if present).
559
-
560
- **Heartbeat not sending messages to Telegram:**
561
- - **Fresh `traderclaw setup`:** the installer runs `configureGatewayScheduling`, which sets a custom `heartbeat.prompt` on the `main` agent (no `HEARTBEAT_OK` escape). You only need the manual `openclaw config set` command below if you are on an older install or overwrote `agents.list`.
562
- - **`HEARTBEAT.md` must live in the agent workspace root** (default `~/.openclaw/workspace/HEARTBEAT.md`, next to `AGENTS.md`). A copy under `workspace/.openclaw/` or only inside the plugin package is **not** loaded as the heartbeat checklist. Copy from `skills/solana-trader/HEARTBEAT.md` in the installed package if needed. See [OpenClaw agent workspace](https://docs.openclaw.ai/concepts/agent-workspace).
563
- - OpenClaw's default heartbeat prompt tells the model "If nothing needs attention, reply HEARTBEAT_OK" — which is stripped and never delivered. Run `traderclaw setup` again (v1.0.16+) to set a custom prompt, or apply manually:
564
-
565
- ```bash
566
- openclaw config set agents.list '[{"id":"main","default":true,"heartbeat":{"every":"30m","target":"telegram","isolatedSession":true,"lightContext":true,"prompt":"Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Execute a full trading cycle: Steps 0 through 10. The cycle is NOT complete until all 10 steps are done including Step 8 (memory write-back), Step 9 (X post), and Step 10 (report). Do not stop early. Do not infer or repeat old tasks from prior chats. Never reply HEARTBEAT_OK. Never end your message with a question."}}]'
567
- openclaw gateway restart
568
- ```
569
-
570
- - Verify with `openclaw config get agents` — the `main` agent should have `heartbeat.prompt`, `target`, `isolatedSession`, and `lightContext`.
571
- - Confirm Telegram is healthy: `openclaw channels status --probe`
572
- - Prefer `target: "telegram"` over `"last"` so delivery does not depend on a prior channel contact.
573
-
574
- **Scheduled cron jobs run but you never see Telegram/WhatsApp output:**
575
- - The installer merges **six** prescriptive managed jobs (`alpha-scan`, `dead-money-sweep`, `source-reputation-recalc`, `meta-rotation-analysis`, `strategy-evolution`, `daily-performance-report`). Older templates such as `subscription-cleanup` or `whale-watch` are no longer in that managed set; re-run **`traderclaw setup`** to refresh `~/.openclaw/cron/jobs.json`, or edit jobs manually. User-defined cron jobs whose ids are not in the template set are preserved on merge.
576
- - TraderClaw templates merged into `~/.openclaw/cron/jobs.json` use **`delivery.mode: "announce"`** with **`channel: "last"`** so each completed isolated job posts a summary to the same channel you last used (see [OpenClaw cron delivery](https://docs.clawd.bot/automation/cron-jobs)).
577
- - If you installed before this behavior: run **`traderclaw setup`** again so the installer re-merges cron jobs, or stop the gateway and edit managed job entries in `~/.openclaw/cron/jobs.json` — set **`delivery`** to `{ "mode": "announce", "channel": "last", "bestEffort": true }` for each TraderClaw job (or remove `delivery` entirely; isolated jobs default to announce when omitted).
578
- - The same **`last`** requirement as heartbeat applies: message the bot at least once so the gateway knows where to deliver.
579
-
580
562
  **Tools returning errors:**
581
563
  - Run `traderclaw status` to check system health
582
564
  - Check if kill switch is enabled
@@ -585,7 +567,5 @@ openclaw gateway restart
585
567
  **Memory/state not persisting:**
586
568
  - Check that the `dataDir` config points to a writable location
587
569
  - Default is `<cwd>/.traderclaw-v1-data` — verify permissions
588
- - Check **`STATE.md`** exists under `~/.openclaw/workspace/` after first `solana_state_save` call (not `process.cwd()` — required for systemd installs)
589
- - Check `memory/` under the same workspace for daily log files
590
-
591
- **X / Twitter posting:** with X credentials and skills aligned, `x_post_tweet` has been validated end-to-end in production installs after config + workspace updates.
570
+ - Check `MEMORY.md` exists at project root after first `solana_state_save` call
571
+ - Check `memory/` directory for daily log files
@@ -55,8 +55,105 @@ function getNpmGlobalInstallCwd() {
55
55
  }
56
56
  }
57
57
 
58
- /** Older `plugins.entries` keys / npm-era ids to merge orchestrator URL for. */
59
- const LEGACY_TRADER_PLUGIN_IDS = ["traderclaw-v1", "solana-traderclaw-v1", "solana-trader"];
58
+ /** Older `plugins.entries` keys / npm-era ids for the v1 plugin. */
59
+ const LEGACY_TRADER_PLUGIN_IDS = ["traderclaw-v1", "solana-traderclaw-v1", "solana-traderclaw"];
60
+
61
+ function isRecord(value) {
62
+ return !!value && typeof value === "object" && !Array.isArray(value);
63
+ }
64
+
65
+ function getLegacyTraderPluginIds(pluginId) {
66
+ return pluginId === "solana-trader" ? LEGACY_TRADER_PLUGIN_IDS : [];
67
+ }
68
+
69
+ function normalizeTraderPluginEntries(config, pluginId) {
70
+ if (!isRecord(config)) return false;
71
+ if (!isRecord(config.plugins)) config.plugins = {};
72
+ if (!isRecord(config.plugins.entries)) config.plugins.entries = {};
73
+
74
+ const entries = config.plugins.entries;
75
+ const legacyIds = getLegacyTraderPluginIds(pluginId);
76
+ if (legacyIds.length === 0) return false;
77
+
78
+ let touched = false;
79
+ let hasSource = false;
80
+ let enabledSeen = false;
81
+ let enabledValue = false;
82
+ let mergedConfig = {};
83
+
84
+ for (const sourceId of [...legacyIds, pluginId]) {
85
+ const entry = entries[sourceId];
86
+ if (!isRecord(entry)) continue;
87
+ hasSource = true;
88
+ if (typeof entry.enabled === "boolean") {
89
+ enabledSeen = true;
90
+ enabledValue = enabledValue || entry.enabled;
91
+ }
92
+ if (isRecord(entry.config)) {
93
+ mergedConfig = { ...mergedConfig, ...entry.config };
94
+ }
95
+ }
96
+
97
+ if (!hasSource) return false;
98
+
99
+ const canonicalEntry = isRecord(entries[pluginId]) ? entries[pluginId] : {};
100
+ const nextEntry = {
101
+ ...canonicalEntry,
102
+ enabled: typeof canonicalEntry.enabled === "boolean" ? canonicalEntry.enabled : (enabledSeen ? enabledValue : true),
103
+ config: mergedConfig,
104
+ };
105
+
106
+ if (entries[pluginId] !== nextEntry) {
107
+ entries[pluginId] = nextEntry;
108
+ touched = true;
109
+ }
110
+
111
+ for (const legacyId of legacyIds) {
112
+ if (Object.prototype.hasOwnProperty.call(entries, legacyId)) {
113
+ delete entries[legacyId];
114
+ touched = true;
115
+ }
116
+ }
117
+
118
+ return touched;
119
+ }
120
+
121
+ function normalizeTraderAllowlist(config, pluginId) {
122
+ if (!isRecord(config?.plugins)) return false;
123
+ const legacyIds = new Set(getLegacyTraderPluginIds(pluginId));
124
+ if (legacyIds.size === 0 || !Array.isArray(config.plugins.allow)) return false;
125
+
126
+ const nextAllow = [];
127
+ const seen = new Set();
128
+ let touched = false;
129
+
130
+ for (const id of config.plugins.allow) {
131
+ if (typeof id !== "string") {
132
+ touched = true;
133
+ continue;
134
+ }
135
+ const trimmed = id.trim();
136
+ if (!trimmed) {
137
+ touched = true;
138
+ continue;
139
+ }
140
+ if (legacyIds.has(trimmed)) {
141
+ touched = true;
142
+ continue;
143
+ }
144
+ if (seen.has(trimmed)) {
145
+ touched = true;
146
+ continue;
147
+ }
148
+ seen.add(trimmed);
149
+ nextAllow.push(trimmed);
150
+ }
151
+
152
+ if (touched) {
153
+ config.plugins.allow = nextAllow;
154
+ }
155
+ return touched;
156
+ }
60
157
 
61
158
  function stripAnsi(text) {
62
159
  if (typeof text !== "string") return text;
@@ -433,6 +530,9 @@ function seedPluginConfig(modeConfig, orchestratorUrl, configPath = CONFIG_FILE)
433
530
  if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
434
531
  if (!config.plugins.entries || typeof config.plugins.entries !== "object") config.plugins.entries = {};
435
532
 
533
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
534
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
535
+
436
536
  const entries = config.plugins.entries;
437
537
 
438
538
  const mergeOrchestratorForId = (pluginId) => {
@@ -453,9 +553,6 @@ function seedPluginConfig(modeConfig, orchestratorUrl, configPath = CONFIG_FILE)
453
553
  };
454
554
 
455
555
  mergeOrchestratorForId(modeConfig.pluginId);
456
- for (const legacyId of LEGACY_TRADER_PLUGIN_IDS) {
457
- if (entries[legacyId]) mergeOrchestratorForId(legacyId);
458
- }
459
556
 
460
557
  // Do not set plugins.allow here: OpenClaw validates allow[] against the plugin registry, and
461
558
  // the id is not registered until after `openclaw plugins install`. Pre-seeding allow caused:
@@ -596,6 +693,8 @@ function mergePluginsAllowlist(modeConfig, configPath = CONFIG_FILE) {
596
693
  return;
597
694
  }
598
695
  if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
696
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
697
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
599
698
  const allowSet = new Set(
600
699
  Array.isArray(config.plugins.allow) ? config.plugins.allow.filter((id) => typeof id === "string" && id.trim()) : [],
601
700
  );
@@ -972,6 +1071,8 @@ function seedXConfig(modeConfig, configPath = CONFIG_FILE, wizardOpts = {}) {
972
1071
 
973
1072
  if (!config.plugins || typeof config.plugins !== "object") config.plugins = {};
974
1073
  if (!config.plugins.entries || typeof config.plugins.entries !== "object") config.plugins.entries = {};
1074
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
1075
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
975
1076
 
976
1077
  const entry = config.plugins.entries[modeConfig.pluginId];
977
1078
  if (!entry || typeof entry !== "object") return { skipped: true, reason: "plugin entry not found" };
@@ -1047,6 +1148,8 @@ function persistXProfileIdentities(configPath, modeConfig, identities) {
1047
1148
  } catch {
1048
1149
  return { written: 0 };
1049
1150
  }
1151
+ normalizeTraderPluginEntries(config, modeConfig.pluginId);
1152
+ normalizeTraderAllowlist(config, modeConfig.pluginId);
1050
1153
  const entry = config?.plugins?.entries?.[modeConfig.pluginId];
1051
1154
  if (!entry?.config?.x?.profiles || typeof entry.config.x.profiles !== "object") return { written: 0 };
1052
1155
 
@@ -13,6 +13,7 @@ const PACKAGE_JSON = JSON.parse(readFileSync(new URL("../package.json", import.m
13
13
  const VERSION = PACKAGE_JSON.version;
14
14
  const NPM_PACKAGE_NAME = typeof PACKAGE_JSON.name === "string" ? PACKAGE_JSON.name : "solana-traderclaw";
15
15
  const PLUGIN_ID = "solana-trader";
16
+ const LEGACY_PLUGIN_IDS = ["traderclaw-v1", "solana-traderclaw-v1", "solana-traderclaw"];
16
17
  const CONFIG_DIR = join(homedir(), ".openclaw");
17
18
  const CONFIG_FILE = join(CONFIG_DIR, "openclaw.json");
18
19
  const WALLET_PRIVATE_KEY_ENV = "TRADERCLAW_WALLET_PRIVATE_KEY";
@@ -166,11 +167,69 @@ function readConfig() {
166
167
  }
167
168
 
168
169
  function writeConfig(config) {
170
+ normalizePluginConfigShape(config);
169
171
  mkdirSync(CONFIG_DIR, { recursive: true });
170
172
  writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
171
173
  }
172
174
 
175
+ function isRecord(value) {
176
+ return !!value && typeof value === "object" && !Array.isArray(value);
177
+ }
178
+
179
+ function normalizePluginConfigShape(config) {
180
+ if (!isRecord(config)) return config;
181
+ if (!isRecord(config.plugins)) config.plugins = {};
182
+ const plugins = config.plugins;
183
+ if (!isRecord(plugins.entries)) plugins.entries = {};
184
+ const entries = plugins.entries;
185
+
186
+ let enabledSeen = false;
187
+ let enabledValue = false;
188
+ let mergedConfig = {};
189
+ let found = false;
190
+
191
+ for (const sourceId of [...LEGACY_PLUGIN_IDS, PLUGIN_ID]) {
192
+ const entry = entries[sourceId];
193
+ if (!isRecord(entry)) continue;
194
+ found = true;
195
+ if (typeof entry.enabled === "boolean") {
196
+ enabledSeen = true;
197
+ enabledValue = enabledValue || entry.enabled;
198
+ }
199
+ if (isRecord(entry.config)) {
200
+ mergedConfig = { ...mergedConfig, ...entry.config };
201
+ }
202
+ }
203
+
204
+ if (found) {
205
+ const canonical = isRecord(entries[PLUGIN_ID]) ? entries[PLUGIN_ID] : {};
206
+ entries[PLUGIN_ID] = {
207
+ ...canonical,
208
+ enabled: typeof canonical.enabled === "boolean" ? canonical.enabled : (enabledSeen ? enabledValue : true),
209
+ config: mergedConfig,
210
+ };
211
+ }
212
+
213
+ for (const legacyId of LEGACY_PLUGIN_IDS) {
214
+ delete entries[legacyId];
215
+ }
216
+
217
+ if (Array.isArray(plugins.allow)) {
218
+ const seen = new Set();
219
+ plugins.allow = plugins.allow.filter((id) => {
220
+ if (typeof id !== "string") return false;
221
+ const trimmed = id.trim();
222
+ if (!trimmed || LEGACY_PLUGIN_IDS.includes(trimmed) || seen.has(trimmed)) return false;
223
+ seen.add(trimmed);
224
+ return true;
225
+ });
226
+ }
227
+
228
+ return config;
229
+ }
230
+
173
231
  function getPluginConfig(config) {
232
+ normalizePluginConfigShape(config);
174
233
  const plugins = config.plugins;
175
234
  if (!plugins) return null;
176
235
  const entries = plugins.entries;
@@ -181,6 +240,7 @@ function getPluginConfig(config) {
181
240
  }
182
241
 
183
242
  function setPluginConfig(config, pluginConfig) {
243
+ normalizePluginConfigShape(config);
184
244
  if (!config.plugins) config.plugins = {};
185
245
  if (!config.plugins.entries) config.plugins.entries = {};
186
246
  config.plugins.entries[PLUGIN_ID] = {