solana-traderclaw 1.0.145 → 1.0.147

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.
@@ -141,12 +141,19 @@ var AlphaStreamManager = class {
141
141
  return this.subscribed && this.ws !== null && this.ws.readyState === 1;
142
142
  }
143
143
  getStats() {
144
+ const ls = this.config.lifetimeState;
145
+ const lifetimeMessageCount = ls ? ls.lifetimeMessageCount : this.messageCount;
146
+ const firstConnectedAt = ls && ls.firstConnectedAt > 0 ? ls.firstConnectedAt : this.connectedAt;
147
+ const lifetimeLastEventTs = ls && ls.lifetimeLastEventTs > 0 ? ls.lifetimeLastEventTs : this.lastEventTs;
144
148
  return {
145
149
  subscribed: this.isSubscribed(),
146
- messageCount: this.messageCount,
147
- lastEventTs: this.lastEventTs,
150
+ messageCount: lifetimeMessageCount,
151
+ currentWsMessageCount: this.messageCount,
152
+ lastEventTs: lifetimeLastEventTs,
148
153
  connectedAt: this.connectedAt,
154
+ firstConnectedAt,
149
155
  uptimeSeconds: this.connectedAt ? Math.floor((Date.now() - this.connectedAt) / 1e3) : 0,
156
+ lifetimeUptimeSeconds: firstConnectedAt ? Math.floor((Date.now() - firstConnectedAt) / 1e3) : 0,
150
157
  reconnectAttempt: this.reconnectAttempt,
151
158
  unhealthyStreak: this.unhealthyStreak,
152
159
  circuitBackoff: this.unhealthyStreak >= CIRCUIT_UNHEALTHY_THRESHOLD
@@ -200,6 +207,9 @@ var AlphaStreamManager = class {
200
207
  this.ws.on("open", () => {
201
208
  clearTimeout(connectTimeout);
202
209
  this.connectedAt = Date.now();
210
+ if (this.config.lifetimeState && this.config.lifetimeState.firstConnectedAt === 0) {
211
+ this.config.lifetimeState.firstConnectedAt = this.connectedAt;
212
+ }
203
213
  this.reconnectAttempt = 0;
204
214
  this.log("info", "WebSocket connected, waiting for server handshake...");
205
215
  pingInterval = setInterval(() => {
@@ -286,6 +296,10 @@ var AlphaStreamManager = class {
286
296
  case "alpha_signal": {
287
297
  this.messageCount++;
288
298
  this.lastEventTs = Date.now();
299
+ if (this.config.lifetimeState) {
300
+ this.config.lifetimeState.lifetimeMessageCount++;
301
+ this.config.lifetimeState.lifetimeLastEventTs = this.lastEventTs;
302
+ }
289
303
  const data = msg.data;
290
304
  if (data) {
291
305
  const signal = {
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-3UQIQJPQ.js";
20
20
  import {
21
21
  AlphaStreamManager
22
- } from "./chunk-ZUY36F4Q.js";
22
+ } from "./chunk-GX4HA22L.js";
23
23
  import {
24
24
  BitqueryStreamManager
25
25
  } from "./chunk-S2DLZKMQ.js";
@@ -48,6 +48,7 @@ import { Type } from "@sinclair/typebox";
48
48
  import kayba, { SpanType } from "@kayba_ai/tracing";
49
49
  import * as fs from "fs";
50
50
  import * as path from "path";
51
+ import { fileURLToPath } from "node:url";
51
52
  import { homedir } from "os";
52
53
 
53
54
  // lib/x-client.mjs
@@ -777,6 +778,41 @@ var SOLANA_TRADER_LIFECYCLE_SINGLETON_KEY = Symbol.for(
777
778
  "openclaw.solana-trader.lifecycle.v1"
778
779
  );
779
780
  var __solanaTraderGlobalSingletonHolder = globalThis;
781
+ var SOLANA_TRADER_ALPHA_BUFFER_SINGLETON_KEY = Symbol.for(
782
+ "openclaw.solana-trader.alpha-buffer.v1"
783
+ );
784
+ var SOLANA_TRADER_ALPHA_LIFETIME_SINGLETON_KEY = Symbol.for(
785
+ "openclaw.solana-trader.alpha-lifetime.v1"
786
+ );
787
+ var __solanaTraderAlphaSingletonHolder = globalThis;
788
+ function getOrCreateAlphaBuffer() {
789
+ const existing = __solanaTraderAlphaSingletonHolder[SOLANA_TRADER_ALPHA_BUFFER_SINGLETON_KEY];
790
+ if (existing) return existing;
791
+ const fresh = new AlphaBuffer();
792
+ __solanaTraderAlphaSingletonHolder[SOLANA_TRADER_ALPHA_BUFFER_SINGLETON_KEY] = fresh;
793
+ return fresh;
794
+ }
795
+ function getOrCreateAlphaLifetimeState() {
796
+ const existing = __solanaTraderAlphaSingletonHolder[SOLANA_TRADER_ALPHA_LIFETIME_SINGLETON_KEY];
797
+ if (existing) return existing;
798
+ const fresh = {
799
+ lifetimeMessageCount: 0,
800
+ firstConnectedAt: 0,
801
+ lifetimeLastEventTs: 0
802
+ };
803
+ __solanaTraderAlphaSingletonHolder[SOLANA_TRADER_ALPHA_LIFETIME_SINGLETON_KEY] = fresh;
804
+ return fresh;
805
+ }
806
+ var __solanaTraderStatusQueriesMd = (() => {
807
+ try {
808
+ const distPath = fileURLToPath(import.meta.url);
809
+ const packageRoot = path.dirname(path.dirname(distPath));
810
+ const mdPath = path.join(packageRoot, "lib", "status-queries.md");
811
+ return fs.readFileSync(mdPath, "utf-8");
812
+ } catch {
813
+ return void 0;
814
+ }
815
+ })();
780
816
  function __solanaTraderDisposePreviousLifecycle(logger) {
781
817
  const prev = __solanaTraderGlobalSingletonHolder[SOLANA_TRADER_LIFECYCLE_SINGLETON_KEY];
782
818
  if (!prev) return;
@@ -2572,11 +2608,13 @@ ${notes}
2572
2608
  async () => get("/api/agents/active")
2573
2609
  )
2574
2610
  });
2575
- const alphaBuffer = new AlphaBuffer();
2611
+ const alphaBuffer = getOrCreateAlphaBuffer();
2612
+ const alphaLifetimeState = getOrCreateAlphaLifetimeState();
2576
2613
  const alphaStreamManager = new AlphaStreamManager({
2577
2614
  wsUrl: orchestratorUrl.replace(/^http/, "ws").replace(/\/$/, "") + "/ws",
2578
2615
  getAccessToken: () => sessionManager.getAccessToken(),
2579
2616
  buffer: alphaBuffer,
2617
+ lifetimeState: alphaLifetimeState,
2580
2618
  agentId: config.agentId,
2581
2619
  logger: {
2582
2620
  info: (msg) => api.logger.info(`[solana-trader] ${msg}`),
@@ -2799,7 +2837,7 @@ ${notes}
2799
2837
  });
2800
2838
  api.registerTool({
2801
2839
  name: "solana_alpha_signals",
2802
- description: "Get buffered alpha signals from the SpyFly stream. By default returns only unseen signals and marks them as seen. Use minScore to filter low-quality signals. Poll this every heartbeat cycle in Step 1.5b. Returns signals sorted by ingestion time (newest last).",
2840
+ description: "Read the live alpha-stream state and buffered signals from the running plugin. **Single source of truth** for any 'how many alpha signals / are we getting alpha / is alpha connected / latest signals' question \u2014 call it on the same turn. Signals are NOT logged per-message, so the journal/heartbeat-history will look empty; only this tool returns the real count.\n\nReturned shape: { signals[], count, bufferSize, subscribed, stats }.\n\nHEADLINE FIELDS for live status answers:\n - stats.messageCount = total alpha_signal messages this gateway process has received (survives plugin re-registers and WS reconnects). Use this for 'how many signals so far' answers.\n - stats.lifetimeUptimeSeconds = seconds since the first WS connect in this gateway process. Divide messageCount/lifetimeUptimeSeconds to get rate.\n - stats.lastEventTs = wall-clock ms of the most recent alpha_signal (lifetime).\n - subscribed = current WS is in subscribed state.\n - stats.reconnectAttempt, stats.unhealthyStreak, stats.circuitBackoff = health signals.\n\nDebug-only fields (do NOT report to users as 'totals'):\n - stats.currentWsMessageCount = messages since the CURRENT WS connect (resets on every reconnect; misleading as a 'total').\n - stats.uptimeSeconds = uptime of the current WS connect.\n - stats.connectedAt = ts of the current WS open.\n\nFor windows beyond what bufferSize covers (\u2248200 signals), or after a gateway restart wiped the lifetime counters, fall through to `solana_alpha_history` (REST, tier=enterprise \u2192 up to 200 results, last 12+ months).\n\nParams: pass unseen:false when the user is asking about overall state (don't mutate _seen). Pass unseen:true (default) only during heartbeat polling to mark and consume new signals. Use minScore to filter low-quality. Returns signals sorted by ingestion time (newest last).",
2803
2841
  parameters: Type.Object({
2804
2842
  minScore: Type.Optional(Type.Number({ description: "Minimum systemScore threshold (0-100). Signals below this are excluded." })),
2805
2843
  chain: Type.Optional(Type.String({ description: "Filter by chain (e.g., 'solana'). BSC is already filtered at ingestion." })),
@@ -2824,7 +2862,7 @@ ${notes}
2824
2862
  });
2825
2863
  api.registerTool({
2826
2864
  name: "solana_alpha_history",
2827
- description: "Query historical alpha signal data via the SpyFly REST API (GET /api/pings). Returns up to 1 year of stored signals for source reputation analysis, post-downtime catch-up, and strategy learning. Tier-gated: starter=10, pro=50, enterprise=200 results. 99.99% of tokens are dead but source patterns are invaluable.",
2865
+ description: "Query historical alpha signal data via the orchestrator REST API (GET /api/pings). Use this whenever the user asks about an ALPHA window that extends beyond what `solana_alpha_signals` can cover, e.g.:\n - 'how many alpha signals in the last hour / today / this week / since Monday'\n - 'any alpha on <token> in the last 24h'\n - 'PAIN/$X signals from yesterday'\n - any time the live buffer is empty or just reconnected but the user is asking about a real time window.\n\nReturns up to 1 year of stored signals. Tier-gated by your account (starter=10, pro=50, enterprise=200 results). Combine with `solana_alpha_signals` (live state) to answer 'we've received N signals lifetime in this gateway, plus M historical in the window you asked about'.\n\nParams: tokenAddress, channelId, limit, days (lookback window). 99.99% of tokens are dead but source patterns are invaluable.",
2828
2866
  parameters: Type.Object({
2829
2867
  tokenAddress: Type.Optional(Type.String({ description: "Filter by token mint address" })),
2830
2868
  channelId: Type.Optional(Type.String({ description: "Filter by source channel ID" })),
@@ -2848,7 +2886,7 @@ ${notes}
2848
2886
  });
2849
2887
  api.registerTool({
2850
2888
  name: "solana_alpha_sources",
2851
- description: "Get per-source statistics from the alpha signal buffer \u2014 signal count, average systemScore, and source type for each channel. Use for quick reputation checks during signal processing and to identify high-quality vs low-quality sources.",
2889
+ description: "Get per-source statistics from the alpha signal buffer \u2014 signal count, average systemScore, and source type for each channel. Call this when the user asks 'which alpha sources / channels are active', 'where are signals coming from', or any breakdown-by-source question. Always live; never answer source breakdowns from memory. Use also for quick reputation checks during signal processing.",
2852
2890
  parameters: Type.Object({}),
2853
2891
  execute: wrapExecute("solana_alpha_sources", async () => ({
2854
2892
  sources: alphaBuffer.getSourceStatsAll(),
@@ -4265,6 +4303,20 @@ ${String(params.summary)}
4265
4303
  content: entitlementMd,
4266
4304
  source: "solana-trader:entitlements-digest"
4267
4305
  });
4306
+ context.bootstrapFiles.push({
4307
+ name: "live-queries.md",
4308
+ path: "live-queries.md",
4309
+ content: "# Live alpha status queries \u2014 always call the tool\n\nWhen the user asks anything about CURRENT alpha activity, call the matching tool **on this turn**. Do NOT answer from memory, heartbeat history, journal logs, or earlier turn context \u2014 alpha signals are not journalled per-message, so 'I don't see any / 0' is wrong by default.\n\n## Routing\n\n| User question shape | Tool(s) to call | Key fields in response |\n|---|---|---|\n| how many alpha signals / are we getting alpha / activity since gateway start | `solana_alpha_signals` (unseen:false) | stats.messageCount, stats.lifetimeUptimeSeconds, stats.lastEventTs, subscribed, bufferSize |\n| is alpha connected / healthy / stream status | `solana_alpha_signals` (unseen:false) | subscribed, stats.reconnectAttempt, stats.unhealthyStreak, stats.circuitBackoff, stats.lastEventTs |\n| how many alpha signals in last hour / today / this week / since <day> | `solana_alpha_signals` (live state) **AND** `solana_alpha_history` (days=\u2026 or compute from window) | live: stats.messageCount + bufferSize; historical: pings[] in window |\n| any alpha on token <X> (recent / historical) | `solana_alpha_signals` for in-buffer signals, then `solana_alpha_history` (tokenAddress=X, days=N) for older | both responses merged by ts |\n| what alpha sources / channels are active | `solana_alpha_sources` | sources[] (name, type, count, avgScore) |\n| latest signals / new signals | `solana_alpha_signals` (unseen:false) | signals[] sorted newest last |\n\n## Field meanings \u2014 IMPORTANT\n\n`solana_alpha_signals` returns `stats` with both lifetime and current-WS fields. Use LIFETIME fields for user-facing answers:\n\n- `stats.messageCount` = **lifetime** total alpha_signal messages received since the gateway process started. Survives plugin re-registers and WS reconnects. **This is the headline number.**\n- `stats.lifetimeUptimeSeconds` = seconds since the first WS open in this process.\n- `stats.lastEventTs` = wall-clock ms of the most recent signal (lifetime).\n- `stats.firstConnectedAt` = wall-clock ms of the first WS open in this process.\n\nDebug-only (do NOT report these as 'totals' \u2014 they reset on every WS reconnect / plugin re-register):\n\n- `stats.currentWsMessageCount` \u2014 messages since the current WS opened.\n- `stats.uptimeSeconds` \u2014 current WS uptime.\n- `stats.connectedAt` \u2014 current WS open ts.\n\n## When to also call `solana_alpha_history`\n\nCall `solana_alpha_history` in addition to `solana_alpha_signals` whenever the user's window pre-dates the current gateway-process lifetime (e.g. `stats.lifetimeUptimeSeconds` is shorter than the asked window, or `stats.messageCount` is 0 because the gateway just restarted). Tier=enterprise returns up to 200 pings, up to ~1 year back.\n\n## Reply template (live-only)\n\n```\nLive alpha state:\n- subscribed: <bool>\n- lifetime: <messageCount> messages over <lifetimeUptimeSeconds>s (\u2248 <rate>/min) since gateway start\n- last signal: <Xs/Xm ago> (lastEventTs)\n- buffer (deduped): <bufferSize>\n- reconnects: <reconnectAttempt>, unhealthy streak: <unhealthyStreak>\n```\n\n## Reply template (with historical window, e.g. 'last hour')\n\n```\nAlpha activity (<window>):\n- live (gateway lifetime): <messageCount> messages over <lifetimeUptimeSeconds>s\n- historical (`solana_alpha_history` days=<N>): <pings.length> pings\n- combined unique within <window>: <merged count>\n- subscribed: <bool>, last signal: <ago>\n```\n\nThis routing overrides any heartbeat-cycle 'minimal calls' cap: ad-hoc user status questions are NOT subject to per-cycle envelopes.\n",
4310
+ source: "solana-trader:live-queries"
4311
+ });
4312
+ if (__solanaTraderStatusQueriesMd) {
4313
+ context.bootstrapFiles.push({
4314
+ name: "STATUS_QUERIES.md",
4315
+ path: "STATUS_QUERIES.md",
4316
+ content: __solanaTraderStatusQueriesMd,
4317
+ source: "solana-trader:status-queries"
4318
+ });
4319
+ }
4268
4320
  api.logger.info(`[solana-trader] Bootstrap: injected ${context.bootstrapFiles.length} files for agent ${bootAgentId}`);
4269
4321
  },
4270
4322
  {
@@ -2,7 +2,7 @@ import {
2
2
  ALPHA_INGESTION_STALE_MS,
3
3
  ALPHA_STALE_GRACE_AFTER_CONNECT_MS,
4
4
  AlphaStreamManager
5
- } from "../chunk-ZUY36F4Q.js";
5
+ } from "../chunk-GX4HA22L.js";
6
6
  export {
7
7
  ALPHA_INGESTION_STALE_MS,
8
8
  ALPHA_STALE_GRACE_AFTER_CONNECT_MS,
@@ -0,0 +1,103 @@
1
+ # Live status queries — always call the tool, never answer from memory
2
+
3
+ When the user asks any question about **current alpha stream state**, you MUST call the matching plugin tool **on this turn**, then summarize what the tool returned. Do NOT answer from memory, heartbeat history, journal logs, log snippets, or earlier conversation context: alpha counts grow second-by-second and stale answers (especially "none" / "zero") are wrong by default.
4
+
5
+ ## Why this rule exists
6
+
7
+ The plugin does NOT log every received alpha signal to the journal (that would be noisy at sustained throughput). Signal counts and the buffer of recent signals live **only inside the running plugin process** and are exposed via these tools. If you don't call the tool, you have no data — you only have the absence of log lines, which is not the same thing.
8
+
9
+ The heartbeat history (`memory/<date>.md`, `MEMORY.md`) only captures signals the agent **personally evaluated** during a heartbeat cycle. It is NOT a record of what the WebSocket received. Using heartbeat history to answer "how many signals" gives a count that is typically much smaller than the real number (because most signals never reach a cycle where the agent processes them).
10
+
11
+ ## Alpha question → tool routing
12
+
13
+ When the user's question matches a row in column 1, call the tool(s) in column 2 (always on this turn, before replying), read the response, and answer from that data.
14
+
15
+ | User question (or similar) | Call this tool | What to report back |
16
+ |---|---|---|
17
+ | "how many alpha signals have we gotten?" / "are we getting alpha?" / "anything from alpha lately?" | `solana_alpha_signals` (with `unseen: false`) | `stats.messageCount` (LIFETIME — survives re-registers/reconnects), `stats.lifetimeUptimeSeconds`, `stats.lastEventTs`, `subscribed`, `bufferSize` |
18
+ | "what's the alpha stream doing right now?" / "is alpha connected?" / "alpha health?" | `solana_alpha_signals` (with `unseen: false`) | `subscribed`, `stats.reconnectAttempt`, `stats.unhealthyStreak`, `stats.circuitBackoff`, `stats.lastEventTs` (interpret as "Xs ago") |
19
+ | "how many alpha signals in the last hour / today / this week / since Monday?" | `solana_alpha_signals` (live) **AND** `solana_alpha_history` (`days=` covering the window) | Live: `stats.messageCount` since gateway start + signals in `signals[]` within the window. Historical: pings in window from REST. Report both. |
20
+ | "any alpha on `<token>` recently / in the last 24h?" | `solana_alpha_signals` (filter `signals[]` by `tokenAddress`/`tokenSymbol`) **AND** `solana_alpha_history` (`tokenAddress=<addr>&days=N`) | Merge live + historical signals for that token by timestamp. |
21
+ | "what alpha sources are active?" / "which channels are sending signals?" | `solana_alpha_sources` | `sources` array — name, type, count, avgScore per channel |
22
+ | "show me the latest alpha signals" / "new signals?" | `solana_alpha_signals` (with `unseen: false` for full buffer, `unseen: true` only if you intend to consume) | Up to N signals: token, source, kind, signalStage, marketCap, systemScore, ts |
23
+
24
+ ## `stats` fields — what to use vs. what to ignore
25
+
26
+ `solana_alpha_signals` returns a `stats` object with both **lifetime** and **per-current-WS** fields. **Always quote the lifetime fields to the user.**
27
+
28
+ USE these for user-facing answers (stable across plugin re-registers and WS reconnects):
29
+
30
+ - `stats.messageCount` — **lifetime** total alpha_signal messages received since the gateway process started.
31
+ - `stats.lifetimeUptimeSeconds` — seconds since the first WebSocket open in this gateway process.
32
+ - `stats.lastEventTs` — wall-clock ms of the most recent signal (lifetime).
33
+ - `stats.firstConnectedAt` — wall-clock ms of the first WS open in this process.
34
+
35
+ DO NOT use as "totals" (they reset on every WS reconnect / plugin re-register, which happens every agent turn — so the numbers are misleading as standalone answers):
36
+
37
+ - `stats.currentWsMessageCount` — messages since the CURRENT WS opened. Useful only for debugging "why is the WS cycling?".
38
+ - `stats.uptimeSeconds` — current WS uptime.
39
+ - `stats.connectedAt` — current WS open ts.
40
+
41
+ ## When to also call `solana_alpha_history`
42
+
43
+ The live tool gives you "messages since this gateway process started" and a buffer of ~200 deduped signals. For windows that exceed those:
44
+
45
+ - The asked window predates `stats.firstConnectedAt` (e.g. user asks "last 24h" but gateway started 2h ago).
46
+ - The user asks about a specific date or named window ("yesterday", "since Monday", "last week").
47
+ - The buffer is empty (e.g. just after a gateway restart) but the user is asking about a real time range.
48
+
49
+ …ALSO call `solana_alpha_history` (`days=N` covering the window) and combine the two responses. Tier=enterprise → up to 200 results back ~1 year.
50
+
51
+ ## How to answer (template — live-only)
52
+
53
+ After the tool returns, structure your reply like this so the user can verify you queried live data:
54
+
55
+ ```
56
+ Live alpha state (as of <now>):
57
+ - subscribed: <subscribed>
58
+ - lifetime: <stats.messageCount> messages over <stats.lifetimeUptimeSeconds>s (≈ <rate>/min) since gateway start
59
+ - last signal: <stats.lastEventTs as "Xs/Xm ago">
60
+ - buffer (deduped, ≤200): <bufferSize>
61
+ - reconnects: <stats.reconnectAttempt>, unhealthy streak: <stats.unhealthyStreak>
62
+
63
+ Top sources (from `solana_alpha_sources`):
64
+ - <sourceName> (<sourceType>): <count> signals, avg score <avgScore>
65
+
66
+ Sample of latest signals (newest first):
67
+ - <tokenSymbol> (<tokenName>) — <kind>/<signalStage>, MC $<marketCap>, score <systemScore>, from <sourceName>, <ts ago>
68
+ ```
69
+
70
+ ## How to answer (template — when window > gateway lifetime)
71
+
72
+ ```
73
+ Alpha activity (<window>):
74
+ - live (gateway lifetime, <lifetimeUptimeSeconds>s): <messageCount> messages, currently <bufferSize> in buffer
75
+ - historical (`solana_alpha_history` days=<N>): <pings.length> pings in window
76
+ - combined unique: <merged count>
77
+ - subscribed: <bool>, last signal: <ago>
78
+
79
+ Top historical sources / tokens: …
80
+ ```
81
+
82
+ ## Common mistakes to avoid
83
+
84
+ - **Don't answer "zero" or "none" without calling the tool.** "I don't see any in the logs / heartbeat history" is wrong — signals are NOT logged per-message and heartbeat history only captures what the agent itself touched.
85
+ - **Don't reuse a count from earlier in the conversation.** The buffer is continuously updated; quote only freshly-fetched numbers.
86
+ - **Don't quote `currentWsMessageCount` or `uptimeSeconds` as "totals".** Those reset every time you (or anyone) interacts with the agent, because each interaction re-registers the plugin. Always use `stats.messageCount` and `stats.lifetimeUptimeSeconds` for user-facing answers.
87
+ - **Don't mix heartbeat history with live tool data without flagging it.** If the user asks "how many in the last hour" and you can only see live state, say so and call `solana_alpha_history` to fill the window.
88
+ - **Don't claim "the stream is offline" from log gaps alone.** Check `subscribed` and `stats.lastEventTs` — a stream can be healthy with low-traffic minutes.
89
+ - **Don't paste raw JSON to the user.** Summarize per the templates above; offer raw JSON only if asked.
90
+
91
+ ## When the user does NOT need a tool call
92
+
93
+ These are NOT live-state queries; answer from your knowledge:
94
+
95
+ - "how does the alpha stream work?" (explain the design from `skills/solana-trader/refs/alpha-signals.md` if available)
96
+ - "what's a `ca_drop`?" (definition)
97
+ - "which alpha sources are premium?" (static info from refs)
98
+
99
+ ## Related
100
+
101
+ - `HEARTBEAT.md` — per-heartbeat cycle envelope (separate from ad-hoc Q&A).
102
+ - `skills/solana-trader/refs/alpha-signals.md` — full alpha pipeline reference.
103
+ - Tool catalog: `solana_alpha_subscribe`, `solana_alpha_unsubscribe`, `solana_alpha_signals`, `solana_alpha_sources`, `solana_alpha_history`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-traderclaw",
3
- "version": "1.0.145",
3
+ "version": "1.0.147",
4
4
  "description": "TraderClaw V1-Upgraded — Solana trading for OpenClaw with intelligence lab, tool envelopes, prompt scrubbing, read-only X social intel, and split skill docs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -127,3 +127,21 @@ These are permanent directives. They apply every session, every cycle, without e
127
127
  4. **Always check kill switch before new entries.** Call `solana_killswitch_status()` before any new trade execution. If active, halt immediately — no exceptions, no overrides.
128
128
 
129
129
  5. **Always report every cycle.** Never run a silent heartbeat. Every cycle produces a report: what you scanned, what you found, what you did, what you skipped and why. Crypto is 24/7. Every cycle reports.
130
+
131
+ ## Live status questions → ALWAYS call the tool
132
+
133
+ When the user asks about the **current** state of the alpha stream (counts, sources, recent signals, subscription health, or any time window), you MUST call the matching plugin tool **on the same turn** before replying. Do not answer "zero / none / I don't see anything" from memory, heartbeat history, journal logs, or log impressions — signals are not logged per-message; they live only inside the running plugin (live state) and the orchestrator REST (historical) and are surfaced via tools.
134
+
135
+ See `STATUS_QUERIES.md` (auto-injected by the plugin at every register) for the full question → tool routing table, field semantics (lifetime vs. current-WS), and the standard answer template. The short version:
136
+
137
+ | Question shape | Tool(s) to call |
138
+ |---|---|
139
+ | "how many alpha signals / are we getting alpha?" | `solana_alpha_signals` (`unseen: false`) — quote `stats.messageCount` (LIFETIME, survives re-registers) and `stats.lifetimeUptimeSeconds` |
140
+ | "is alpha connected / healthy?" | `solana_alpha_signals` (`unseen: false`) — read `subscribed`, `stats.lastEventTs`, `stats.reconnectAttempt`, `stats.unhealthyStreak`, `stats.circuitBackoff` |
141
+ | "how many in last hour / today / since `<day>`?" / "any alpha on `<token>` in 24h?" | `solana_alpha_signals` (live) **AND** `solana_alpha_history` (`days=N` covering the window, `tokenAddress=` when filtering) |
142
+ | "which alpha sources / channels?" | `solana_alpha_sources` |
143
+ | "latest / new alpha signals?" | `solana_alpha_signals` (with or without `unseen: true` depending on intent) |
144
+
145
+ Field reminder: use `stats.messageCount` and `stats.lifetimeUptimeSeconds` as the headline numbers. Do NOT quote `stats.currentWsMessageCount` or `stats.uptimeSeconds` as "totals" — they reset on every plugin re-register (every agent turn / Telegram message).
146
+
147
+ This rule applies to all sessions — direct chats, Telegram, anywhere the user asks. It overrides the heartbeat-cycle envelope: ad-hoc live-state questions are NOT subject to the "minimal per-cycle calls" cap.