solana-traderclaw 1.0.83 → 1.0.84

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.
@@ -29,7 +29,7 @@
29
29
  agentId: "main",
30
30
  sessionTarget: "isolated",
31
31
  delivery: { mode: "none" },
32
- message: "CRON_JOB: strategy_evolution\n\nStep 1: Call solana_journal_summary to get aggregate performance stats (win rate, avg PnL, trade count). If fewer than 20 closed trades since the last strategy evolution, skip weight updates but still run pattern detection.\n\nStep 2: Call solana_memory_search for 'strategy_evolution' — find last 3 evolution cycle results. Call solana_memory_search for 'strategy_drift_warning' — find drift warnings since last evolution. Call solana_memory_search for 'pre_trade_rationale' — recent decision patterns.\n\nStep 3: Run Recurring Pattern Detection — search for learning_entry tags, group by area, check linked chains (3+ = confirmed pattern), investigate drift warnings.\n\nStep 4: Call solana_strategy_state to read current feature weights.\n\nStep 5: Call solana_trades to get recent closed trades. Apply ADL checks (direction consistency, weight velocity, reversion check).\n\nStep 6: Compute proposed weight changes. Score each with VFM (Frequency + Failure Reduction + Self-Cost). Only apply changes scoring >= 3/5.\n\nStep 7: Verify guardrails: maxDeltaOk, sumWeightsOk, minTradesOk, floorCapOk. If all pass, call solana_strategy_update with incremented version.\n\nStep 8: Run Named Pattern Recognition — search for winning trade clusters, catalog new patterns, evolve existing ones.\n\nStep 9: Evaluate discovery filter performance. Log all results via solana_memory_write with tags: strategy_evolution, vfm_scorecard, pattern_detection, named_pattern.\n\nFORMATTING RULES:\n- Every token reference MUST use SYMBOL (full_CA) format.\n- Do not execute trades. Do not ask questions.",
32
+ message: "CRON_JOB: strategy_evolution\n\nStep 1: Call solana_journal_summary to get aggregate performance stats (win rate, avg PnL, trade count). If fewer than 10 closed trades since the last strategy evolution, skip weight updates but still run pattern detection.\n\nStep 2: Call solana_memory_search for 'strategy_evolution' — find last 3 evolution cycle results. Call solana_memory_search for 'strategy_drift_warning' — find drift warnings since last evolution. Call solana_memory_search for 'pre_trade_rationale' — recent decision patterns.\n\nStep 3: Run Recurring Pattern Detection — search for learning_entry tags, group by area, check linked chains (3+ = confirmed pattern), investigate drift warnings.\n\nStep 4: Call solana_strategy_state to read current feature weights.\n\nStep 5: Call solana_trades to get recent closed trades. Apply ADL checks (direction consistency, weight velocity, reversion check).\n\nStep 6: Compute proposed weight changes. Score each with VFM (Frequency + Failure Reduction + Self-Cost). Only apply changes scoring >= 3/5.\n\nStep 7: Verify guardrails: maxDeltaOk, sumWeightsOk, minTradesOk, floorCapOk. If all pass, call solana_strategy_update with incremented version.\n\nStep 8: Run Named Pattern Recognition — search for winning trade clusters, catalog new patterns, evolve existing ones.\n\nStep 9: Evaluate discovery filter performance. Log all results via solana_memory_write with tags: strategy_evolution, vfm_scorecard, pattern_detection, named_pattern.\n\nFORMATTING RULES:\n- Every token reference MUST use SYMBOL (full_CA) format.\n- Do not execute trades. Do not ask questions.",
33
33
  enabled: true
34
34
  },
35
35
  {
@@ -53,8 +53,14 @@ var IntelligenceLab = class {
53
53
  } else {
54
54
  candidates.push(full);
55
55
  }
56
- if (candidates.length > 5e3) {
57
- candidates.splice(0, candidates.length - 5e3);
56
+ const MAX_CANDIDATES = 200;
57
+ if (candidates.length > MAX_CANDIDATES) {
58
+ const labeled = candidates.filter((c) => c.outcome);
59
+ const unlabeled = candidates.filter((c) => !c.outcome);
60
+ const keepUnlabeled = Math.max(0, MAX_CANDIDATES - labeled.length);
61
+ const trimmed = [...labeled, ...unlabeled.slice(-keepUnlabeled)];
62
+ candidates.length = 0;
63
+ candidates.push(...trimmed);
58
64
  }
59
65
  writeJsonFile(this.candidatesFile(), candidates);
60
66
  return existing >= 0 ? candidates[existing] : full;
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  } from "./chunk-PIZZXNMQ.js";
22
22
  import {
23
23
  IntelligenceLab
24
- } from "./chunk-C24QA3MQ.js";
24
+ } from "./chunk-FBS5FGW2.js";
25
25
  import {
26
26
  scrubUntrustedText
27
27
  } from "./chunk-AI6MTHUN.js";
@@ -1142,6 +1142,33 @@ var solanaTraderPlugin = {
1142
1142
  fs.writeFileSync(filePath, entries.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf-8");
1143
1143
  return entries.length;
1144
1144
  };
1145
+ const volatileStateKeys = /* @__PURE__ */ new Set(["lastHeartbeat", "lastHeartbeatAt"]);
1146
+ const renderSummaryBullets = (key, val) => {
1147
+ if (key === "lastCycleSummary" && val && typeof val === "object") {
1148
+ const s = val;
1149
+ const bullets = ["## Last Cycle Summary", ""];
1150
+ if (s.capitalSol !== void 0) bullets.push(`- **Capital SOL:** ${s.capitalSol}`);
1151
+ if (s.openPositions !== void 0) bullets.push(`- **Open Positions:** ${s.openPositions}`);
1152
+ if (s.signalsProcessed !== void 0) bullets.push(`- **Signals Processed:** ${s.signalsProcessed}`);
1153
+ if (s.topWatch) bullets.push(`- **Top Watch:** ${s.topWatch}`);
1154
+ if (s.xApiStatus) bullets.push(`- **X API Status:** ${s.xApiStatus}`);
1155
+ if (s.bitqueryStatus) bullets.push(`- **Bitquery Status:** ${s.bitqueryStatus}`);
1156
+ const rendered = Object.keys(s).filter((k) => !["capitalSol", "openPositions", "signalsProcessed", "topWatch", "xApiStatus", "bitqueryStatus"].includes(k));
1157
+ for (const rk of rendered.slice(0, 5)) {
1158
+ const rv = s[rk];
1159
+ const disp = typeof rv === "object" ? JSON.stringify(rv) : String(rv);
1160
+ bullets.push(`- **${rk}:** ${disp.length > 120 ? disp.slice(0, 120) + "\u2026" : disp}`);
1161
+ }
1162
+ bullets.push("");
1163
+ return bullets;
1164
+ }
1165
+ return [];
1166
+ };
1167
+ const formatStateValue = (val) => {
1168
+ if (val === null || val === void 0) return String(val);
1169
+ if (typeof val === "object") return JSON.stringify(val);
1170
+ return String(val);
1171
+ };
1145
1172
  const generateMemoryMd = (aid, stateObj) => {
1146
1173
  const lines = [
1147
1174
  `# ${aid} \u2014 Durable Memory`,
@@ -1160,7 +1187,7 @@ var solanaTraderPlugin = {
1160
1187
  if (state.walletId) identity.push(`- **Wallet:** ${state.walletId}`);
1161
1188
  if (state.mode) identity.push(`- **Mode:** ${state.mode}`);
1162
1189
  if (state.strategyVersion) identity.push(`- **Strategy Version:** ${state.strategyVersion}`);
1163
- if (state.regime) identity.push(`- **Regime:** ${state.regime}`);
1190
+ if (state.regime) identity.push(`- **Regime:** ${formatStateValue(state.regime)}`);
1164
1191
  if (state.maxPositions) identity.push(`- **Max Positions:** ${state.maxPositions}`);
1165
1192
  if (state.maxPositionSizeSol) identity.push(`- **Max Position Size:** ${state.maxPositionSizeSol} SOL`);
1166
1193
  if (identity.length > 0) {
@@ -1192,13 +1219,24 @@ var solanaTraderPlugin = {
1192
1219
  const rc = state.regimeCanary;
1193
1220
  lines.push("## Regime Canary", "", `- **Regime:** ${rc.regime || "unknown"}`, `- **Detected At:** ${rc.detectedAt || "unknown"}`, "");
1194
1221
  }
1195
- const excludeKeys = /* @__PURE__ */ new Set(["tier", "walletId", "mode", "strategyVersion", "regime", "maxPositions", "maxPositionSizeSol", "defenseMode", "killSwitchActive", "watchlist", "permanentLearnings", "regimeCanary"]);
1196
- const otherKeys = Object.keys(state).filter((k) => !excludeKeys.has(k));
1197
- if (otherKeys.length > 0) {
1222
+ const structuredKeys = /* @__PURE__ */ new Set(["tier", "walletId", "mode", "strategyVersion", "regime", "maxPositions", "maxPositionSizeSol", "defenseMode", "killSwitchActive", "watchlist", "permanentLearnings", "regimeCanary"]);
1223
+ const summaryKeys = /* @__PURE__ */ new Set(["lastCycleSummary"]);
1224
+ const otherKeys = Object.keys(state).filter((k) => !structuredKeys.has(k) && !volatileStateKeys.has(k));
1225
+ const summaryRendered = [];
1226
+ const remainingKeys = [];
1227
+ for (const key of otherKeys) {
1228
+ if (summaryKeys.has(key)) {
1229
+ summaryRendered.push(...renderSummaryBullets(key, state[key]));
1230
+ } else {
1231
+ remainingKeys.push(key);
1232
+ }
1233
+ }
1234
+ if (summaryRendered.length > 0) lines.push(...summaryRendered);
1235
+ if (remainingKeys.length > 0) {
1198
1236
  lines.push("## Other State Keys", "");
1199
- for (const key of otherKeys.slice(0, 30)) {
1237
+ for (const key of remainingKeys.slice(0, 30)) {
1200
1238
  const val = state[key];
1201
- const display = typeof val === "object" ? JSON.stringify(val) : String(val);
1239
+ const display = formatStateValue(val);
1202
1240
  lines.push(`- **${key}:** ${display.length > 200 ? display.slice(0, 200) + "\u2026" : display}`);
1203
1241
  }
1204
1242
  lines.push("");
@@ -2412,6 +2450,29 @@ ${notes}
2412
2450
  const capitalOnly = failed === 1 && steps.find((s) => !s.ok)?.step === "solana_capital_status";
2413
2451
  startupGateState = { ok: allOk, ts: Date.now(), steps };
2414
2452
  const k = config.apiKey && String(config.apiKey).trim() || null;
2453
+ if (allOk || capitalOnly) {
2454
+ try {
2455
+ const effectiveAgentId = config.agentId || "main";
2456
+ const stateFilePath = path.join(stateDir, `${effectiveAgentId}.json`);
2457
+ const existing = readJsonFile(stateFilePath);
2458
+ const existingState = existing?.state && typeof existing.state === "object" ? existing.state : {};
2459
+ const alphaStep = steps.find((s) => s.step === "solana_alpha_subscribe" && s.ok);
2460
+ const tier = alphaStep?.details?.tier;
2461
+ const seedFields = {};
2462
+ if (!existingState.walletId && walletId) seedFields.walletId = walletId;
2463
+ if (!existingState.tier && tier) seedFields.tier = tier;
2464
+ if (!existingState.mode) seedFields.mode = "HARDENED";
2465
+ if (!existingState.strategyVersion) seedFields.strategyVersion = "1.0.0";
2466
+ if (Object.keys(seedFields).length > 0) {
2467
+ const merged = { ...existingState, ...seedFields };
2468
+ writeJsonFile(stateFilePath, { agentId: effectiveAgentId, state: merged, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
2469
+ writeMemoryMd(effectiveAgentId, merged);
2470
+ api.logger.info(`[solana-trader] Seeded identity fields into state: ${Object.keys(seedFields).join(", ")}`);
2471
+ }
2472
+ } catch (seedErr) {
2473
+ api.logger.warn(`[solana-trader] Failed to seed identity fields: ${seedErr instanceof Error ? seedErr.message : String(seedErr)}`);
2474
+ }
2475
+ }
2415
2476
  return {
2416
2477
  ok: allOk,
2417
2478
  ts: Date.now(),
@@ -2561,6 +2622,49 @@ ${notes}
2561
2622
  parameters: Type.Object({}),
2562
2623
  execute: wrapExecute("solana_system_status", async () => get("/api/system/status"))
2563
2624
  });
2625
+ api.registerTool({
2626
+ name: "solana_storage_status",
2627
+ description: "Check local VPS disk usage and plugin storage health. Reports disk free/total, daily log count + size, candidates count, session directory size. Use in heartbeat Step 0 or risk_audit to detect disk pressure before it causes silent failures.",
2628
+ parameters: Type.Object({}),
2629
+ execute: wrapExecute("solana_storage_status", async () => {
2630
+ const os = await import("os");
2631
+ const result = {};
2632
+ try {
2633
+ const stat = fs.statfsSync(workspaceRoot);
2634
+ const totalGB = Math.round(stat.bsize * stat.blocks / 1024 ** 3 * 100) / 100;
2635
+ const freeGB = Math.round(stat.bsize * stat.bavail / 1024 ** 3 * 100) / 100;
2636
+ const usedPct = Math.round((1 - freeGB / totalGB) * 100);
2637
+ result.disk = { totalGB, freeGB, usedPct, warning: usedPct > 85, critical: usedPct > 95 };
2638
+ } catch {
2639
+ result.disk = { error: "unable to read disk stats" };
2640
+ }
2641
+ try {
2642
+ const logFiles = fs.readdirSync(memoryDir).filter((f) => f.endsWith(".md"));
2643
+ let totalBytes = 0;
2644
+ for (const f of logFiles) {
2645
+ try {
2646
+ totalBytes += fs.statSync(path.join(memoryDir, f)).size;
2647
+ } catch {
2648
+ }
2649
+ }
2650
+ result.dailyLogs = { count: logFiles.length, totalKB: Math.round(totalBytes / 1024) };
2651
+ } catch {
2652
+ result.dailyLogs = { count: 0, totalKB: 0 };
2653
+ }
2654
+ try {
2655
+ const candidatesPath = intelligenceLab.exportDataset("json");
2656
+ const parsed = JSON.parse(candidatesPath);
2657
+ const labeled = Array.isArray(parsed) ? parsed.filter((c) => c.outcome).length : 0;
2658
+ const total = Array.isArray(parsed) ? parsed.length : 0;
2659
+ result.candidates = { total, labeled, unlabeled: total - labeled };
2660
+ } catch {
2661
+ result.candidates = { total: 0, labeled: 0, unlabeled: 0 };
2662
+ }
2663
+ result.memoryRAM = { rssKB: Math.round(process.memoryUsage().rss / 1024), heapUsedKB: Math.round(process.memoryUsage().heapUsed / 1024) };
2664
+ result.uptime = { systemHours: Math.round(os.uptime() / 3600 * 10) / 10, processHours: Math.round(process.uptime() / 3600 * 10) / 10 };
2665
+ return result;
2666
+ })
2667
+ });
2564
2668
  api.registerTool({
2565
2669
  name: "solana_startup_gate",
2566
2670
  description: "Run the mandatory startup sequence and return deterministic pass/fail results per step. Optionally auto-fixes gateway credentials if gatewayBaseUrl and gatewayToken are present in plugin config. On full pass, includes welcomeMessage. If the only failed step is solana_capital_status (e.g. capital API error), still includes welcomeMessage so the user gets onboarding text; check welcomeNote in that case.",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IntelligenceLab
3
- } from "../chunk-C24QA3MQ.js";
3
+ } from "../chunk-FBS5FGW2.js";
4
4
  import "../chunk-JO3BXAUQ.js";
5
5
  export {
6
6
  IntelligenceLab
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solana-traderclaw",
3
- "version": "1.0.83",
3
+ "version": "1.0.84",
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",
@@ -215,6 +215,8 @@ HARDENED range: +100–300%. DEGEN range: +200–500%. `percent` = price increas
215
215
 
216
216
  **Pre-trade journal FIRST** — call `solana_memory_write` with tag `pre_trade_rationale` BEFORE executing. Also call `solana_decision_log` to record the decision with confidence, sizing rationale, and risk factors.
217
217
 
218
+ **Source attribution (mandatory):** The `pre_trade_rationale` memory entry MUST include `source: "<how you found this token>"` — one of: `alpha_signal:<source_name>`, `scan_launches`, `scan_hot_pairs`, `bitquery_subscription`, `manual`, `watchlist`. This is required for source trust scoring and strategy evolution to correlate wins/losses with discovery channels.
219
+
218
220
  **Prior history check (mandatory):**
219
221
  ```
220
222
  solana_memory_by_token({ tokenAddress: "CA" })
@@ -308,22 +310,31 @@ Execute exits via `solana_trade_execute` with `side: "sell"`.
308
310
 
309
311
  Partial exits → "🔴 PARTIAL EXIT (50%): SYMBOL (CA)"
310
312
 
311
- **Post-exit mandatory actions:**
313
+ **Post-exit mandatory actions (ALL required — skipping any is a critical violation):**
312
314
 
313
315
  1. Call `solana_trade_review` for each closed position.
314
316
 
315
- 2. Label the outcome for intelligence lab learning:
317
+ 2. **LABEL THE OUTCOME CRITICAL, DO NOT SKIP:**
316
318
  ```
317
319
  solana_candidate_label_outcome({ id: "CA", outcome: "win|loss|skip|dead_money", pnlPct: X.XX, holdingHours: H })
318
320
  ```
319
- This is how the intelligence lab learns. Every exit MUST be labeled.
321
+ The intelligence lab CANNOT learn without labeled outcomes. An unlabeled exit is wasted data. If you skip this call, the entire learning pipeline is broken — strategy evolution, model evaluation, and replay all depend on labeled candidates.
322
+
323
+ 3. **LEARNING ENTRY — REQUIRED after every loss or dead_money exit:**
324
+ ```
325
+ solana_memory_write({
326
+ content: "LEARNING ENTRY: LRN-YYYYMMDD-NNN\nPriority: P2\nArea: <area_tag>\nWHAT HAPPENED: <1 sentence>\nWHY IT WENT WRONG: <root cause>\nEVIDENCE: token CA, entry price, exit price, hold time\nSUGGESTED ADJUSTMENT: <what to change>",
327
+ tags: ["learning_entry", "learning_entry_<area>"]
328
+ })
329
+ ```
330
+ Losses without learning entries are the #1 reason the strategy fails to evolve. Check `refs/review-learning.md` for the full entry format and area tags.
320
331
 
321
- 3. Unsubscribe from Bitquery stream for the exited token:
332
+ 4. Unsubscribe from Bitquery stream for the exited token:
322
333
  ```
323
334
  solana_bitquery_unsubscribe({ subscriptionId: "<id>" })
324
335
  ```
325
336
 
326
- 4. If this was an alpha-sourced trade, check and record source accuracy:
337
+ 5. If this was an alpha-sourced trade, check and record source accuracy:
327
338
  ```
328
339
  solana_alpha_history({ tokenAddress: "CA", limit: 5 })
329
340
  ```
@@ -339,6 +350,11 @@ Log the source's accuracy via `solana_memory_write` with tag `source_reputation`
339
350
  - `solana_team_bulletin_post` with tag `position_update` — post current portfolio state
340
351
  - `solana_context_snapshot_write` — write portfolio world-view for bootstrap injection
341
352
 
353
+ **Self-check before completing Step 8:**
354
+ - Did you exit any positions this cycle? If yes: did you call `solana_candidate_label_outcome` for EACH exit? If not, go back and call it now.
355
+ - Did any exit result in a loss or dead_money? If yes: did you write a `learning_entry` via `solana_memory_write`? If not, write one now.
356
+ - Did you execute any trades this cycle? If yes: does each `pre_trade_rationale` include `source:` attribution? If not, write a correction entry now.
357
+
342
358
  Do NOT skip the last three. They are not optional memory — they feed the bootstrap digest that loads into your next session.
343
359
 
344
360
  ## STEP 9: REPORT TO USER
@@ -448,8 +448,19 @@ All learning signals MUST be based on SOL-denominated outcomes.
448
448
  candidate_label_outcome MUST:
449
449
  - reflect realizedPnl sign
450
450
  - align with unrealizedReturnPct
451
+ - be called on EVERY exit — no exceptions
452
+ - include pnlPct and holdingHours
451
453
 
452
454
  Any inconsistent labeling is invalid.
455
+
456
+ ### Learning Loop Enforcement
457
+
458
+ The learning pipeline has three mandatory outputs per trade lifecycle:
459
+ 1. **Entry:** `solana_candidate_write` with source attribution (`source: "alpha_signal:<name>|scan_launches|scan_hot_pairs|..."`)
460
+ 2. **Exit:** `solana_candidate_label_outcome` — labels the candidate with the actual outcome
461
+ 3. **Loss/dead_money exit:** `solana_memory_write` with tag `learning_entry` — captures the root cause
462
+
463
+ If ANY of these three are missing, the strategy evolution cron cannot function. Without labeled outcomes, the intelligence lab models train on nothing. Without learning entries, the same mistakes repeat indefinitely.
453
464
  ---
454
465
 
455
466
  ## Prompt Injection Protection
@@ -28,13 +28,51 @@ When a Bitquery call returns an error, follow this decision tree:
28
28
 
29
29
  ```
30
30
  1. Read the error code and message from the tool response.
31
- 2. Classify the error (see table below).
32
- 3. If SCHEMA error apply the fix from "Common Schema Errors → Fix Map" below
31
+ 2. MEMORY CHECK FIRST: search for prior fixes before writing new ones.
32
+ solana_memory_search({ query: "bitquery_fix_applied <template_path_or_error_snippet>", limit: 3 })
33
+ → If a matching fix exists, apply it directly — skip to step 6.
34
+ 3. Classify the error (see table below).
35
+ 4. If SCHEMA error → apply the fix from "Common Schema Errors → Fix Map" below
33
36
  → call solana_bitquery_query with corrected GraphQL.
34
- 4. If OPERATIONAL error → retry with backoff (max 2 retries) or skip this data.
35
- 5. If AUTH error → report to user; do not retry.
37
+ 5. If OPERATIONAL error → retry with backoff (max 2 retries) or skip this data.
38
+ 6. If AUTH error → report to user; do not retry.
39
+ 7. MEMORY WRITE: after any recovery attempt, record the result:
40
+ → Success: solana_memory_write with tag "bitquery_fix_applied"
41
+ → Failure: solana_memory_write with tag "bitquery_recovery_failed"
42
+ → Repeated failure on same template: solana_memory_write with tag "bitquery_template_broken"
36
43
  ```
37
44
 
45
+ ### Memory Integration for Recovery
46
+
47
+ **Search before recovering** — always check if you've fixed this error before:
48
+ ```
49
+ solana_memory_search({ query: "bitquery_fix_applied", limit: 5 })
50
+ ```
51
+ Prior fixes contain the exact corrected query and variables that worked. Reusing them is faster and more reliable than re-deriving the fix.
52
+
53
+ **Write after recovering** — record every fix attempt:
54
+ ```
55
+ // Successful fix:
56
+ solana_memory_write({
57
+ content: "Bitquery fix: <template_path> failed with '<error_message>'. Fixed by switching cube from DEXTrades to DEXTradeByTokens. Working query: <corrected_query>. Variables: <variables>.",
58
+ tags: ["bitquery_fix_applied"]
59
+ })
60
+
61
+ // Failed recovery:
62
+ solana_memory_write({
63
+ content: "Bitquery recovery failed: <template_path> error '<error_message>'. Tried: <what_was_attempted>. Result: still failing.",
64
+ tags: ["bitquery_recovery_failed"]
65
+ })
66
+
67
+ // Template consistently broken (2+ failures):
68
+ solana_memory_write({
69
+ content: "Bitquery template broken: <template_path> consistently fails with '<error_pattern>'. Recommend using solana_bitquery_query with custom GraphQL instead.",
70
+ tags: ["bitquery_template_broken"]
71
+ })
72
+ ```
73
+
74
+ This memory loop prevents the agent from re-deriving the same fix every session and creates a growing knowledge base of working query patterns.
75
+
38
76
  ### Error Classification
39
77
 
40
78
  | Response indicator | Class | Action |
@@ -69,6 +69,16 @@ Used by Steps 0, 5.5, 8.5, 9.
69
69
  | `strategy_evolution` | Strategy evolution reasoning log (Step 9) |
70
70
  | `discovery_filter_evolution` | Discovery filter parameter updates (Step 9) |
71
71
 
72
+ ## Bitquery Recovery Tags
73
+
74
+ Used when Bitquery API calls fail and the agent applies recovery procedures.
75
+
76
+ | Tag | Purpose |
77
+ |---|---|
78
+ | `bitquery_fix_applied` | Successful Bitquery recovery — records error class, fix applied, and working query/variables |
79
+ | `bitquery_recovery_failed` | Recovery attempt failed — records error class, what was tried, and why it didn't work |
80
+ | `bitquery_template_broken` | Template identified as consistently broken — records template path, error pattern, suggested replacement |
81
+
72
82
  ## System Tags
73
83
 
74
84
  | Tag | Purpose |
@@ -88,3 +98,4 @@ Used by Steps 0, 5.5, 8.5, 9.
88
98
  | `coordinated_shill_detected` | Coordinated shill campaign detected |
89
99
  | `website_analyzed` | Website legitimacy analysis result |
90
100
  | `alpha_source_model` | Personal alpha source trust model |
101
+ | `memory_trim` | Memory trim cron results |