1 line
1.7 MiB
Plaintext
1 line
1.7 MiB
Plaintext
{"repo": "build23w/fdv.lol", "files": {"src/agents/driver.js": {"6c2ecb9ac62b2c89eda961032245c83bd7f15181": {"t": 1785746658.4696896, "n": 108335, "h": "892155b76683c56b", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\nimport { createDecisionMemory } from \"./memory.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\t\tset(\"partialTpPct\", tune.partialTpPct, 0, 100, \"int\");\n\t\tset(\"minNetEdgePct\", tune.minNetEdgePct, -10, 10, 0.1);\n\t\tset(\"edgeSafetyBufferPct\", tune.edgeSafetyBufferPct, 0, 2, 0.05);\n\t\tset(\"maxEntryCostPct\", tune.maxEntryCostPct, 0, 10, 0.05);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimMinTerminalProb\", tune.entrySimMinTerminalProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tpartialTpPct: { type: [\"integer\", \"null\"] },\n\t\t\t\tminNetEdgePct: { type: [\"number\", \"null\"] },\n\t\t\t\tedgeSafetyBufferPct: { type: [\"number\", \"null\"] },\n\t\t\t\tmaxEntryCostPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinTerminalProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"partialTpPct\",\n\t\t\t\t\"minNetEdgePct\",\n\t\t\t\t\"edgeSafetyBufferPct\",\n\t\t\t\t\"maxEntryCostPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimMinTerminalProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst k = String(kind || \"\").trim().toLowerCase();\n\tconst allowedPref = (k === \"buy\")\n\t\t? \"buy | skip\"\n\t\t: (k === \"sell\")\n\t\t\t? \"sell_all | sell_partial | hold | long_hold\"\n\t\t\t: \"(unset)\";\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: set JSON keys mode='agentic_prepass' and kind='\" + String(k || \"\") + \"'.\" +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: actionPreference MUST be one of: \" + allowedPref + \".\" +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj, expectedKind = \"\") {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || expectedKind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tif (kind === \"buy\") {\n\t\t\tif (actionPreference !== \"buy\" && actionPreference !== \"skip\") return null;\n\t\t}\n\t\tif (kind === \"sell\") {\n\t\t\tif (actionPreference !== \"sell_all\" && actionPreference !== \"sell_partial\" && actionPreference !== \"hold\" && actionPreference !== \"long_hold\") return null;\n\t\t}\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n\tfunction _summarizeSwarm(members = []) {\n\t\ttry {\n\t\t\tconst ms = Array.isArray(members) ? members : [];\n\t\t\tconst clean = ms.filter((m) => m && typeof m === \"object\");\n\t\t\tif (!clean.length) return null;\n\n\t\t\tconst actionScores = new Map();\n\t\t\tconst byRole = {};\n\t\t\tfor (const m of clean) {\n\t\t\t\tconst ap = String(m.actionPreference || \"\").trim().toLowerCase();\n\t\t\t\tconst conf = _clampNum(m.confidence, 0, 1);\n\t\t\t\tif (ap) actionScores.set(ap, (actionScores.get(ap) || 0) + (Number.isFinite(conf) ? conf : 0));\n\t\t\t\tconst r = String(m.role || \"\").trim().slice(0, 40);\n\t\t\t\tif (r) {\n\t\t\t\t\tbyRole[r] = {\n\t\t\t\t\t\tactionPreference: ap || null,\n\t\t\t\t\t\tconfidence: Number.isFinite(conf) ? conf : null,\n\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 3) : [],\n\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 3) : [],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst consensus = Array.from(actionScores.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.map(([actionPreference, score]) => ({ actionPreference, score }))\n\t\t\t\t.slice(0, 3);\n\n\t\t\tconst top = consensus[0] || null;\n\t\t\tconst runnerUp = consensus[1] || null;\n\t\t\tconst margin = (top && runnerUp) ? (top.score - runnerUp.score) : null;\n\n\t\t\treturn {\n\t\t\t\tv: 1,\n\t\t\t\tconsensus: top ? { actionPreference: top.actionPreference, score: top.score, margin } : null,\n\t\t\t\trank: consensus,\n\t\t\t\tbyRole,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"quoteTrust\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"regime\",\n\t\t\t\t\"badge\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (Array.isArray(c.quoteTrustFlags) && c.quoteTrustFlags.length) keepCtx.quoteTrustFlags = c.quoteTrustFlags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean);\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst qm = c.quoteMetrics;\n\t\t\t\tif (qm && typeof qm === \"object\") {\n\t\t\t\t\tkeepCtx.quoteMetrics = {\n\t\t\t\t\t\tshockFrac: qm.quoteShockFrac ?? null,\n\t\t\t\t\t\tpnlClampApplied: (\"pnlClampApplied\" in qm) ? !!qm.pnlClampApplied : undefined,\n\t\t\t\t\t\tfeeDragPct: qm.feeDragPct ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst urgent = c.urgentMeta;\n\t\t\t\tif (urgent && typeof urgent === \"object\") {\n\t\t\t\t\tconst evidence = urgent.evidence && typeof urgent.evidence === \"object\" ? urgent.evidence : null;\n\t\t\t\t\tkeepCtx.urgent = {\n\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\tsev: urgent.sev ?? null,\n\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\tquoteTrustFloor: urgent.quoteTrustFloor ?? null,\n\t\t\t\t\t\tevidence: evidence ? {\n\t\t\t\t\t\t\tscore: evidence.score ?? null,\n\t\t\t\t\t\t\tddPct: evidence.ddPct ?? null,\n\t\t\t\t\t\t\tchgSlopeMin: evidence.chgSlopeMin ?? null,\n\t\t\t\t\t\t\tscSlopeMin: evidence.scSlopeMin ?? null,\n\t\t\t\t\t\t\tquoteShockFrac: evidence.quoteShockFrac ?? null,\n\t\t\t\t\t\t\tpassChg: (\"passChg\" in evidence) ? !!evidence.passChg : undefined,\n\t\t\t\t\t\t\tpassScore: (\"passScore\" in evidence) ? !!evidence.passScore : undefined,\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tconst urgent = an.urgent && typeof an.urgent === \"object\" ? an.urgent : null;\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\turgent: urgent ? {\n\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\tregime: r?.regime ? String(r.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tquoteTrust: (typeof r?.quoteTrust === \"number\") ? r.quoteTrust : undefined,\n\t\t\t\t\t\t\t\turgentKind: r?.urgentKind ? String(r.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\ttags: Array.isArray(r?.tags) ? r.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tquoteTrust: c?.quoteTrust ?? null,\n\t\t\t\t\tquoteTrustFlags: Array.isArray(c?.quoteTrustFlags) ? c.quoteTrustFlags.slice(0, 4) : undefined,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\tregime: c?.regime ?? null,\n\t\t\t\t\tbadge: c?.badge ?? null,\n\t\t\t\t\turgentKind: c?.urgent?.kind ?? c?.agentSignals?.urgent?.kind ?? null,\n\t\t\t\t\turgentHard: c?.urgent?.hard ?? c?.agentSignals?.urgent?.hard ?? null,\n\t\t\t\t\turgentCount: c?.urgent?.count ?? c?.agentSignals?.urgent?.count ?? null,\n\t\t\t\t\turgentNeedCount: c?.urgent?.needCount ?? c?.agentSignals?.urgent?.needCount ?? null,\n\t\t\t\t\turgentScore: c?.urgent?.evidence?.score ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t\tregime: last?.regime ? String(last.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\tquoteTrust: (typeof last?.quoteTrust === \"number\") ? last.quoteTrust : undefined,\n\t\t\t\t\t\turgentKind: last?.urgentKind ? String(last.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\ttags: Array.isArray(last?.tags) ? last.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"quoteTrust\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"regime\",\n\t\t\t\t\t\t\"badge\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (Array.isArray(c.quoteTrustFlags) && c.quoteTrustFlags.length) keepCtx.quoteTrustFlags = c.quoteTrustFlags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean);\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst qm = c.quoteMetrics;\n\t\t\t\t\t\tif (qm && typeof qm === \"object\") {\n\t\t\t\t\t\t\tkeepCtx.quoteMetrics = {\n\t\t\t\t\t\t\t\tshockFrac: qm.quoteShockFrac ?? null,\n\t\t\t\t\t\t\t\tpnlClampApplied: (\"pnlClampApplied\" in qm) ? !!qm.pnlClampApplied : undefined,\n\t\t\t\t\t\t\t\tfeeDragPct: qm.feeDragPct ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst urgent = c.urgentMeta;\n\t\t\t\t\t\tif (urgent && typeof urgent === \"object\") {\n\t\t\t\t\t\t\tconst evidence = urgent.evidence && typeof urgent.evidence === \"object\" ? urgent.evidence : null;\n\t\t\t\t\t\t\tkeepCtx.urgent = {\n\t\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\t\tsev: urgent.sev ?? null,\n\t\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t\t\tquoteTrustFloor: urgent.quoteTrustFloor ?? null,\n\t\t\t\t\t\t\t\tevidence: evidence ? {\n\t\t\t\t\t\t\t\t\tscore: evidence.score ?? null,\n\t\t\t\t\t\t\t\t\tddPct: evidence.ddPct ?? null,\n\t\t\t\t\t\t\t\t\tchgSlopeMin: evidence.chgSlopeMin ?? null,\n\t\t\t\t\t\t\t\t\tscSlopeMin: evidence.scSlopeMin ?? null,\n\t\t\t\t\t\t\t\t\tquoteShockFrac: evidence.quoteShockFrac ?? null,\n\t\t\t\t\t\t\t\t\tpassChg: (\"passChg\" in evidence) ? !!evidence.passChg : undefined,\n\t\t\t\t\t\t\t\t\tpassScore: (\"passScore\" in evidence) ? !!evidence.passScore : undefined,\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tconst urgent = an.urgent && typeof an.urgent === \"object\" ? an.urgent : null;\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\turgent: urgent ? {\n\t\t\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t\tregime: r?.regime ? String(r.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tquoteTrust: (typeof r?.quoteTrust === \"number\") ? r.quoteTrust : undefined,\n\t\t\t\t\t\t\t\t\t\turgentKind: r?.urgentKind ? String(r.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\ttags: Array.isArray(r?.tags) ? r.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tquoteTrust: c?.quoteTrust ?? null,\n\t\t\t\t\tquoteTrustFlags: Array.isArray(c?.quoteTrustFlags) ? c.quoteTrustFlags.slice(0, 4) : undefined,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\tregime: c?.regime ?? null,\n\t\t\t\t\tbadge: c?.badge ?? null,\n\t\t\t\t\turgentKind: c?.urgent?.kind ?? c?.agentSignals?.urgent?.kind ?? null,\n\t\t\t\t\turgentHard: c?.urgent?.hard ?? c?.agentSignals?.urgent?.hard ?? null,\n\t\t\t\t\turgentCount: c?.urgent?.count ?? c?.agentSignals?.urgent?.count ?? null,\n\t\t\t\t\turgentNeedCount: c?.urgent?.needCount ?? c?.agentSignals?.urgent?.needCount ?? null,\n\t\t\t\t\turgentScore: c?.urgent?.evidence?.score ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t\tregime: last?.regime ? String(last.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\tquoteTrust: (typeof last?.quoteTrust === \"number\") ? last.quoteTrust : undefined,\n\t\t\t\t\t\turgentKind: last?.urgentKind ? String(last.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\ttags: Array.isArray(last?.tags) ? last.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\tlet _lastSwarmPruneAt = 0;\n\n\tconst decisionMemory = createDecisionMemory({\n\t\tstorageKey: \"fdv_agent_decision_memory_v1\",\n\t\tpersist: true,\n\t});\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\tfunction _pruneSwarmCache(nowTs, ttlMs) {\n\t\ttry {\n\t\t\tconst n = Number.isFinite(Number(nowTs)) ? Number(nowTs) : now();\n\t\t\tconst ttl = Math.max(500, Math.min(120000, Math.floor(Number(ttlMs) || 0)));\n\t\t\t// Throttle: avoid O(N) scans too frequently.\n\t\t\tif (n - _lastSwarmPruneAt < 1000) return;\n\t\t\t_lastSwarmPruneAt = n;\n\t\t\tfor (const [k, rec] of swarmCache.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!rec || typeof rec !== \"object\") {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst at = _safeNum(rec.at, 0);\n\t\t\t\t\tconst age = at > 0 ? Math.max(0, n - at) : Number.POSITIVE_INFINITY;\n\t\t\t\t\t// Delete stale completed entries once they exceed TTL.\n\t\t\t\t\tif (!rec.inFlight && age > ttl) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Safety valve: if an in-flight entry is *very* old, drop it too.\n\t\t\t\t\tif (rec.inFlight && age > Math.max(ttl * 3, 60000)) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Decision memory is implemented in memory.js (ring-buffered + prompt-safe snapshot).\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// function _compactEvolveAnyForPrompt(e) {\n\t// \ttry {\n\t// \t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t// \t\tif (!obj) return null;\n\t// \t\t// If this is a stored summary shape, use the existing compactor.\n\t// \t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t// \t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t// \t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t// \t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t// \t\t}\n\t// \t\treturn null;\n\t// \t} catch {\n\t// \t\treturn null;\n\t// \t}\n\t// }\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry { decisionMemory.updateFromPayload(payload); } catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry { decisionMemory.updateAfterDecision({ kind, payload, decision, ok }); } catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry { return decisionMemory.snapshotForPrompt(); } catch { return { v: 4, startedAt: now() }; }\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\t_pruneSwarmCache(nowTs, swarmTtlMs);\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p, kind);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\tconst swarmSummary = _summarizeSwarm(members);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tsummary: swarmSummary,\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: nowTs, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t\tdecisionMemory.recordSwarm({ kind: String(kind || \"\"), mint: mintFull || null, members: swarmObj?.members || [] });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed, kind);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t} catch {}\n\n\t\t\t\tif (TRAINING_CAPTURE?.enabled || uploadToGary) {\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tlet uploadToGary = null;\n\t\t\ttry {\n\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t} catch {}\n\n\t\t\tconst wantLocal = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tconst wantRemote = !!uploadToGary;\n\t\t\tif (wantLocal || wantRemote) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\tif (s.startsWith(\"claude-\")) return \"anthropic\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\" || llmProvider === \"anthropic\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst anthropicKey = (o && (o.anthropicApiKey || o.anthropicKey || (provider === \"anthropic\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.anthropicApiKey || o.anthropicKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_anthropic_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t\t: (provider === \"anthropic\")\n\t\t\t\t\t\t\t\t\t\t? anthropicKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\t\t\t\tconst anthropicBaseUrl = String((o && (o.anthropicBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.anthropicBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_anthropic_base_url\", \"https://api.anthropic.com\")\n\t\t\t\t).trim() || \"https://api.anthropic.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t\t: (provider === \"anthropic\")\n\t\t\t\t\t\t\t\t\t\t? anthropicBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"runner\",\n\t\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\t\tok: false,\n\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\tmeta: null,\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t\t} catch {}\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tappendTrainingCapture({\n\t\t\t\tmode: \"runner\",\n\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\tok: true,\n\t\t\t\terr: \"\",\n\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "dc7032e3c250310fe32d4d1053454f75cfacb2da": {"t": 1785766654.2467275, "n": 108335, "h": "892155b76683c56b", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\nimport { createDecisionMemory } from \"./memory.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\t\tset(\"partialTpPct\", tune.partialTpPct, 0, 100, \"int\");\n\t\tset(\"minNetEdgePct\", tune.minNetEdgePct, -10, 10, 0.1);\n\t\tset(\"edgeSafetyBufferPct\", tune.edgeSafetyBufferPct, 0, 2, 0.05);\n\t\tset(\"maxEntryCostPct\", tune.maxEntryCostPct, 0, 10, 0.05);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimMinTerminalProb\", tune.entrySimMinTerminalProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tpartialTpPct: { type: [\"integer\", \"null\"] },\n\t\t\t\tminNetEdgePct: { type: [\"number\", \"null\"] },\n\t\t\t\tedgeSafetyBufferPct: { type: [\"number\", \"null\"] },\n\t\t\t\tmaxEntryCostPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinTerminalProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"partialTpPct\",\n\t\t\t\t\"minNetEdgePct\",\n\t\t\t\t\"edgeSafetyBufferPct\",\n\t\t\t\t\"maxEntryCostPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimMinTerminalProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst k = String(kind || \"\").trim().toLowerCase();\n\tconst allowedPref = (k === \"buy\")\n\t\t? \"buy | skip\"\n\t\t: (k === \"sell\")\n\t\t\t? \"sell_all | sell_partial | hold | long_hold\"\n\t\t\t: \"(unset)\";\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: set JSON keys mode='agentic_prepass' and kind='\" + String(k || \"\") + \"'.\" +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: actionPreference MUST be one of: \" + allowedPref + \".\" +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj, expectedKind = \"\") {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || expectedKind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tif (kind === \"buy\") {\n\t\t\tif (actionPreference !== \"buy\" && actionPreference !== \"skip\") return null;\n\t\t}\n\t\tif (kind === \"sell\") {\n\t\t\tif (actionPreference !== \"sell_all\" && actionPreference !== \"sell_partial\" && actionPreference !== \"hold\" && actionPreference !== \"long_hold\") return null;\n\t\t}\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n\tfunction _summarizeSwarm(members = []) {\n\t\ttry {\n\t\t\tconst ms = Array.isArray(members) ? members : [];\n\t\t\tconst clean = ms.filter((m) => m && typeof m === \"object\");\n\t\t\tif (!clean.length) return null;\n\n\t\t\tconst actionScores = new Map();\n\t\t\tconst byRole = {};\n\t\t\tfor (const m of clean) {\n\t\t\t\tconst ap = String(m.actionPreference || \"\").trim().toLowerCase();\n\t\t\t\tconst conf = _clampNum(m.confidence, 0, 1);\n\t\t\t\tif (ap) actionScores.set(ap, (actionScores.get(ap) || 0) + (Number.isFinite(conf) ? conf : 0));\n\t\t\t\tconst r = String(m.role || \"\").trim().slice(0, 40);\n\t\t\t\tif (r) {\n\t\t\t\t\tbyRole[r] = {\n\t\t\t\t\t\tactionPreference: ap || null,\n\t\t\t\t\t\tconfidence: Number.isFinite(conf) ? conf : null,\n\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 3) : [],\n\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 3) : [],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst consensus = Array.from(actionScores.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.map(([actionPreference, score]) => ({ actionPreference, score }))\n\t\t\t\t.slice(0, 3);\n\n\t\t\tconst top = consensus[0] || null;\n\t\t\tconst runnerUp = consensus[1] || null;\n\t\t\tconst margin = (top && runnerUp) ? (top.score - runnerUp.score) : null;\n\n\t\t\treturn {\n\t\t\t\tv: 1,\n\t\t\t\tconsensus: top ? { actionPreference: top.actionPreference, score: top.score, margin } : null,\n\t\t\t\trank: consensus,\n\t\t\t\tbyRole,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"quoteTrust\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"regime\",\n\t\t\t\t\"badge\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (Array.isArray(c.quoteTrustFlags) && c.quoteTrustFlags.length) keepCtx.quoteTrustFlags = c.quoteTrustFlags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean);\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst qm = c.quoteMetrics;\n\t\t\t\tif (qm && typeof qm === \"object\") {\n\t\t\t\t\tkeepCtx.quoteMetrics = {\n\t\t\t\t\t\tshockFrac: qm.quoteShockFrac ?? null,\n\t\t\t\t\t\tpnlClampApplied: (\"pnlClampApplied\" in qm) ? !!qm.pnlClampApplied : undefined,\n\t\t\t\t\t\tfeeDragPct: qm.feeDragPct ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst urgent = c.urgentMeta;\n\t\t\t\tif (urgent && typeof urgent === \"object\") {\n\t\t\t\t\tconst evidence = urgent.evidence && typeof urgent.evidence === \"object\" ? urgent.evidence : null;\n\t\t\t\t\tkeepCtx.urgent = {\n\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\tsev: urgent.sev ?? null,\n\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\tquoteTrustFloor: urgent.quoteTrustFloor ?? null,\n\t\t\t\t\t\tevidence: evidence ? {\n\t\t\t\t\t\t\tscore: evidence.score ?? null,\n\t\t\t\t\t\t\tddPct: evidence.ddPct ?? null,\n\t\t\t\t\t\t\tchgSlopeMin: evidence.chgSlopeMin ?? null,\n\t\t\t\t\t\t\tscSlopeMin: evidence.scSlopeMin ?? null,\n\t\t\t\t\t\t\tquoteShockFrac: evidence.quoteShockFrac ?? null,\n\t\t\t\t\t\t\tpassChg: (\"passChg\" in evidence) ? !!evidence.passChg : undefined,\n\t\t\t\t\t\t\tpassScore: (\"passScore\" in evidence) ? !!evidence.passScore : undefined,\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tconst urgent = an.urgent && typeof an.urgent === \"object\" ? an.urgent : null;\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\turgent: urgent ? {\n\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\tregime: r?.regime ? String(r.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tquoteTrust: (typeof r?.quoteTrust === \"number\") ? r.quoteTrust : undefined,\n\t\t\t\t\t\t\t\turgentKind: r?.urgentKind ? String(r.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\ttags: Array.isArray(r?.tags) ? r.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tquoteTrust: c?.quoteTrust ?? null,\n\t\t\t\t\tquoteTrustFlags: Array.isArray(c?.quoteTrustFlags) ? c.quoteTrustFlags.slice(0, 4) : undefined,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\tregime: c?.regime ?? null,\n\t\t\t\t\tbadge: c?.badge ?? null,\n\t\t\t\t\turgentKind: c?.urgent?.kind ?? c?.agentSignals?.urgent?.kind ?? null,\n\t\t\t\t\turgentHard: c?.urgent?.hard ?? c?.agentSignals?.urgent?.hard ?? null,\n\t\t\t\t\turgentCount: c?.urgent?.count ?? c?.agentSignals?.urgent?.count ?? null,\n\t\t\t\t\turgentNeedCount: c?.urgent?.needCount ?? c?.agentSignals?.urgent?.needCount ?? null,\n\t\t\t\t\turgentScore: c?.urgent?.evidence?.score ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t\tregime: last?.regime ? String(last.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\tquoteTrust: (typeof last?.quoteTrust === \"number\") ? last.quoteTrust : undefined,\n\t\t\t\t\t\turgentKind: last?.urgentKind ? String(last.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\ttags: Array.isArray(last?.tags) ? last.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"quoteTrust\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"regime\",\n\t\t\t\t\t\t\"badge\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (Array.isArray(c.quoteTrustFlags) && c.quoteTrustFlags.length) keepCtx.quoteTrustFlags = c.quoteTrustFlags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean);\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst qm = c.quoteMetrics;\n\t\t\t\t\t\tif (qm && typeof qm === \"object\") {\n\t\t\t\t\t\t\tkeepCtx.quoteMetrics = {\n\t\t\t\t\t\t\t\tshockFrac: qm.quoteShockFrac ?? null,\n\t\t\t\t\t\t\t\tpnlClampApplied: (\"pnlClampApplied\" in qm) ? !!qm.pnlClampApplied : undefined,\n\t\t\t\t\t\t\t\tfeeDragPct: qm.feeDragPct ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst urgent = c.urgentMeta;\n\t\t\t\t\t\tif (urgent && typeof urgent === \"object\") {\n\t\t\t\t\t\t\tconst evidence = urgent.evidence && typeof urgent.evidence === \"object\" ? urgent.evidence : null;\n\t\t\t\t\t\t\tkeepCtx.urgent = {\n\t\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\t\tsev: urgent.sev ?? null,\n\t\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t\t\tquoteTrustFloor: urgent.quoteTrustFloor ?? null,\n\t\t\t\t\t\t\t\tevidence: evidence ? {\n\t\t\t\t\t\t\t\t\tscore: evidence.score ?? null,\n\t\t\t\t\t\t\t\t\tddPct: evidence.ddPct ?? null,\n\t\t\t\t\t\t\t\t\tchgSlopeMin: evidence.chgSlopeMin ?? null,\n\t\t\t\t\t\t\t\t\tscSlopeMin: evidence.scSlopeMin ?? null,\n\t\t\t\t\t\t\t\t\tquoteShockFrac: evidence.quoteShockFrac ?? null,\n\t\t\t\t\t\t\t\t\tpassChg: (\"passChg\" in evidence) ? !!evidence.passChg : undefined,\n\t\t\t\t\t\t\t\t\tpassScore: (\"passScore\" in evidence) ? !!evidence.passScore : undefined,\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tconst urgent = an.urgent && typeof an.urgent === \"object\" ? an.urgent : null;\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\turgent: urgent ? {\n\t\t\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t\tregime: r?.regime ? String(r.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tquoteTrust: (typeof r?.quoteTrust === \"number\") ? r.quoteTrust : undefined,\n\t\t\t\t\t\t\t\t\t\turgentKind: r?.urgentKind ? String(r.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\ttags: Array.isArray(r?.tags) ? r.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tquoteTrust: c?.quoteTrust ?? null,\n\t\t\t\t\tquoteTrustFlags: Array.isArray(c?.quoteTrustFlags) ? c.quoteTrustFlags.slice(0, 4) : undefined,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\tregime: c?.regime ?? null,\n\t\t\t\t\tbadge: c?.badge ?? null,\n\t\t\t\t\turgentKind: c?.urgent?.kind ?? c?.agentSignals?.urgent?.kind ?? null,\n\t\t\t\t\turgentHard: c?.urgent?.hard ?? c?.agentSignals?.urgent?.hard ?? null,\n\t\t\t\t\turgentCount: c?.urgent?.count ?? c?.agentSignals?.urgent?.count ?? null,\n\t\t\t\t\turgentNeedCount: c?.urgent?.needCount ?? c?.agentSignals?.urgent?.needCount ?? null,\n\t\t\t\t\turgentScore: c?.urgent?.evidence?.score ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t\tregime: last?.regime ? String(last.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\tquoteTrust: (typeof last?.quoteTrust === \"number\") ? last.quoteTrust : undefined,\n\t\t\t\t\t\turgentKind: last?.urgentKind ? String(last.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\ttags: Array.isArray(last?.tags) ? last.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\tlet _lastSwarmPruneAt = 0;\n\n\tconst decisionMemory = createDecisionMemory({\n\t\tstorageKey: \"fdv_agent_decision_memory_v1\",\n\t\tpersist: true,\n\t});\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\tfunction _pruneSwarmCache(nowTs, ttlMs) {\n\t\ttry {\n\t\t\tconst n = Number.isFinite(Number(nowTs)) ? Number(nowTs) : now();\n\t\t\tconst ttl = Math.max(500, Math.min(120000, Math.floor(Number(ttlMs) || 0)));\n\t\t\t// Throttle: avoid O(N) scans too frequently.\n\t\t\tif (n - _lastSwarmPruneAt < 1000) return;\n\t\t\t_lastSwarmPruneAt = n;\n\t\t\tfor (const [k, rec] of swarmCache.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!rec || typeof rec !== \"object\") {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst at = _safeNum(rec.at, 0);\n\t\t\t\t\tconst age = at > 0 ? Math.max(0, n - at) : Number.POSITIVE_INFINITY;\n\t\t\t\t\t// Delete stale completed entries once they exceed TTL.\n\t\t\t\t\tif (!rec.inFlight && age > ttl) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Safety valve: if an in-flight entry is *very* old, drop it too.\n\t\t\t\t\tif (rec.inFlight && age > Math.max(ttl * 3, 60000)) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Decision memory is implemented in memory.js (ring-buffered + prompt-safe snapshot).\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// function _compactEvolveAnyForPrompt(e) {\n\t// \ttry {\n\t// \t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t// \t\tif (!obj) return null;\n\t// \t\t// If this is a stored summary shape, use the existing compactor.\n\t// \t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t// \t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t// \t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t// \t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t// \t\t}\n\t// \t\treturn null;\n\t// \t} catch {\n\t// \t\treturn null;\n\t// \t}\n\t// }\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry { decisionMemory.updateFromPayload(payload); } catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry { decisionMemory.updateAfterDecision({ kind, payload, decision, ok }); } catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry { return decisionMemory.snapshotForPrompt(); } catch { return { v: 4, startedAt: now() }; }\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\t_pruneSwarmCache(nowTs, swarmTtlMs);\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p, kind);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\tconst swarmSummary = _summarizeSwarm(members);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tsummary: swarmSummary,\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: nowTs, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t\tdecisionMemory.recordSwarm({ kind: String(kind || \"\"), mint: mintFull || null, members: swarmObj?.members || [] });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed, kind);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t} catch {}\n\n\t\t\t\tif (TRAINING_CAPTURE?.enabled || uploadToGary) {\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tlet uploadToGary = null;\n\t\t\ttry {\n\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t} catch {}\n\n\t\t\tconst wantLocal = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tconst wantRemote = !!uploadToGary;\n\t\t\tif (wantLocal || wantRemote) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\tif (s.startsWith(\"claude-\")) return \"anthropic\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\" || llmProvider === \"anthropic\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst anthropicKey = (o && (o.anthropicApiKey || o.anthropicKey || (provider === \"anthropic\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.anthropicApiKey || o.anthropicKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_anthropic_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t\t: (provider === \"anthropic\")\n\t\t\t\t\t\t\t\t\t\t? anthropicKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\t\t\t\tconst anthropicBaseUrl = String((o && (o.anthropicBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.anthropicBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_anthropic_base_url\", \"https://api.anthropic.com\")\n\t\t\t\t).trim() || \"https://api.anthropic.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t\t: (provider === \"anthropic\")\n\t\t\t\t\t\t\t\t\t\t? anthropicBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"runner\",\n\t\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\t\tok: false,\n\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\tmeta: null,\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t\t} catch {}\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tappendTrainingCapture({\n\t\t\t\tmode: \"runner\",\n\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\tok: true,\n\t\t\t\terr: \"\",\n\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "326b87c715474aa9551250824878db913a911a33": {"t": 1785766656.4323537, "n": 107601, "h": "804fa985246060cd", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\nimport { createDecisionMemory } from \"./memory.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\t\tset(\"partialTpPct\", tune.partialTpPct, 0, 100, \"int\");\n\t\tset(\"minNetEdgePct\", tune.minNetEdgePct, -10, 10, 0.1);\n\t\tset(\"edgeSafetyBufferPct\", tune.edgeSafetyBufferPct, 0, 2, 0.05);\n\t\tset(\"maxEntryCostPct\", tune.maxEntryCostPct, 0, 10, 0.05);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimMinTerminalProb\", tune.entrySimMinTerminalProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tpartialTpPct: { type: [\"integer\", \"null\"] },\n\t\t\t\tminNetEdgePct: { type: [\"number\", \"null\"] },\n\t\t\t\tedgeSafetyBufferPct: { type: [\"number\", \"null\"] },\n\t\t\t\tmaxEntryCostPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinTerminalProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"partialTpPct\",\n\t\t\t\t\"minNetEdgePct\",\n\t\t\t\t\"edgeSafetyBufferPct\",\n\t\t\t\t\"maxEntryCostPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimMinTerminalProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst k = String(kind || \"\").trim().toLowerCase();\n\tconst allowedPref = (k === \"buy\")\n\t\t? \"buy | skip\"\n\t\t: (k === \"sell\")\n\t\t\t? \"sell_all | sell_partial | hold | long_hold\"\n\t\t\t: \"(unset)\";\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: set JSON keys mode='agentic_prepass' and kind='\" + String(k || \"\") + \"'.\" +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: actionPreference MUST be one of: \" + allowedPref + \".\" +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj, expectedKind = \"\") {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || expectedKind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tif (kind === \"buy\") {\n\t\t\tif (actionPreference !== \"buy\" && actionPreference !== \"skip\") return null;\n\t\t}\n\t\tif (kind === \"sell\") {\n\t\t\tif (actionPreference !== \"sell_all\" && actionPreference !== \"sell_partial\" && actionPreference !== \"hold\" && actionPreference !== \"long_hold\") return null;\n\t\t}\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n\tfunction _summarizeSwarm(members = []) {\n\t\ttry {\n\t\t\tconst ms = Array.isArray(members) ? members : [];\n\t\t\tconst clean = ms.filter((m) => m && typeof m === \"object\");\n\t\t\tif (!clean.length) return null;\n\n\t\t\tconst actionScores = new Map();\n\t\t\tconst byRole = {};\n\t\t\tfor (const m of clean) {\n\t\t\t\tconst ap = String(m.actionPreference || \"\").trim().toLowerCase();\n\t\t\t\tconst conf = _clampNum(m.confidence, 0, 1);\n\t\t\t\tif (ap) actionScores.set(ap, (actionScores.get(ap) || 0) + (Number.isFinite(conf) ? conf : 0));\n\t\t\t\tconst r = String(m.role || \"\").trim().slice(0, 40);\n\t\t\t\tif (r) {\n\t\t\t\t\tbyRole[r] = {\n\t\t\t\t\t\tactionPreference: ap || null,\n\t\t\t\t\t\tconfidence: Number.isFinite(conf) ? conf : null,\n\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 3) : [],\n\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 3) : [],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst consensus = Array.from(actionScores.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.map(([actionPreference, score]) => ({ actionPreference, score }))\n\t\t\t\t.slice(0, 3);\n\n\t\t\tconst top = consensus[0] || null;\n\t\t\tconst runnerUp = consensus[1] || null;\n\t\t\tconst margin = (top && runnerUp) ? (top.score - runnerUp.score) : null;\n\n\t\t\treturn {\n\t\t\t\tv: 1,\n\t\t\t\tconsensus: top ? { actionPreference: top.actionPreference, score: top.score, margin } : null,\n\t\t\t\trank: consensus,\n\t\t\t\tbyRole,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"quoteTrust\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"regime\",\n\t\t\t\t\"badge\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (Array.isArray(c.quoteTrustFlags) && c.quoteTrustFlags.length) keepCtx.quoteTrustFlags = c.quoteTrustFlags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean);\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst qm = c.quoteMetrics;\n\t\t\t\tif (qm && typeof qm === \"object\") {\n\t\t\t\t\tkeepCtx.quoteMetrics = {\n\t\t\t\t\t\tshockFrac: qm.quoteShockFrac ?? null,\n\t\t\t\t\t\tpnlClampApplied: (\"pnlClampApplied\" in qm) ? !!qm.pnlClampApplied : undefined,\n\t\t\t\t\t\tfeeDragPct: qm.feeDragPct ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst urgent = c.urgentMeta;\n\t\t\t\tif (urgent && typeof urgent === \"object\") {\n\t\t\t\t\tconst evidence = urgent.evidence && typeof urgent.evidence === \"object\" ? urgent.evidence : null;\n\t\t\t\t\tkeepCtx.urgent = {\n\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\tsev: urgent.sev ?? null,\n\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\tquoteTrustFloor: urgent.quoteTrustFloor ?? null,\n\t\t\t\t\t\tevidence: evidence ? {\n\t\t\t\t\t\t\tscore: evidence.score ?? null,\n\t\t\t\t\t\t\tddPct: evidence.ddPct ?? null,\n\t\t\t\t\t\t\tchgSlopeMin: evidence.chgSlopeMin ?? null,\n\t\t\t\t\t\t\tscSlopeMin: evidence.scSlopeMin ?? null,\n\t\t\t\t\t\t\tquoteShockFrac: evidence.quoteShockFrac ?? null,\n\t\t\t\t\t\t\tpassChg: (\"passChg\" in evidence) ? !!evidence.passChg : undefined,\n\t\t\t\t\t\t\tpassScore: (\"passScore\" in evidence) ? !!evidence.passScore : undefined,\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tconst urgent = an.urgent && typeof an.urgent === \"object\" ? an.urgent : null;\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\turgent: urgent ? {\n\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\tregime: r?.regime ? String(r.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tquoteTrust: (typeof r?.quoteTrust === \"number\") ? r.quoteTrust : undefined,\n\t\t\t\t\t\t\t\turgentKind: r?.urgentKind ? String(r.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\ttags: Array.isArray(r?.tags) ? r.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tquoteTrust: c?.quoteTrust ?? null,\n\t\t\t\t\tquoteTrustFlags: Array.isArray(c?.quoteTrustFlags) ? c.quoteTrustFlags.slice(0, 4) : undefined,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\tregime: c?.regime ?? null,\n\t\t\t\t\tbadge: c?.badge ?? null,\n\t\t\t\t\turgentKind: c?.urgent?.kind ?? c?.agentSignals?.urgent?.kind ?? null,\n\t\t\t\t\turgentHard: c?.urgent?.hard ?? c?.agentSignals?.urgent?.hard ?? null,\n\t\t\t\t\turgentCount: c?.urgent?.count ?? c?.agentSignals?.urgent?.count ?? null,\n\t\t\t\t\turgentNeedCount: c?.urgent?.needCount ?? c?.agentSignals?.urgent?.needCount ?? null,\n\t\t\t\t\turgentScore: c?.urgent?.evidence?.score ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t\tregime: last?.regime ? String(last.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\tquoteTrust: (typeof last?.quoteTrust === \"number\") ? last.quoteTrust : undefined,\n\t\t\t\t\t\turgentKind: last?.urgentKind ? String(last.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\ttags: Array.isArray(last?.tags) ? last.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"quoteTrust\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"regime\",\n\t\t\t\t\t\t\"badge\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (Array.isArray(c.quoteTrustFlags) && c.quoteTrustFlags.length) keepCtx.quoteTrustFlags = c.quoteTrustFlags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean);\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst qm = c.quoteMetrics;\n\t\t\t\t\t\tif (qm && typeof qm === \"object\") {\n\t\t\t\t\t\t\tkeepCtx.quoteMetrics = {\n\t\t\t\t\t\t\t\tshockFrac: qm.quoteShockFrac ?? null,\n\t\t\t\t\t\t\t\tpnlClampApplied: (\"pnlClampApplied\" in qm) ? !!qm.pnlClampApplied : undefined,\n\t\t\t\t\t\t\t\tfeeDragPct: qm.feeDragPct ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst urgent = c.urgentMeta;\n\t\t\t\t\t\tif (urgent && typeof urgent === \"object\") {\n\t\t\t\t\t\t\tconst evidence = urgent.evidence && typeof urgent.evidence === \"object\" ? urgent.evidence : null;\n\t\t\t\t\t\t\tkeepCtx.urgent = {\n\t\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\t\tsev: urgent.sev ?? null,\n\t\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t\t\tquoteTrustFloor: urgent.quoteTrustFloor ?? null,\n\t\t\t\t\t\t\t\tevidence: evidence ? {\n\t\t\t\t\t\t\t\t\tscore: evidence.score ?? null,\n\t\t\t\t\t\t\t\t\tddPct: evidence.ddPct ?? null,\n\t\t\t\t\t\t\t\t\tchgSlopeMin: evidence.chgSlopeMin ?? null,\n\t\t\t\t\t\t\t\t\tscSlopeMin: evidence.scSlopeMin ?? null,\n\t\t\t\t\t\t\t\t\tquoteShockFrac: evidence.quoteShockFrac ?? null,\n\t\t\t\t\t\t\t\t\tpassChg: (\"passChg\" in evidence) ? !!evidence.passChg : undefined,\n\t\t\t\t\t\t\t\t\tpassScore: (\"passScore\" in evidence) ? !!evidence.passScore : undefined,\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tconst urgent = an.urgent && typeof an.urgent === \"object\" ? an.urgent : null;\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\turgent: urgent ? {\n\t\t\t\t\t\t\t\t\tkind: urgent.kind ?? null,\n\t\t\t\t\t\t\t\t\thard: (\"hard\" in urgent) ? !!urgent.hard : undefined,\n\t\t\t\t\t\t\t\t\tcount: urgent.count ?? null,\n\t\t\t\t\t\t\t\t\tneedCount: urgent.needCount ?? null,\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t\tregime: r?.regime ? String(r.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tquoteTrust: (typeof r?.quoteTrust === \"number\") ? r.quoteTrust : undefined,\n\t\t\t\t\t\t\t\t\t\turgentKind: r?.urgentKind ? String(r.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\ttags: Array.isArray(r?.tags) ? r.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tquoteTrust: c?.quoteTrust ?? null,\n\t\t\t\t\tquoteTrustFlags: Array.isArray(c?.quoteTrustFlags) ? c.quoteTrustFlags.slice(0, 4) : undefined,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\tregime: c?.regime ?? null,\n\t\t\t\t\tbadge: c?.badge ?? null,\n\t\t\t\t\turgentKind: c?.urgent?.kind ?? c?.agentSignals?.urgent?.kind ?? null,\n\t\t\t\t\turgentHard: c?.urgent?.hard ?? c?.agentSignals?.urgent?.hard ?? null,\n\t\t\t\t\turgentCount: c?.urgent?.count ?? c?.agentSignals?.urgent?.count ?? null,\n\t\t\t\t\turgentNeedCount: c?.urgent?.needCount ?? c?.agentSignals?.urgent?.needCount ?? null,\n\t\t\t\t\turgentScore: c?.urgent?.evidence?.score ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t\tregime: last?.regime ? String(last.regime).slice(0, 24) : undefined,\n\t\t\t\t\t\tquoteTrust: (typeof last?.quoteTrust === \"number\") ? last.quoteTrust : undefined,\n\t\t\t\t\t\turgentKind: last?.urgentKind ? String(last.urgentKind).slice(0, 24) : undefined,\n\t\t\t\t\t\ttags: Array.isArray(last?.tags) ? last.tags.slice(0, 5).map((x) => String(x || \"\")).filter(Boolean) : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\tlet _lastSwarmPruneAt = 0;\n\n\tconst decisionMemory = createDecisionMemory({\n\t\tstorageKey: \"fdv_agent_decision_memory_v1\",\n\t\tpersist: true,\n\t});\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\tfunction _pruneSwarmCache(nowTs, ttlMs) {\n\t\ttry {\n\t\t\tconst n = Number.isFinite(Number(nowTs)) ? Number(nowTs) : now();\n\t\t\tconst ttl = Math.max(500, Math.min(120000, Math.floor(Number(ttlMs) || 0)));\n\t\t\t// Throttle: avoid O(N) scans too frequently.\n\t\t\tif (n - _lastSwarmPruneAt < 1000) return;\n\t\t\t_lastSwarmPruneAt = n;\n\t\t\tfor (const [k, rec] of swarmCache.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!rec || typeof rec !== \"object\") {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst at = _safeNum(rec.at, 0);\n\t\t\t\t\tconst age = at > 0 ? Math.max(0, n - at) : Number.POSITIVE_INFINITY;\n\t\t\t\t\t// Delete stale completed entries once they exceed TTL.\n\t\t\t\t\tif (!rec.inFlight && age > ttl) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Safety valve: if an in-flight entry is *very* old, drop it too.\n\t\t\t\t\tif (rec.inFlight && age > Math.max(ttl * 3, 60000)) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Decision memory is implemented in memory.js (ring-buffered + prompt-safe snapshot).\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// function _compactEvolveAnyForPrompt(e) {\n\t// \ttry {\n\t// \t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t// \t\tif (!obj) return null;\n\t// \t\t// If this is a stored summary shape, use the existing compactor.\n\t// \t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t// \t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t// \t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t// \t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t// \t\t}\n\t// \t\treturn null;\n\t// \t} catch {\n\t// \t\treturn null;\n\t// \t}\n\t// }\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry { decisionMemory.updateFromPayload(payload); } catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry { decisionMemory.updateAfterDecision({ kind, payload, decision, ok }); } catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry { return decisionMemory.snapshotForPrompt(); } catch { return { v: 4, startedAt: now() }; }\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\t_pruneSwarmCache(nowTs, swarmTtlMs);\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p, kind);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\tconst swarmSummary = _summarizeSwarm(members);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tsummary: swarmSummary,\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: nowTs, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t\tdecisionMemory.recordSwarm({ kind: String(kind || \"\"), mint: mintFull || null, members: swarmObj?.members || [] });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed, kind);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t} catch {}\n\n\t\t\t\tif (TRAINING_CAPTURE?.enabled || uploadToGary) {\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tlet uploadToGary = null;\n\t\t\ttry {\n\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t} catch {}\n\n\t\t\tconst wantLocal = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tconst wantRemote = !!uploadToGary;\n\t\t\tif (wantLocal || wantRemote) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"runner\",\n\t\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\t\tok: false,\n\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\tmeta: null,\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t\t} catch {}\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tappendTrainingCapture({\n\t\t\t\tmode: \"runner\",\n\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\tok: true,\n\t\t\t\terr: \"\",\n\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "a05de974d86c59e1a94b25ab4687f83a3467f0d3": {"t": 1785766658.248811, "n": 100628, "h": "cbf4c5084e66fadd", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\nimport { createDecisionMemory } from \"./memory.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst k = String(kind || \"\").trim().toLowerCase();\n\tconst allowedPref = (k === \"buy\")\n\t\t? \"buy | skip\"\n\t\t: (k === \"sell\")\n\t\t\t? \"sell_all | sell_partial | hold | long_hold\"\n\t\t\t: \"(unset)\";\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: set JSON keys mode='agentic_prepass' and kind='\" + String(k || \"\") + \"'.\" +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: actionPreference MUST be one of: \" + allowedPref + \".\" +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj, expectedKind = \"\") {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || expectedKind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tif (kind === \"buy\") {\n\t\t\tif (actionPreference !== \"buy\" && actionPreference !== \"skip\") return null;\n\t\t}\n\t\tif (kind === \"sell\") {\n\t\t\tif (actionPreference !== \"sell_all\" && actionPreference !== \"sell_partial\" && actionPreference !== \"hold\" && actionPreference !== \"long_hold\") return null;\n\t\t}\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n\tfunction _summarizeSwarm(members = []) {\n\t\ttry {\n\t\t\tconst ms = Array.isArray(members) ? members : [];\n\t\t\tconst clean = ms.filter((m) => m && typeof m === \"object\");\n\t\t\tif (!clean.length) return null;\n\n\t\t\tconst actionScores = new Map();\n\t\t\tconst byRole = {};\n\t\t\tfor (const m of clean) {\n\t\t\t\tconst ap = String(m.actionPreference || \"\").trim().toLowerCase();\n\t\t\t\tconst conf = _clampNum(m.confidence, 0, 1);\n\t\t\t\tif (ap) actionScores.set(ap, (actionScores.get(ap) || 0) + (Number.isFinite(conf) ? conf : 0));\n\t\t\t\tconst r = String(m.role || \"\").trim().slice(0, 40);\n\t\t\t\tif (r) {\n\t\t\t\t\tbyRole[r] = {\n\t\t\t\t\t\tactionPreference: ap || null,\n\t\t\t\t\t\tconfidence: Number.isFinite(conf) ? conf : null,\n\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 3) : [],\n\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 3) : [],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst consensus = Array.from(actionScores.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.map(([actionPreference, score]) => ({ actionPreference, score }))\n\t\t\t\t.slice(0, 3);\n\n\t\t\tconst top = consensus[0] || null;\n\t\t\tconst runnerUp = consensus[1] || null;\n\t\t\tconst margin = (top && runnerUp) ? (top.score - runnerUp.score) : null;\n\n\t\t\treturn {\n\t\t\t\tv: 1,\n\t\t\t\tconsensus: top ? { actionPreference: top.actionPreference, score: top.score, margin } : null,\n\t\t\t\trank: consensus,\n\t\t\t\tbyRole,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\tlet _lastSwarmPruneAt = 0;\n\n\tconst decisionMemory = createDecisionMemory({\n\t\tstorageKey: \"fdv_agent_decision_memory_v1\",\n\t\tpersist: true,\n\t});\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\tfunction _pruneSwarmCache(nowTs, ttlMs) {\n\t\ttry {\n\t\t\tconst n = Number.isFinite(Number(nowTs)) ? Number(nowTs) : now();\n\t\t\tconst ttl = Math.max(500, Math.min(120000, Math.floor(Number(ttlMs) || 0)));\n\t\t\t// Throttle: avoid O(N) scans too frequently.\n\t\t\tif (n - _lastSwarmPruneAt < 1000) return;\n\t\t\t_lastSwarmPruneAt = n;\n\t\t\tfor (const [k, rec] of swarmCache.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!rec || typeof rec !== \"object\") {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst at = _safeNum(rec.at, 0);\n\t\t\t\t\tconst age = at > 0 ? Math.max(0, n - at) : Number.POSITIVE_INFINITY;\n\t\t\t\t\t// Delete stale completed entries once they exceed TTL.\n\t\t\t\t\tif (!rec.inFlight && age > ttl) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Safety valve: if an in-flight entry is *very* old, drop it too.\n\t\t\t\t\tif (rec.inFlight && age > Math.max(ttl * 3, 60000)) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Decision memory is implemented in memory.js (ring-buffered + prompt-safe snapshot).\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// function _compactEvolveAnyForPrompt(e) {\n\t// \ttry {\n\t// \t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t// \t\tif (!obj) return null;\n\t// \t\t// If this is a stored summary shape, use the existing compactor.\n\t// \t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t// \t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t// \t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t// \t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t// \t\t}\n\t// \t\treturn null;\n\t// \t} catch {\n\t// \t\treturn null;\n\t// \t}\n\t// }\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry { decisionMemory.updateFromPayload(payload); } catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry { decisionMemory.updateAfterDecision({ kind, payload, decision, ok }); } catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry { return decisionMemory.snapshotForPrompt(); } catch { return { v: 2, startedAt: now() }; }\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\t_pruneSwarmCache(nowTs, swarmTtlMs);\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p, kind);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\tconst swarmSummary = _summarizeSwarm(members);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tsummary: swarmSummary,\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: nowTs, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t\tdecisionMemory.recordSwarm({ kind: String(kind || \"\"), mint: mintFull || null, members: swarmObj?.members || [] });\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed, kind);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t} catch {}\n\n\t\t\t\tif (TRAINING_CAPTURE?.enabled || uploadToGary) {\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tlet uploadToGary = null;\n\t\t\ttry {\n\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t} catch {}\n\n\t\t\tconst wantLocal = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tconst wantRemote = !!uploadToGary;\n\t\t\tif (wantLocal || wantRemote) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"runner\",\n\t\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\t\tok: false,\n\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\tmeta: null,\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t\t} catch {}\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tappendTrainingCapture({\n\t\t\t\tmode: \"runner\",\n\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\tok: true,\n\t\t\t\terr: \"\",\n\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "1ae64cfe9e6d8853fe20796662d85d62b00d9d8d": {"t": 1785766660.6320803, "n": 104771, "h": "5d37b9ee951acc55", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst k = String(kind || \"\").trim().toLowerCase();\n\tconst allowedPref = (k === \"buy\")\n\t\t? \"buy | skip\"\n\t\t: (k === \"sell\")\n\t\t\t? \"sell_all | sell_partial | hold | long_hold\"\n\t\t\t: \"(unset)\";\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: set JSON keys mode='agentic_prepass' and kind='\" + String(k || \"\") + \"'.\" +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: actionPreference MUST be one of: \" + allowedPref + \".\" +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj, expectedKind = \"\") {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || expectedKind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tif (kind === \"buy\") {\n\t\t\tif (actionPreference !== \"buy\" && actionPreference !== \"skip\") return null;\n\t\t}\n\t\tif (kind === \"sell\") {\n\t\t\tif (actionPreference !== \"sell_all\" && actionPreference !== \"sell_partial\" && actionPreference !== \"hold\" && actionPreference !== \"long_hold\") return null;\n\t\t}\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n\tfunction _summarizeSwarm(members = []) {\n\t\ttry {\n\t\t\tconst ms = Array.isArray(members) ? members : [];\n\t\t\tconst clean = ms.filter((m) => m && typeof m === \"object\");\n\t\t\tif (!clean.length) return null;\n\n\t\t\tconst actionScores = new Map();\n\t\t\tconst byRole = {};\n\t\t\tfor (const m of clean) {\n\t\t\t\tconst ap = String(m.actionPreference || \"\").trim().toLowerCase();\n\t\t\t\tconst conf = _clampNum(m.confidence, 0, 1);\n\t\t\t\tif (ap) actionScores.set(ap, (actionScores.get(ap) || 0) + (Number.isFinite(conf) ? conf : 0));\n\t\t\t\tconst r = String(m.role || \"\").trim().slice(0, 40);\n\t\t\t\tif (r) {\n\t\t\t\t\tbyRole[r] = {\n\t\t\t\t\t\tactionPreference: ap || null,\n\t\t\t\t\t\tconfidence: Number.isFinite(conf) ? conf : null,\n\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 3) : [],\n\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 3) : [],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst consensus = Array.from(actionScores.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.map(([actionPreference, score]) => ({ actionPreference, score }))\n\t\t\t\t.slice(0, 3);\n\n\t\t\tconst top = consensus[0] || null;\n\t\t\tconst runnerUp = consensus[1] || null;\n\t\t\tconst margin = (top && runnerUp) ? (top.score - runnerUp.score) : null;\n\n\t\t\treturn {\n\t\t\t\tv: 1,\n\t\t\t\tconsensus: top ? { actionPreference: top.actionPreference, score: top.score, margin } : null,\n\t\t\t\trank: consensus,\n\t\t\t\tbyRole,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\tlet _lastSwarmPruneAt = 0;\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\tfunction _pruneSwarmCache(nowTs, ttlMs) {\n\t\ttry {\n\t\t\tconst n = Number.isFinite(Number(nowTs)) ? Number(nowTs) : now();\n\t\t\tconst ttl = Math.max(500, Math.min(120000, Math.floor(Number(ttlMs) || 0)));\n\t\t\t// Throttle: avoid O(N) scans too frequently.\n\t\t\tif (n - _lastSwarmPruneAt < 1000) return;\n\t\t\t_lastSwarmPruneAt = n;\n\t\t\tfor (const [k, rec] of swarmCache.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!rec || typeof rec !== \"object\") {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst at = _safeNum(rec.at, 0);\n\t\t\t\t\tconst age = at > 0 ? Math.max(0, n - at) : Number.POSITIVE_INFINITY;\n\t\t\t\t\t// Delete stale completed entries once they exceed TTL.\n\t\t\t\t\tif (!rec.inFlight && age > ttl) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Safety valve: if an in-flight entry is *very* old, drop it too.\n\t\t\t\t\tif (rec.inFlight && age > Math.max(ttl * 3, 60000)) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t\trecentSwarm: [],\n\t};\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// function _compactEvolveAnyForPrompt(e) {\n\t// \ttry {\n\t// \t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t// \t\tif (!obj) return null;\n\t// \t\t// If this is a stored summary shape, use the existing compactor.\n\t// \t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t// \t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t// \t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t// \t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t// \t\t}\n\t// \t\treturn null;\n\t// \t} catch {\n\t// \t\treturn null;\n\t// \t}\n\t// }\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tswarm: (Array.isArray(_decisionMemory.recentSwarm) ? _decisionMemory.recentSwarm.slice(0, 2) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\t_pruneSwarmCache(nowTs, swarmTtlMs);\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p, kind);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\tconst swarmSummary = _summarizeSwarm(members);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tsummary: swarmSummary,\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: nowTs, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t_pushRing(_decisionMemory.recentSwarm, {\n\t\t\t\t\t\t\t\t\t\tat: now(),\n\t\t\t\t\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\t\t\t\t\tmint: mintFull || null,\n\t\t\t\t\t\t\t\t\t\tmembers: swarmObj?.members || [],\n\t\t\t\t\t\t\t\t\t}, 6);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed, kind);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t} catch {}\n\n\t\t\t\tif (TRAINING_CAPTURE?.enabled || uploadToGary) {\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tlet uploadToGary = null;\n\t\t\ttry {\n\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tlet baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\tif (!baseUrl && apiKey) baseUrl = \"http://127.0.0.1:8088\";\n\t\t\t\tif (baseUrl && apiKey) uploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t} catch {}\n\n\t\t\tconst wantLocal = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tconst wantRemote = !!uploadToGary;\n\t\t\tif (wantLocal || wantRemote) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"runner\",\n\t\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\t\tok: false,\n\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\tmeta: null,\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t\t} catch {}\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tappendTrainingCapture({\n\t\t\t\tmode: \"runner\",\n\t\t\t\tsource: String(prefix || \"AGENT\"),\n\t\t\t\tok: true,\n\t\t\t\terr: \"\",\n\t\t\t\tsystem: String(system || \"\").slice(0, 8000),\n\t\t\t\tuser: String(user || \"\").slice(0, 20000),\n\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t}, { storageKey: TRAINING_CAPTURE?.storageKey, maxEntries: TRAINING_CAPTURE?.maxEntries }).catch(() => {});\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "ed708e0e06a52e0c6075caeaab822966887bf254": {"t": 1785766662.3239915, "n": 103783, "h": "718052a584f25f41", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst k = String(kind || \"\").trim().toLowerCase();\n\tconst allowedPref = (k === \"buy\")\n\t\t? \"buy | skip\"\n\t\t: (k === \"sell\")\n\t\t\t? \"sell_all | sell_partial | hold | long_hold\"\n\t\t\t: \"(unset)\";\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: set JSON keys mode='agentic_prepass' and kind='\" + String(k || \"\") + \"'.\" +\n\t\t\"\\n\" +\n\t\t\"- REQUIRED: actionPreference MUST be one of: \" + allowedPref + \".\" +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj, expectedKind = \"\") {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || expectedKind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tif (kind === \"buy\") {\n\t\t\tif (actionPreference !== \"buy\" && actionPreference !== \"skip\") return null;\n\t\t}\n\t\tif (kind === \"sell\") {\n\t\t\tif (actionPreference !== \"sell_all\" && actionPreference !== \"sell_partial\" && actionPreference !== \"hold\" && actionPreference !== \"long_hold\") return null;\n\t\t}\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n\tfunction _summarizeSwarm(members = []) {\n\t\ttry {\n\t\t\tconst ms = Array.isArray(members) ? members : [];\n\t\t\tconst clean = ms.filter((m) => m && typeof m === \"object\");\n\t\t\tif (!clean.length) return null;\n\n\t\t\tconst actionScores = new Map();\n\t\t\tconst byRole = {};\n\t\t\tfor (const m of clean) {\n\t\t\t\tconst ap = String(m.actionPreference || \"\").trim().toLowerCase();\n\t\t\t\tconst conf = _clampNum(m.confidence, 0, 1);\n\t\t\t\tif (ap) actionScores.set(ap, (actionScores.get(ap) || 0) + (Number.isFinite(conf) ? conf : 0));\n\t\t\t\tconst r = String(m.role || \"\").trim().slice(0, 40);\n\t\t\t\tif (r) {\n\t\t\t\t\tbyRole[r] = {\n\t\t\t\t\t\tactionPreference: ap || null,\n\t\t\t\t\t\tconfidence: Number.isFinite(conf) ? conf : null,\n\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 3) : [],\n\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 3) : [],\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst consensus = Array.from(actionScores.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.map(([actionPreference, score]) => ({ actionPreference, score }))\n\t\t\t\t.slice(0, 3);\n\n\t\t\tconst top = consensus[0] || null;\n\t\t\tconst runnerUp = consensus[1] || null;\n\t\t\tconst margin = (top && runnerUp) ? (top.score - runnerUp.score) : null;\n\n\t\t\treturn {\n\t\t\t\tv: 1,\n\t\t\t\tconsensus: top ? { actionPreference: top.actionPreference, score: top.score, margin } : null,\n\t\t\t\trank: consensus,\n\t\t\t\tbyRole,\n\t\t\t};\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\tlet _lastSwarmPruneAt = 0;\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\tfunction _pruneSwarmCache(nowTs, ttlMs) {\n\t\ttry {\n\t\t\tconst n = Number.isFinite(Number(nowTs)) ? Number(nowTs) : now();\n\t\t\tconst ttl = Math.max(500, Math.min(120000, Math.floor(Number(ttlMs) || 0)));\n\t\t\t// Throttle: avoid O(N) scans too frequently.\n\t\t\tif (n - _lastSwarmPruneAt < 1000) return;\n\t\t\t_lastSwarmPruneAt = n;\n\t\t\tfor (const [k, rec] of swarmCache.entries()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (!rec || typeof rec !== \"object\") {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst at = _safeNum(rec.at, 0);\n\t\t\t\t\tconst age = at > 0 ? Math.max(0, n - at) : Number.POSITIVE_INFINITY;\n\t\t\t\t\t// Delete stale completed entries once they exceed TTL.\n\t\t\t\t\tif (!rec.inFlight && age > ttl) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Safety valve: if an in-flight entry is *very* old, drop it too.\n\t\t\t\t\tif (rec.inFlight && age > Math.max(ttl * 3, 60000)) {\n\t\t\t\t\t\tswarmCache.delete(k);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t\trecentSwarm: [],\n\t};\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// function _compactEvolveAnyForPrompt(e) {\n\t// \ttry {\n\t// \t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t// \t\tif (!obj) return null;\n\t// \t\t// If this is a stored summary shape, use the existing compactor.\n\t// \t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t// \t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t// \t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t// \t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t// \t\t}\n\t// \t\treturn null;\n\t// \t} catch {\n\t// \t\treturn null;\n\t// \t}\n\t// }\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tswarm: (Array.isArray(_decisionMemory.recentSwarm) ? _decisionMemory.recentSwarm.slice(0, 2) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\t_pruneSwarmCache(nowTs, swarmTtlMs);\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role, kind);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p, kind);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\tconst swarmSummary = _summarizeSwarm(members);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tsummary: swarmSummary,\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: nowTs, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t_pushRing(_decisionMemory.recentSwarm, {\n\t\t\t\t\t\t\t\t\t\tat: now(),\n\t\t\t\t\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\t\t\t\t\tmint: mintFull || null,\n\t\t\t\t\t\t\t\t\t\tmembers: swarmObj?.members || [],\n\t\t\t\t\t\t\t\t\t}, 6);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed, kind);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "60b7f64188c0928d894253267b51d06eb3159aae": {"t": 1785766664.1569428, "n": 100529, "h": "9ad3d2810d5f398c", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _buildAgenticNegotiationSchema(kind) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t// A compact, strict schema for a pre-decision negotiation pass.\n\t\t// All fields are required but allow nulls to reduce truncation failures.\n\t\treturn {\n\t\t\tname: \"fdv_agentic_negotiation_v1\",\n\t\t\tstrict: true,\n\t\t\tschema: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tmode: { type: [\"string\", \"null\"], enum: [\"agentic_prepass\", null] },\n\t\t\t\t\trole: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkind: { type: [\"string\", \"null\"] },\n\t\t\t\t\tactionPreference: { type: [\"string\", \"null\"] },\n\t\t\t\t\tconfidence: { type: [\"number\", \"null\"] },\n\t\t\t\t\tintent: { type: [\"string\", \"null\"] },\n\t\t\t\t\tkeyDrivers: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tguardrails: { anyOf: [{ type: \"array\", items: { type: \"string\" }, maxItems: 10 }, { type: \"null\" }] },\n\t\t\t\t\tproposed: {\n\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\trequired: [\"pct\", \"holdSeconds\"],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\ttune: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t\tforecast: { type: [\"object\", \"null\"] },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"buy\", \"sell\", \"tune\", \"forecast\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"mode\", \"role\", \"kind\", \"actionPreference\", \"confidence\", \"intent\", \"keyDrivers\", \"guardrails\", \"proposed\", \"note\"],\n\t\t\t},\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _agenticNegotiationEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (p !== \"openai\") return false;\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgenticNegotiation === true) return true;\n\t\t} catch {}\n\t\t// Default ON for OpenAI: creates a lightweight \"negotiate -> decide\" loop.\n\t\treturn _readBoolLs(\"fdv_agent_agentic_negotiation\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _buildAgenticNegotiationSystemPrompt(systemPromptFinal) {\n\tconst base = String(systemPromptFinal || \"\").trim();\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\t[\n\t\t\t\"AGENTIC NEGOTIATION PRE-PASS (do NOT output the final decision object):\",\n\t\t\t\"- Goal: negotiate tradeoffs (risk vs reward, route/liq vs momentum, size vs slippage) using ONLY provided inputs.\",\n\t\t\t\"- Identify hard blockers (no-route/quote failures, extreme rug signals, illiquidity) vs soft concerns.\",\n\t\t\t\"- Produce a compact plan + guardrails the final decision should obey.\",\n\t\t\t\"- Output ONLY valid JSON matching the provided schema.\",\n\t\t\t\"- Ignore any earlier output-format rules for the final decision; this is a pre-pass.\",\n\t\t\t\"- Keep strings short; no markdown; no extra keys.\",\n\t\t].join(\"\\n\")\n\t);\n}\n\nfunction _buildSwarmMemberSystemPrompt(systemPromptFinal, role) {\n\tconst base = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\tconst r = String(role || \"\").trim().toLowerCase();\n\tconst focus = (r === \"leader\")\n\t\t? [\n\t\t\t\"ROLE: TEAM_LEADER\",\n\t\t\t\"- Synthesize overall plan and the single best actionPreference.\",\n\t\t\t\"- Emphasize tradeoffs and decisive guardrails.\",\n\t\t]\n\t\t: (r === \"meme_flow\")\n\t\t\t? [\n\t\t\t\t\"ROLE: MEME_FLOW_ANALYST\",\n\t\t\t\t\"- Specialize in momentum/flow/leader-series/tape and entry timing.\",\n\t\t\t\t\"- If candlesTail exists, use it lightly; do not overfit.\",\n\t\t\t]\n\t\t\t: [\n\t\t\t\t\"ROLE: RISK_GUARD_ANALYST\",\n\t\t\t\t\"- Specialize in route viability, liquidity, quote failures, and rug/honeypot risk.\",\n\t\t\t\t\"- Prefer SKIP/HOLD when hard safety blockers appear.\",\n\t\t\t];\n\treturn (\n\t\tbase +\n\t\t\"\\n\\n\" +\n\t\tfocus.join(\"\\n\") +\n\t\t\"\\n\" +\n\t\t\"- Include top-level JSON key role with this role name.\"\n\t);\n}\n\nfunction _swarmEnabled({ provider, kind } = {}) {\n\ttry {\n\t\tconst p = String(provider || \"\").trim().toLowerCase();\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tif (k !== \"buy\" && k !== \"sell\") return false;\n\t\t// Do not swarm local Gary model; keep it lean.\n\t\tif (p === \"gary\") return false;\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentSwarm === true) return true;\n\t\t} catch {}\n\t\t// Default ON for non-Gary providers.\n\t\treturn _readBoolLs(\"fdv_agent_swarm\", true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction _validateAgenticNegotiation(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst mode = String(obj.mode || \"\").trim();\n\t\tif (mode !== \"agentic_prepass\") return null;\n\t\tconst role = String(obj.role || \"\").trim().slice(0, 40);\n\t\tconst kind = String(obj.kind || \"\").trim().toLowerCase();\n\t\tconst actionPreference = String(obj.actionPreference || \"\").trim().toLowerCase();\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst intent = String(obj.intent || \"\").trim().slice(0, 220);\n\t\tconst note = String(obj.note || \"\").trim().slice(0, 220);\n\t\tconst keyDrivers = Array.isArray(obj.keyDrivers)\n\t\t\t? obj.keyDrivers.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\t\tconst guardrails = Array.isArray(obj.guardrails)\n\t\t\t? obj.guardrails.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 8)\n\t\t\t: [];\n\n\t\tlet proposed = null;\n\t\tif (obj.proposed && typeof obj.proposed === \"object\") {\n\t\t\tconst out = { buy: null, sell: null };\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.buy && typeof obj.proposed.buy === \"object\") {\n\t\t\t\t\tconst solUi = _safeNum(obj.proposed.buy.solUi, NaN);\n\t\t\t\t\tconst slippageBps = _safeNum(obj.proposed.buy.slippageBps, NaN);\n\t\t\t\t\tout.buy = {\n\t\t\t\t\t\tsolUi: Number.isFinite(solUi) ? Math.max(0, solUi) : null,\n\t\t\t\t\t\tslippageBps: Number.isFinite(slippageBps) ? Math.max(0, Math.floor(slippageBps)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tif (obj.proposed.sell && typeof obj.proposed.sell === \"object\") {\n\t\t\t\t\tconst pct = _safeNum(obj.proposed.sell.pct, NaN);\n\t\t\t\t\tconst holdSeconds = _safeNum(obj.proposed.sell.holdSeconds, NaN);\n\t\t\t\t\tout.sell = {\n\t\t\t\t\t\tpct: Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.floor(pct))) : null,\n\t\t\t\t\t\tholdSeconds: Number.isFinite(holdSeconds) ? Math.max(0, Math.floor(holdSeconds)) : null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst tune = _validateTune(obj.proposed.tune);\n\t\t\t\tif (tune) out.tune = tune;\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst forecast = _validateForecast(obj.proposed.forecast);\n\t\t\t\tif (forecast) out.forecast = forecast;\n\t\t\t} catch {}\n\t\t\tproposed = out;\n\t\t}\n\n\t\treturn {\n\t\t\tmode: \"agentic_prepass\",\n\t\t\trole: role || null,\n\t\t\tkind: kind || null,\n\t\t\tactionPreference: actionPreference || null,\n\t\t\tconfidence,\n\t\t\tintent: intent || null,\n\t\t\tkeyDrivers,\n\t\t\tguardrails,\n\t\t\tproposed,\n\t\t\tnote: note || null,\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\tconst swarmCache = new Map();\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t\trecentSwarm: [],\n\t};\n\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst vRaw = ls.getItem(\"fdv_agent_decision_memory\");\n\t\t\tconst v = String(vRaw || \"\").trim().toLowerCase();\n\t\t\t// Default ON if unset/empty.\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _compactEvolveAnyForPrompt(e) {\n\t\ttry {\n\t\t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t\t\tif (!obj) return null;\n\t\t\t// If this is a stored summary shape, use the existing compactor.\n\t\t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t\t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t\t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t\t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tswarm: (Array.isArray(_decisionMemory.recentSwarm) ? _decisionMemory.recentSwarm.slice(0, 2) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\tlet agenticMeta = null;\n\t\tlet agenticText = \"\";\n\t\tlet agenticParsed = null;\n\t\tlet agenticValidated = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\t\t\tconst provider = String(cfgN?.provider || \"\").trim().toLowerCase();\n\t\t\tconst swarmOn = _swarmEnabled({ provider, kind });\n\t\t\tconst agenticOn = !swarmOn && _agenticNegotiationEnabled({ provider, kind });\n\n\t\t\tconst baseTemperature = Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15;\n\t\t\tconst baseMaxTokens = Math.max(\n\t\t\t\t120,\n\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t);\n\n\t\t\tconst reasoningSessionKey = wantsReasoning ? _ensureReasoningSessionKey() : \"\";\n\t\t\tconst reasoningArgsBase = wantsReasoning\n\t\t\t\t? { reasoningSessionKey, reasoningIncludeEncrypted: true }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFirstCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: !!reasoningReset }\n\t\t\t\t: {};\n\t\t\tconst reasoningArgsForFollowupCall = wantsReasoning\n\t\t\t\t? { ...reasoningArgsBase, reasoningReset: false }\n\t\t\t\t: {};\n\n\t\t\tlet didAwaitedReasoningPrepass = false;\n\n\t\t\tconst reqFinal = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: baseTemperature,\n\t\t\t\tmaxTokens: baseMaxTokens,\n\t\t\t\t...(provider === \"openai\" ? { verbosity: \"medium\" } : {}),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treqFinal.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\n\t\t\tif (swarmOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`swarm prepass(SWR) kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst roles = [\n\t\t\t\t\t{ role: \"leader\", tempAdd: 0.02 },\n\t\t\t\t\t{ role: \"meme_flow\", tempAdd: 0.05 },\n\t\t\t\t\t{ role: \"risk_guard\", tempAdd: -0.02 },\n\t\t\t\t];\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\").slice(0, 64);\n\t\t\t\tconst swarmKey = `${provider}:${String(cfgModel || \"\").trim().toLowerCase()}:${String(kind || \"\").trim().toLowerCase()}:${mintFull}`;\n\t\t\t\tconst nowTs = now();\n\t\t\t\tconst swarmTtlMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_cache_ttl_ms\", \"12000\"), 12000);\n\t\t\t\t\t\treturn Math.max(1500, Math.min(120000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 12000;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst swarmRevalMs = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst raw = _safeNum(_readLs(\"fdv_agent_swarm_reval_ms\", \"4500\"), 4500);\n\t\t\t\t\t\treturn Math.max(500, Math.min(60000, Math.floor(raw)));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn 4500;\n\t\t\t\t\t}\n\t\t\t\t})();\n\n\t\t\t\tconst prevSwarmRec = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst r = swarmCache.get(swarmKey);\n\t\t\t\t\t\treturn (r && typeof r === \"object\") ? r : null;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tconst prevAge = prevSwarmRec ? Math.max(0, nowTs - _safeNum(prevSwarmRec.at, 0)) : Number.POSITIVE_INFINITY;\n\t\t\t\tconst hasPrev = !!(prevSwarmRec && prevSwarmRec.swarm && typeof prevSwarmRec.swarm === \"object\");\n\t\t\t\tconst prevFresh = hasPrev && prevAge <= swarmTtlMs;\n\n\t\t\t\t// Immediate: attach cached swarm (stale or fresh) so the final decision can use it.\n\t\t\t\tif (hasPrev) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.swarm = prevSwarmRec.swarm;\n\t\t\t\t\t\tuserMsg.swarmCache = { ageMs: prevAge, fresh: !!prevFresh };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\n\t\t\t\tconst shouldRevalidate = (!hasPrev) || (prevAge >= swarmRevalMs);\n\t\t\t\tconst inFlight = !!(prevSwarmRec && prevSwarmRec.inFlight);\n\t\t\t\tif (shouldRevalidate && !inFlight) {\n\t\t\t\t\tconst mkReq = (role, tempAdd = 0) => {\n\t\t\t\t\t\tconst sys = _buildSwarmMemberSystemPrompt(systemPromptFinal, role);\n\t\t\t\t\t\tconst rKey = (wantsReasoning && reasoningSessionKey)\n\t\t\t\t\t\t\t? `${String(reasoningSessionKey)}:swarm:${String(role)}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsystem: sys,\n\t\t\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\t\t\ttemperature: Math.max(0, Math.min(0.45, baseTemperature + tempAdd)),\n\t\t\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t\t\t...(wantsReasoning && rKey\n\t\t\t\t\t\t\t\t? { reasoningSessionKey: rKey, reasoningReset: !!reasoningReset, reasoningIncludeEncrypted: true }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\n\t\t\t\t\tconst promise = (async () => {\n\t\t\t\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\t\t\t\troles.map((rr) => client.chatJsonWithMeta(mkReq(rr.role, rr.tempAdd)))\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst members = [];\n\t\t\t\t\t\tfor (let i = 0; i < settled.length; i++) {\n\t\t\t\t\t\t\tconst rr = roles[i];\n\t\t\t\t\t\t\tconst s = settled[i];\n\t\t\t\t\t\t\tif (!s || s.status !== \"fulfilled\") continue;\n\t\t\t\t\t\t\tconst mm = s.value;\n\t\t\t\t\t\t\tconst t = String(mm?.text || \"\");\n\t\t\t\t\t\t\tconst p = _safeJsonParse(t);\n\t\t\t\t\t\t\tconst v = _validateAgenticNegotiation(p);\n\t\t\t\t\t\t\tif (v) {\n\t\t\t\t\t\t\t\tv.role = v.role || String(rr.role);\n\t\t\t\t\t\t\t\tmembers.push(v);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!members.length) return null;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tcreatedAt: now(),\n\t\t\t\t\t\t\tmembers: members.map((m) => ({\n\t\t\t\t\t\t\t\trole: m.role || null,\n\t\t\t\t\t\t\t\tactionPreference: m.actionPreference || null,\n\t\t\t\t\t\t\t\tconfidence: Number.isFinite(Number(m.confidence)) ? _clampNum(m.confidence, 0, 1) : null,\n\t\t\t\t\t\t\t\tkeyDrivers: Array.isArray(m.keyDrivers) ? m.keyDrivers.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tguardrails: Array.isArray(m.guardrails) ? m.guardrails.slice(0, 6) : [],\n\t\t\t\t\t\t\t\tproposed: m.proposed || null,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t};\n\t\t\t\t\t})();\n\n\t\t\t\t\ttry { swarmCache.set(swarmKey, { at: prevSwarmRec?.at || 0, swarm: prevSwarmRec?.swarm || null, inFlight: promise }); } catch {}\n\n\t\t\t\t\tpromise\n\t\t\t\t\t\t.then((swarmObj) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (!swarmObj) return;\n\t\t\t\t\t\t\t\tswarmCache.set(swarmKey, { at: now(), swarm: swarmObj, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (_decisionMemoryEnabled()) {\n\t\t\t\t\t\t\t\t\t_pushRing(_decisionMemory.recentSwarm, {\n\t\t\t\t\t\t\t\t\t\tat: now(),\n\t\t\t\t\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\t\t\t\t\tmint: mintFull || null,\n\t\t\t\t\t\t\t\t\t\tmembers: swarmObj?.members || [],\n\t\t\t\t\t\t\t\t\t}, 6);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst cur = swarmCache.get(swarmKey);\n\t\t\t\t\t\t\t\tif (cur && typeof cur === \"object\") swarmCache.set(swarmKey, { at: cur.at || 0, swarm: cur.swarm || null, inFlight: null });\n\t\t\t\t\t\t\t} catch {}\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Fallback: single agentic pre-pass (legacy), only when swarm is disabled.\n\t\t\telse if (agenticOn && client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\ttry {\n\t\t\t\t\t_log(`agentic prepass kind=${String(kind)} model=${String(cfgModel || \"\") || provider}`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t\tconst sysAgentic = _buildAgenticNegotiationSystemPrompt(systemPromptFinal);\n\t\t\t\tconst reqAgentic = {\n\t\t\t\t\tsystem: sysAgentic,\n\t\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\t\ttemperature: Math.max(0, Math.min(0.35, baseTemperature + 0.05)),\n\t\t\t\t\tmaxTokens: Math.max(220, Math.min(900, Math.floor(baseMaxTokens + 220))),\n\t\t\t\t\tverbosity: \"medium\",\n\t\t\t\t\t...reasoningArgsForFirstCall,\n\t\t\t\t};\n\t\t\t\tagenticMeta = await client.chatJsonWithMeta(reqAgentic);\n\t\t\t\tif (wantsReasoning) didAwaitedReasoningPrepass = true;\n\t\t\t\tagenticText = String(agenticMeta?.text || \"\");\n\t\t\t\tagenticParsed = _safeJsonParse(agenticText);\n\t\t\t\tagenticValidated = _validateAgenticNegotiation(agenticParsed);\n\t\t\t\tif (agenticValidated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserMsg.agentic = {\n\t\t\t\t\t\t\tv: 1,\n\t\t\t\t\t\t\tnegotiatedAt: now(),\n\t\t\t\t\t\t\tprepass: agenticValidated,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry { reqFinal.user = JSON.stringify(userMsg); } catch {}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (wantsReasoning) {\n\t\t\t\tObject.assign(\n\t\t\t\t\treqFinal,\n\t\t\t\t\tdidAwaitedReasoningPrepass ? reasoningArgsForFollowupCall : reasoningArgsForFirstCall\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(reqFinal);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(reqFinal);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "b2771129dda918a34cff38cf88884ed857ca760e": {"t": 1785766666.2337313, "n": 86028, "h": "f715b5d30aa6424c", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst candles = Array.isArray(past.candles) ? past.candles : null;\n\t\t\t\tconst format = Array.isArray(past.format) ? past.format : null;\n\t\t\t\tconst last = Array.isArray(candles) && candles.length ? candles[candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tlet candlesTail = null;\n\t\t\t\ttry {\n\t\t\t\t\tif (Array.isArray(candles) && candles.length) {\n\t\t\t\t\t\tconst tailN = 12;\n\t\t\t\t\t\tconst tail = candles.slice(Math.max(0, candles.length - tailN));\n\t\t\t\t\t\tconst idxTs = Array.isArray(format) ? Math.max(0, format.indexOf(\"ts\")) : 0;\n\t\t\t\t\t\tconst idxO = Array.isArray(format) ? Math.max(0, format.indexOf(\"oUsd\")) : 1;\n\t\t\t\t\t\tconst idxC = Array.isArray(format) ? Math.max(0, format.indexOf(\"cUsd\")) : 4;\n\t\t\t\t\t\tcandlesTail = tail\n\t\t\t\t\t\t\t.map((row) => {\n\t\t\t\t\t\t\t\tif (!Array.isArray(row)) return null;\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tts: row[idxTs] ?? null,\n\t\t\t\t\t\t\t\t\to: row[idxO] ?? null,\n\t\t\t\t\t\t\t\t\tc: row[idxC] ?? null,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.filter(Boolean);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\ttsUnit: past.tsUnit ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t\tcandlesTail: candlesTail,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t};\n\n\t// Default OFF (prompt stability). Enable via:\n\t// localStorage.setItem('fdv_agent_decision_memory','1')\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_decision_memory\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _compactEvolveAnyForPrompt(e) {\n\t\ttry {\n\t\t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t\t\tif (!obj) return null;\n\t\t\t// If this is a stored summary shape, use the existing compactor.\n\t\t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t\t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t\t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t\t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treq.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\t\t\tif (wantsReasoning) {\n\t\t\t\treq.reasoningSessionKey = _ensureReasoningSessionKey();\n\t\t\t\treq.reasoningReset = !!reasoningReset;\n\t\t\t\t// Default ON: request encrypted reasoning content when supported.\n\t\t\t\treq.reasoningIncludeEncrypted = true;\n\t\t\t}\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "dc5f7660bdd922dca6181fe5cba3ca58c260d4c2": {"t": 1785766668.146973, "n": 85133, "h": "7111f66e8d5e27de", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst last = Array.isArray(past.candles) && past.candles.length ? past.candles[past.candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t};\n\n\t// Default OFF (prompt stability). Enable via:\n\t// localStorage.setItem('fdv_agent_decision_memory','1')\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_decision_memory\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _compactEvolveAnyForPrompt(e) {\n\t\ttry {\n\t\t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t\t\tif (!obj) return null;\n\t\t\t// If this is a stored summary shape, use the existing compactor.\n\t\t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t\t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t\t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t\t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treq.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\t\t\tif (wantsReasoning) {\n\t\t\t\treq.reasoningSessionKey = _ensureReasoningSessionKey();\n\t\t\t\treq.reasoningReset = !!reasoningReset;\n\t\t\t\t// Default ON: request encrypted reasoning content when supported.\n\t\t\t\treq.reasoningIncludeEncrypted = true;\n\t\t\t}\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet captureText = String(text || \"\");\n\t\t\t\ttry {\n\t\t\t\t\t// Some providers return parsed JSON but no raw `text`. Ensure we still store the response.\n\t\t\t\t\tif (!captureText && validated && typeof validated === \"object\") captureText = JSON.stringify(validated);\n\t\t\t\t} catch {}\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(captureText || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "358cb25d586bd2c26e6379145e9e04ba3a92192b": {"t": 1785766669.9679763, "n": 84852, "h": "ce280b631f45383c", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal but include stateOverride so the model can size to balance.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\ttry {\n\t\t\t\t\tconst st = userMsg.state && typeof userMsg.state === \"object\" ? userMsg.state : null;\n\t\t\t\t\tif (st && Object.keys(st).length) out.state = st;\n\t\t\t\t} catch {}\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst last = Array.isArray(past.candles) && past.candles.length ? past.candles[past.candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t};\n\n\t// Default OFF (prompt stability). Enable via:\n\t// localStorage.setItem('fdv_agent_decision_memory','1')\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_decision_memory\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _compactEvolveAnyForPrompt(e) {\n\t\ttry {\n\t\t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t\t\tif (!obj) return null;\n\t\t\t// If this is a stored summary shape, use the existing compactor.\n\t\t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t\t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t\t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t\t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treq.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\t\t\tif (wantsReasoning) {\n\t\t\t\treq.reasoningSessionKey = _ensureReasoningSessionKey();\n\t\t\t\treq.reasoningReset = !!reasoningReset;\n\t\t\t\t// Default ON: request encrypted reasoning content when supported.\n\t\t\t\treq.reasoningIncludeEncrypted = true;\n\t\t\t}\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\t// Auto-enable upload-to-gary when Gary config is populated, regardless of the active LLM provider.\n\t\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\t\tconst baseUrl = String((o && (o.garyBaseUrl || o.garyUrl)) ? (o.garyBaseUrl || o.garyUrl) : _readLs(\"fdv_gary_base_url\", \"\")).trim();\n\t\t\t\t\tconst apiKey = String((o && (o.garyApiKey || o.garyKey)) ? (o.garyApiKey || o.garyKey) : _readLs(\"fdv_gary_key\", \"\")).trim();\n\t\t\t\t\tconst hmacSecret = String((o && (o.garyHmacSecret || o.hmacSecret)) ? (o.garyHmacSecret || o.hmacSecret) : _readLs(\"fdv_gary_hmac_secret\", \"\")).trim();\n\t\t\t\t\tif (baseUrl && apiKey) {\n\t\t\t\t\t\tuploadToGary = { provider: \"gary\", baseUrl, apiKey, hmacSecret };\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "d7c0d6e46314fbcb75b294f1047f02facf95b150": {"t": 1785766671.9348452, "n": 83653, "h": "afff736117372c07", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj, allowedKeys = null) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tconst allow = (() => {\n\t\t\ttry {\n\t\t\t\tif (!Array.isArray(allowedKeys) || !allowedKeys.length) return null;\n\t\t\t\treturn new Set(allowedKeys.map((k) => String(k || \"\").trim()).filter(Boolean).slice(0, 200));\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\n\t\tconst _shouldAllowKey = (k) => {\n\t\t\ttry {\n\t\t\t\tif (!allow) return true;\n\t\t\t\treturn allow.has(String(k || \"\").trim());\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\tconst _applyKv = (cfg, keyRaw, v) => {\n\t\t\ttry {\n\t\t\t\tconst key = String(keyRaw || \"\").slice(0, 80);\n\t\t\t\tif (!key) return;\n\t\t\t\tif (!_shouldAllowKey(key)) return;\n\t\t\t\t// Structured Outputs schemas may return explicit nulls for \"no-op\" keys; ignore those.\n\t\t\t\tif (v == null) return;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t} catch {}\n\t\t};\n\n\t\t// Preferred compact form (Structured Outputs): patches[]\n\t\tif (Array.isArray(obj.patches)) {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const p of obj.patches) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 24) break;\n\t\t\t\tif (!p || typeof p !== \"object\") continue;\n\t\t\t\t_applyKv(cfg, p.key, p.value);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t\treturn out;\n\t\t}\n\n\t\t// Back-compat: config object\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\t_applyKv(cfg, k, v);\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _buildDecisionSchema(kind, allowedConfigKeys = []) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst tune = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\ttakeProfitPct: { type: [\"number\", \"null\"] },\n\t\t\t\tstopLossPct: { type: [\"number\", \"null\"] },\n\t\t\t\ttrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminProfitToTrailPct: { type: [\"number\", \"null\"] },\n\t\t\t\tminHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tmaxHoldSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tbuyPct: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimMinWinProb: { type: [\"number\", \"null\"] },\n\t\t\t\tentrySimHorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\n\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\"stopLossPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\"buyPct\",\n\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t],\n\t\t};\n\t\tconst forecast = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\thorizonSecs: { type: [\"integer\", \"null\"] },\n\t\t\t\tupProb: { type: [\"number\", \"null\"] },\n\t\t\t\tdownProb: { type: [\"number\", \"null\"] },\n\t\t\t\texpectedMovePct: { type: [\"number\", \"null\"] },\n\t\t\t\tregime: { type: [\"string\", \"null\"] },\n\t\t\t\tnote: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"horizonSecs\", \"upProb\", \"downProb\", \"expectedMovePct\", \"regime\", \"note\"],\n\t\t};\n\t\tconst evolve = {\n\t\t\ttype: \"object\",\n\t\t\tadditionalProperties: false,\n\t\t\tproperties: {\n\t\t\t\toutcomeTs: { type: [\"integer\", \"null\"] },\n\t\t\t\tselfCritique: { type: [\"string\", \"null\"] },\n\t\t\t\tlesson: { type: [\"string\", \"null\"] },\n\t\t\t},\n\t\t\trequired: [\"outcomeTs\", \"selfCritique\", \"lesson\"],\n\t\t};\n\n\t\tif (k === \"buy\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_buy_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"buy\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"buy\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tbuy: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tsolUi: { type: [\"number\", \"null\"] },\n\t\t\t\t\t\t\t\t\t\tslippageBps: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"solUi\", \"slippageBps\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"buy\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"sell\") {\n\t\t\treturn {\n\t\t\t\tname: \"fdv_sell_decision_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"sell\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"sell_all\", \"sell_partial\", \"hold\", \"long_hold\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tholdSeconds: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\tsell: {\n\t\t\t\t\t\t\tanyOf: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tpct: { type: [\"integer\", \"null\"] },\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"pct\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{ type: \"null\" },\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttune: { anyOf: [tune, { type: \"null\" }] },\n\t\t\t\t\t\tforecast: { anyOf: [forecast, { type: \"null\" }] },\n\t\t\t\t\t\tevolve: { anyOf: [evolve, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"holdSeconds\", \"sell\", \"tune\", \"forecast\", \"evolve\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tif (k === \"config_scan\") {\n\t\t\tconst keys = Array.isArray(allowedConfigKeys) ? allowedConfigKeys.map((x) => String(x || \"\").trim()).filter(Boolean).slice(0, 80) : [];\n\t\t\tconst patchKey = (keys && keys.length)\n\t\t\t\t? { type: \"string\", enum: keys }\n\t\t\t\t: { type: \"string\" };\n\t\t\tconst patchItem = {\n\t\t\t\ttype: \"object\",\n\t\t\t\tadditionalProperties: false,\n\t\t\t\tproperties: {\n\t\t\t\t\tkey: patchKey,\n\t\t\t\t\tvalue: { type: [\"number\", \"boolean\", \"string\", \"null\"] },\n\t\t\t\t},\n\t\t\t\trequired: [\"key\", \"value\"],\n\t\t\t};\n\t\t\tconst patches = {\n\t\t\t\ttype: \"array\",\n\t\t\t\titems: patchItem,\n\t\t\t\tmaxItems: 12,\n\t\t\t};\n\t\t\treturn {\n\t\t\t\tname: \"fdv_config_scan_v1\",\n\t\t\t\tstrict: true,\n\t\t\t\tschema: {\n\t\t\t\t\ttype: \"object\",\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t\tproperties: {\n\t\t\t\t\t\tkind: { type: \"string\", enum: [\"config_scan\"] },\n\t\t\t\t\t\taction: { type: \"string\", enum: [\"apply\", \"skip\"] },\n\t\t\t\t\t\tconfidence: { type: \"number\" },\n\t\t\t\t\t\treason: { type: \"string\" },\n\t\t\t\t\t\tpatches: { anyOf: [patches, { type: \"null\" }] },\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"kind\", \"action\", \"confidence\", \"reason\", \"patches\"],\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\n\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\ttry {\n\t\t\tif (kind === \"sell\") {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tif (pp.evolve && typeof pp.evolve === \"object\") keep.evolve = _compactEvolveAnyForPrompt(pp.evolve) || _redactDeep(pp.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tout.payload = keep;\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\ttry {\n\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t} catch {}\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (p.evolve && typeof p.evolve === \"object\") payload.evolve = _compactEvolveAnyForPrompt(p.evolve) || _redactDeep(p.evolve, { maxDepth: 5, maxKeys: 60 });\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\t// Provide a tiny decision-critical summary for sell prompts.\n\t\t\ttry {\n\t\t\t\tconst c = payload.ctx && typeof payload.ctx === \"object\" ? payload.ctx : null;\n\t\t\t\tconst pos = payload.pos && typeof payload.pos === \"object\" ? payload.pos : null;\n\t\t\t\tconst cfg = c && c.cfg && typeof c.cfg === \"object\" ? c.cfg : null;\n\t\t\t\tconst t = pos && pos.tickNow && typeof pos.tickNow === \"object\" ? pos.tickNow : null;\n\t\t\t\tconst nowTs = c ? _safeNum(c.nowTs, null) : null;\n\t\t\t\tconst acquiredAt = pos ? _safeNum(pos.acquiredAt, null) : null;\n\t\t\t\tpayload.summary = {\n\t\t\t\t\tagentRisk: c?.agentRisk ?? null,\n\t\t\t\t\tpnlNetPct: c?.pnlNetPct ?? null,\n\t\t\t\t\tinMinHold: c?.inMinHold ?? null,\n\t\t\t\t\tinSellGuard: c?.inSellGuard ?? null,\n\t\t\t\t\thasPending: c?.hasPending ?? null,\n\t\t\t\t\trugSev: c?.rugSev ?? null,\n\t\t\t\t\theldSecs: (nowTs != null && acquiredAt != null) ? Math.max(0, Math.floor((nowTs - acquiredAt) / 1000)) : null,\n\t\t\t\t\tcfgTpPct: cfg?.takeProfitPct ?? null,\n\t\t\t\t\tcfgSlPct: cfg?.stopLossPct ?? null,\n\t\t\t\t\tposTpPct: pos?.tpPct ?? null,\n\t\t\t\t\tposSlPct: pos?.slPct ?? null,\n\t\t\t\t\tposTrailPct: pos?.trailPct ?? null,\n\t\t\t\t\tpx: t ? { priceUsd: t.priceUsd ?? null, liqUsd: t.liqUsd ?? null, change5m: t.change5m ?? null, change1h: t.change1h ?? null } : undefined,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst last = Array.isArray(past.candles) && past.candles.length ? past.candles[past.candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tlet _reasoningSessionKey = \"\";\n\tlet _reasoningSessionModel = \"\";\n\n\t// Stable, tiny memory to carry decision context across turns.\n\t// This is intentionally small and safe to include in every prompt.\n\tconst _decisionMemory = {\n\t\tv: 1,\n\t\tstartedAt: now(),\n\t\triskPosture: { agentRisk: \"safe\", fullAiControl: false, updatedAt: 0 },\n\t\trecentOutcomes: [],\n\t\trecentDecisions: [],\n\t\trecentBuySkips: [],\n\t};\n\n\t// Default OFF (prompt stability). Enable via:\n\t// localStorage.setItem('fdv_agent_decision_memory','1')\n\tfunction _decisionMemoryEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentDecisionMemory === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_decision_memory\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _evolvePromptEnabled() {\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === false) return false;\n\t\t\tif (globalThis && globalThis.__fdvAgentEvolvePrompt === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return true;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_evolve_prompt\") || \"\").trim().toLowerCase();\n\t\t\tif (!v) return true;\n\t\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tfunction _compactEvolveForPrompt(summary) {\n\t\ttry {\n\t\t\tconst s = (summary && typeof summary === \"object\") ? summary : null;\n\t\t\tif (!s) return null;\n\t\t\tconst p = (s.payload && typeof s.payload === \"object\") ? s.payload : null;\n\n\t\t\tconst stats0 = (p && p.stats && typeof p.stats === \"object\") ? p.stats : null;\n\t\t\tconst todo0 = (p && p.todo && typeof p.todo === \"object\") ? p.todo : null;\n\t\t\tconst rules0 = (p && Array.isArray(p.rules)) ? p.rules : null;\n\n\t\t\tconst out = { v: 1 };\n\t\t\tif (stats0) {\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: (() => { const n = _safeNum(stats0.n, NaN); return Number.isFinite(n) ? Math.max(0, Math.min(999, Math.floor(n))) : null; })(),\n\t\t\t\t\twinRate: (() => { const wr = _safeNum(stats0.winRate, NaN); return Number.isFinite(wr) ? _clampNum(wr, 0, 1) : null; })(),\n\t\t\t\t\tavgPnlSol: (() => { const v = _safeNum(stats0.avgPnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tbest: (() => { const v = _safeNum(stats0.best, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tworst: (() => { const v = _safeNum(stats0.worst, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\tpendingCritiques: (() => { const v = _safeNum(stats0.pendingCritiques, NaN); return Number.isFinite(v) ? Math.max(0, Math.min(999, Math.floor(v))) : null; })(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (todo0) {\n\t\t\t\tout.todo = {\n\t\t\t\t\toutcomeTs: _safeNum(todo0.outcomeTs, 0) || null,\n\t\t\t\t\tmint8: String(todo0.mint8 || String(todo0.mint || \"\").slice(0, 8) || \"\").slice(0, 8) || null,\n\t\t\t\t\tkind: String(todo0.kind || \"\").slice(0, 24) || null,\n\t\t\t\t\tpnlSol: (() => { const v = _safeNum(todo0.pnlSol, NaN); return Number.isFinite(v) ? v : null; })(),\n\t\t\t\t\taction: String(todo0.decisionAction || todo0.action || \"\").slice(0, 24) || null,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (rules0 && rules0.length) {\n\t\t\t\tout.rules = rules0.slice(0, 3).map((r) => ({\n\t\t\t\t\tt: String(r?.text || r?.t || \"\").slice(0, 140),\n\t\t\t\t\th: Math.max(0, Math.min(99999, Math.floor(_safeNum(r?.hits ?? r?.h ?? r?.hitCount, 0)))),\n\t\t\t\t})).filter((r) => r.t);\n\t\t\t}\n\n\t\t\t// Legacy fallback: parse stats/todo/rules out of old multiline EVOLVE text.\n\t\t\tif (!out.stats && (typeof s.text === \"string\" || typeof s.prompt === \"string\")) {\n\t\t\t\tconst raw = String(s.prompt || s.text || \"\");\n\t\t\t\tconst txt = raw.replace(/\\r\\n/g, \"\\n\");\n\t\t\t\tconst mStats = txt.match(/winRate=(\\d+)%\\s+avgPnlSol=([-0-9.]+)/i);\n\t\t\t\tconst mBest = txt.match(/best=([-0-9.]+)/i);\n\t\t\t\tconst mWorst = txt.match(/worst=([-0-9.]+)/i);\n\t\t\t\tconst mPending = txt.match(/pendingCritiques=(\\d+)/i);\n\t\t\t\tconst mN = txt.match(/EVOLVE:\\s*last\\s+(\\d+)\\s+outcomes/i);\n\t\t\t\tout.stats = {\n\t\t\t\t\tn: mN ? Math.max(0, Math.min(999, parseInt(mN[1], 10))) : null,\n\t\t\t\t\twinRate: mStats ? _clampNum(parseInt(mStats[1], 10) / 100, 0, 1) : null,\n\t\t\t\t\tavgPnlSol: mStats ? _safeNum(mStats[2], null) : null,\n\t\t\t\t\tbest: mBest ? _safeNum(mBest[1], null) : null,\n\t\t\t\t\tworst: mWorst ? _safeNum(mWorst[1], null) : null,\n\t\t\t\t\tpendingCritiques: mPending ? Math.max(0, Math.min(999, parseInt(mPending[1], 10))) : null,\n\t\t\t\t};\n\t\t\t\tconst mTodo = txt.match(/EVOLVE TODO:\\s*outcomeTs=(\\d+)\\s+mint=([^\\s…]+).*?kind=([^\\s]+)\\s+pnlSol=([-0-9.]+).*?action=([^\\s]+)?/i);\n\t\t\t\tif (mTodo) {\n\t\t\t\t\tout.todo = {\n\t\t\t\t\t\toutcomeTs: _safeNum(mTodo[1], 0) || null,\n\t\t\t\t\t\tmint8: String(mTodo[2] || \"\").slice(0, 8) || null,\n\t\t\t\t\t\tkind: String(mTodo[3] || \"\").slice(0, 24) || null,\n\t\t\t\t\t\tpnlSol: _safeNum(mTodo[4], null),\n\t\t\t\t\t\taction: String(mTodo[5] || \"\").slice(0, 24) || null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst rulesSection = txt.split(/EVOLVE RULES[^\\n]*:\\s*/i)[1] || \"\";\n\t\t\t\t\tif (rulesSection) {\n\t\t\t\t\t\tconst lines = rulesSection.split(\"\\n\").map((l) => l.trim()).filter((l) => l.startsWith(\"- \"));\n\t\t\t\t\t\tconst rules = [];\n\t\t\t\t\t\tfor (const l of lines) {\n\t\t\t\t\t\t\tif (rules.length >= 3) break;\n\t\t\t\t\t\t\tconst hits = (() => { const m = l.match(/\\(hits=(\\d+)\\)/i); return m ? parseInt(m[1], 10) : 0; })();\n\t\t\t\t\t\t\trules.push({ t: l.replace(/^[-\\s]+/, \"\").replace(/\\s*\\(hits=.*?\\)\\s*$/i, \"\").slice(0, 140), h: Math.max(0, Math.min(99999, hits)) });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (rules.length) out.rules = rules;\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\n\t\t\t// Drop null-heavy objects.\n\t\t\ttry {\n\t\t\t\tif (out.stats && Object.values(out.stats).every((v) => v == null)) delete out.stats;\n\t\t\t\tif (out.todo && Object.values(out.todo).every((v) => v == null)) delete out.todo;\n\t\t\t\tif (Array.isArray(out.rules) && !out.rules.length) delete out.rules;\n\t\t\t} catch {}\n\n\t\t\treturn (out.stats || out.todo || out.rules) ? out : null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _compactEvolveAnyForPrompt(e) {\n\t\ttry {\n\t\t\tconst obj = (e && typeof e === \"object\") ? e : null;\n\t\t\tif (!obj) return null;\n\t\t\t// If this is a stored summary shape, use the existing compactor.\n\t\t\tif (obj.payload && typeof obj.payload === \"object\") return _compactEvolveForPrompt(obj);\n\t\t\t// If this already looks like the compact evolve payload shape, normalize it.\n\t\t\tif (obj.stats || obj.todo || obj.rules || obj.text || obj.prompt) {\n\t\t\t\treturn _compactEvolveForPrompt({ payload: obj, text: obj.text, prompt: obj.prompt });\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _pushRing(arr, item, maxN) {\n\t\ttry {\n\t\t\tif (!Array.isArray(arr)) return;\n\t\t\tarr.unshift(item);\n\t\t\tif (arr.length > maxN) arr.length = maxN;\n\t\t} catch {}\n\t}\n\n\tfunction _deriveSkipCodeFromPayload(p) {\n\t\ttry {\n\t\t\tconst g = p && p.signals && p.signals.gates && typeof p.signals.gates === \"object\" ? p.signals.gates : null;\n\t\t\tif (!g) return \"unknown\";\n\t\t\tif (g.cooldown && g.cooldown.ok === false) return \"cooldown\";\n\t\t\tif (g.buyWarmup && g.buyWarmup.ready === false) return \"warmup\";\n\t\t\tif (g.manualEdge && g.manualEdge.ok === false) return \"manual_edge\";\n\t\t\tif (g.entryCost && g.entryCost.ok === false) return \"entry_cost\";\n\t\t\tif (g.sim && g.sim.ready === false) return \"entry_sim\";\n\t\t\tif (g.finalGateReady === false) return \"final_gate\";\n\t\t\tif (g.onchainLabels && Array.isArray(g.onchainLabels.labels) && g.onchainLabels.labels.length) return \"onchain_label_risk\";\n\t\t\treturn \"unknown\";\n\t\t} catch {\n\t\t\treturn \"unknown\";\n\t\t}\n\t}\n\n\tfunction _updateDecisionMemoryFromPayload(payload) {\n\t\ttry {\n\t\t\tconst p = payload && typeof payload === \"object\" ? payload : null;\n\t\t\tif (!p) return;\n\t\t\tif (p.signals && typeof p.signals === \"object\") {\n\t\t\t\tconst s = p.signals;\n\t\t\t\tif (s.agentRisk) _decisionMemory.riskPosture.agentRisk = String(s.agentRisk || \"\").slice(0, 16) || _decisionMemory.riskPosture.agentRisk;\n\t\t\t\tif (typeof s.fullAiControl === \"boolean\") _decisionMemory.riskPosture.fullAiControl = s.fullAiControl;\n\t\t\t\t_decisionMemory.riskPosture.updatedAt = now();\n\n\t\t\t\tif (s.outcomes && typeof s.outcomes === \"object\" && s.outcomes.lastTrade && typeof s.outcomes.lastTrade === \"object\") {\n\t\t\t\t\tconst lt = s.outcomes.lastTrade;\n\t\t\t\t\tconst ts = _safeNum(lt.ts, 0);\n\t\t\t\t\tif (ts > 0) {\n\t\t\t\t\t\t_pushRing(_decisionMemory.recentOutcomes, {\n\t\t\t\t\t\t\tts,\n\t\t\t\t\t\t\tkind: lt.kind ? String(lt.kind).slice(0, 24) : null,\n\t\t\t\t\t\t\tdecisionAction: lt.decisionAction ? String(lt.decisionAction).slice(0, 20) : null,\n\t\t\t\t\t\t\tpnlSol: (typeof lt.pnlSol === \"number\") ? lt.pnlSol : null,\n\t\t\t\t\t\t\tupDown: lt.upDown ? String(lt.upDown).slice(0, 8) : null,\n\t\t\t\t\t\t}, 3);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _updateDecisionMemoryAfterDecision({ kind, payload, decision, ok }) {\n\t\ttry {\n\t\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\t\tconst d = (decision && typeof decision === \"object\") ? decision : null;\n\t\t\tconst p = (payload && typeof payload === \"object\") ? payload : null;\n\t\t\tconst mint = (() => {\n\t\t\t\ttry {\n\t\t\t\t\tconst m = String(p?.mint || p?.targetMint || p?.proposed?.mint || \"\").trim();\n\t\t\t\t\treturn m ? m.slice(0, 44) : \"\";\n\t\t\t\t} catch { return \"\"; }\n\t\t\t})();\n\n\t\t_updateDecisionMemoryFromPayload(p);\n\t\t_pushRing(_decisionMemory.recentDecisions, {\n\t\t\tat: now(),\n\t\t\tok: !!ok,\n\t\t\tkind: k,\n\t\t\tmint: mint || null,\n\t\t\taction: d?.action ? String(d.action).slice(0, 20) : null,\n\t\t\tconfidence: (typeof d?.confidence === \"number\") ? d.confidence : null,\n\t\t\treason: d?.reason ? String(d.reason).slice(0, 160) : null,\n\t\t}, 10);\n\n\t\tif (k === \"buy\" && d && String(d.action || \"\").toLowerCase() === \"skip\") {\n\t\t\tconst code = _deriveSkipCodeFromPayload(p);\n\t\t\t_pushRing(_decisionMemory.recentBuySkips, { at: now(), mint: mint || null, code }, 12);\n\t\t}\n\t} catch {}\n\t}\n\n\tfunction _decisionMemorySnapshot() {\n\t\ttry {\n\t\t\tconst recent = Array.isArray(_decisionMemory.recentBuySkips) ? _decisionMemory.recentBuySkips.slice(0, 10) : [];\n\t\t\tconst counts = new Map();\n\t\t\tfor (const r of recent) {\n\t\t\t\tconst c = String(r?.code || \"unknown\");\n\t\t\t\tcounts.set(c, (counts.get(c) || 0) + 1);\n\t\t\t}\n\t\t\tconst skipReasons = Array.from(counts.entries())\n\t\t\t\t.sort((a, b) => (b[1] - a[1]))\n\t\t\t\t.slice(0, 4)\n\t\t\t\t.map(([code, count]) => ({ code, count }));\n\n\t\t\treturn {\n\t\t\t\tv: _decisionMemory.v,\n\t\t\t\tstartedAt: _decisionMemory.startedAt,\n\t\t\t\triskPosture: {\n\t\t\t\t\tagentRisk: String(_decisionMemory.riskPosture.agentRisk || \"safe\"),\n\t\t\t\t\tfullAiControl: !!_decisionMemory.riskPosture.fullAiControl,\n\t\t\t\t\tupdatedAt: _decisionMemory.riskPosture.updatedAt || 0,\n\t\t\t\t},\n\t\t\t\toutcomes: (Array.isArray(_decisionMemory.recentOutcomes) ? _decisionMemory.recentOutcomes.slice(0, 3) : []),\n\t\t\t\tskipBuysLately: {\n\t\t\t\t\twindow: recent.length,\n\t\t\t\t\treasons: skipReasons,\n\t\t\t\t},\n\t\t\t\trecentDecisions: (Array.isArray(_decisionMemory.recentDecisions) ? _decisionMemory.recentDecisions.slice(0, 6) : []),\n\t\t\t};\n\t\t} catch {\n\t\t\treturn { v: 1, startedAt: now() };\n\t\t}\n\t}\n\tfunction _ensureReasoningSessionKey() {\n\t\ttry {\n\t\t\tif (_reasoningSessionKey) return _reasoningSessionKey;\n\t\t\t// Short, process-local session id; persisted chaining is not required.\n\t\t\tconst rand = Math.random().toString(36).slice(2);\n\t\t\t_reasoningSessionKey = `fdv_auto_trader:${now()}:${rand}`;\n\t\t\treturn _reasoningSessionKey;\n\t\t} catch {\n\t\t\t_reasoningSessionKey = \"fdv_auto_trader\";\n\t\t\treturn _reasoningSessionKey;\n\t\t}\n\t}\n\n\tfunction _isReasoningModel(modelName) {\n\t\ttry {\n\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t// Reasoning-mode models for the OpenAI Responses API.\n\t\t\treturn /(?:^|\\/)gpt-5\\b/.test(s) || /(?:^|\\/)o\\d+\\b/.test(s);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst cfgN = normalizeLlmConfig(cfg);\n\t\t\tconst cacheKey = (() => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.stringify({\n\t\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\t\tbaseUrl: String(cfgN?.baseUrl || \"\"),\n\t\t\t\t\t\tmodel: String(cfgN?.model || \"\"),\n\t\t\t\t\t\ttimeoutMs: Number(cfgN?.timeoutMs || 0),\n\t\t\t\t\t\tmaxTokens: Number(cfgN?.maxTokens || 0),\n\t\t\t\t\t\thmacSecret: String(cfgN?.hmacSecret || \"\"),\n\t\t\t\t\t\tapiKey: String(cfgN?.apiKey || \"\"),\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\treturn \"\";\n\t\t\t\t}\n\t\t\t})();\n\t\t\tif (!_getClient._cached) {\n\t\t\t\t_getClient._cached = { key: \"\", client: null };\n\t\t\t}\n\t\t\tconst prev = _getClient._cached;\n\t\t\tif (prev && prev.client && prev.key && prev.key === cacheKey) return prev.client;\n\t\t\tconst client = createChatClientFromConfig(cfg);\n\t\t\t_getClient._cached = { key: cacheKey, client };\n\t\t\treturn client;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\treturn createChatClientFromConfig(cfg);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryFromPayload(payload);\n\t\t} catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) userMsg.decisionMemory = _decisionMemorySnapshot();\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tif (!_evolvePromptEnabled()) return null;\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\treturn _compactEvolveForPrompt(s);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary: \"\" });\n\t\ttry {\n\t\t\tif (evolveSummary) {\n\t\t\t\tif (!userMsg.payload || typeof userMsg.payload !== \"object\") userMsg.payload = {};\n\t\t\t\tuserMsg.payload.evolve = evolveSummary;\n\t\t\t}\n\t\t} catch {}\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst cfgModel = (() => {\n\t\t\t\ttry { return String(cfgN?.model || \"\").trim(); } catch { return \"\"; }\n\t\t\t})();\n\t\t\tconst wantsReasoning = _isReasoningModel(cfgModel);\n\t\t\tconst normModel = (() => { try { return String(cfgModel || \"\").trim().toLowerCase(); } catch { return \"\"; } })();\n\t\t\tconst reasoningReset = wantsReasoning && _reasoningSessionModel && _reasoningSessionModel !== normModel;\n\t\t\tif (wantsReasoning) _reasoningSessionModel = normModel;\n\t\t\telse _reasoningSessionModel = \"\";\n\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tconst allowed = (kind === \"config_scan\" && Array.isArray(payload?.allowedKeys))\n\t\t\t\t\t? payload.allowedKeys\n\t\t\t\t\t: [];\n\t\t\t\treq.responseSchema = _buildDecisionSchema(kind, allowed);\n\t\t\t} catch {}\n\t\t\tif (wantsReasoning) {\n\t\t\t\treq.reasoningSessionKey = _ensureReasoningSessionKey();\n\t\t\t\treq.reasoningReset = !!reasoningReset;\n\t\t\t\t// Default ON: request encrypted reasoning content when supported.\n\t\t\t\treq.reasoningIncludeEncrypted = true;\n\t\t\t}\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed, payload?.allowedKeys)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (_decisionMemoryEnabled()) _updateDecisionMemoryAfterDecision({ kind, payload, decision: validated, ok: res.ok });\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "90fbc7d31177f9b1029d2dcff4575c365c365c06": {"t": 1785766674.0675943, "n": 60920, "h": "9dc1558e61fe0bd0", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tconst _stripTrailingCommas = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn String(t || \"\").replace(/,\\s*([}\\]])/g, \"$1\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _balanceClosers = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet inStr = false;\n\t\t\t\t\tlet esc = false;\n\t\t\t\t\tlet openObj = 0;\n\t\t\t\t\tlet openArr = 0;\n\t\t\t\t\tfor (let i = 0; i < t.length; i++) {\n\t\t\t\t\t\tconst ch = t[i];\n\t\t\t\t\t\tif (inStr) {\n\t\t\t\t\t\t\tif (esc) { esc = false; continue; }\n\t\t\t\t\t\t\tif (ch === \"\\\\\") { esc = true; continue; }\n\t\t\t\t\t\t\tif (ch === '\"') { inStr = false; continue; }\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ch === '\"') { inStr = true; continue; }\n\t\t\t\t\t\tif (ch === \"{\") openObj++;\n\t\t\t\t\t\telse if (ch === \"}\") openObj = Math.max(0, openObj - 1);\n\t\t\t\t\t\telse if (ch === \"[\") openArr++;\n\t\t\t\t\t\telse if (ch === \"]\") openArr = Math.max(0, openArr - 1);\n\t\t\t\t\t}\n\t\t\t\t\tlet out = t;\n\t\t\t\t\t// Close arrays first, then objects.\n\t\t\t\t\tif (openArr) out += \"]\".repeat(openArr);\n\t\t\t\t\tif (openObj) out += \"}\".repeat(openObj);\n\t\t\t\t\treturn out;\n\t\t\t\t} catch {\n\t\t\t\t\treturn String(t || \"\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst _tryRepairParse = (t) => {\n\t\t\t\ttry {\n\t\t\t\t\tlet u = String(t || \"\");\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\tu = _balanceClosers(u);\n\t\t\t\t\tu = _stripTrailingCommas(u);\n\t\t\t\t\treturn JSON.parse(u);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\tconst slice = t.slice(firstObj, lastObj + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\tconst slice = t.slice(firstArr, lastArr + 1);\n\t\t\t\ttry { return JSON.parse(slice); } catch {}\n\t\t\t\tconst repaired = _tryRepairParse(slice);\n\t\t\t\tif (repaired) return repaired;\n\t\t\t}\n\n\t\t\t// Last chance: attempt repair on any leading '{'/'[' content, even if truncated.\n\t\t\ttry {\n\t\t\t\tif (firstObj >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstObj));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t\tif (firstArr >= 0) {\n\t\t\t\t\tconst repaired = _tryRepairParse(t.slice(firstArr));\n\t\t\t\t\tif (repaired) return repaired;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tagentRisk: an.agentRisk ? String(an.agentRisk).slice(0, 16) : undefined,\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tout.payload = keep;\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\t// Minimal tick snapshot.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tagentRisk: an.agentRisk ? String(an.agentRisk).slice(0, 16) : undefined,\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst last = Array.isArray(past.candles) && past.candles.length ? past.candles[past.candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst { logKey, cacheKey, cacheEnabled } = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").trim();\n\t\t\t\tconst rawCk = String(payload?.cacheKey || options?.cacheKey || \"\").trim();\n\t\t\t\t// Cache is opt-in via explicit cacheKey. Never cache sell decisions; they must reflect the latest market state.\n\t\t\t\tconst enabled = !!rawCk && String(kind || \"\") !== \"sell\";\n\t\t\t\treturn {\n\t\t\t\t\tlogKey: `${String(kind || \"\")}:${mint || rawCk || \"global\"}`,\n\t\t\t\t\tcacheKey: rawCk ? `${String(kind || \"\")}:${rawCk}` : \"\",\n\t\t\t\t\tcacheEnabled: enabled,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { logKey: `${String(kind || \"\")}:unknown`, cacheKey: \"\", cacheEnabled: false };\n\t\t\t}\n\t\t})();\n\t\tconst prev = (cacheEnabled && cacheKey) ? cache.get(cacheKey) : null;\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? _redactDeep((\"stateOverride\" in options) ? options.stateOverride : {},\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t)\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\t// Keep system prompts short for tiny local models; the kind is also present in USER payload.\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: cacheEnabled ? String(cacheKey || \"\") : undefined,\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tif (cacheEnabled && cacheKey) cache.set(cacheKey, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.08,\n\t\t\t\tmaxTokens: 700,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\tlet res = first;\n\n\t\t\t// Retry if the model produced truncated/invalid JSON.\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 340) + \"\\nRetry: return ONLY valid JSON. Keep config <= 12 keys, only changed keys. Avoid trailing commas. Omit reason if possible.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.03,\n\t\t\t\t\tmaxTokens: 900,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Last resort: ultra-minimal schema (reduces completion size and truncation risk).\n\t\t\tif (res && res.ok === false && String(res.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload3 = {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t\tnote: \"Return ONLY JSON: {action:'apply'|'skip', confidence:0..1, config?:{...}}. No reason field. config must have <= 10 keys from allowedKeys. No trailing commas.\",\n\t\t\t\t};\n\t\t\t\tres = await _run(\"config_scan\", payload3, {\n\t\t\t\t\tcacheKey: \"startup_retry_min\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.02,\n\t\t\t\t\tmaxTokens: 650,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "0a500b618f59272584cd0fad8c5e4938b3fc9a8f": {"t": 1785766677.243459, "n": 57415, "h": "4b4e32c3c240ac59", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tagentRisk: an.agentRisk ? String(an.agentRisk).slice(0, 16) : undefined,\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tout.payload = keep;\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _compactUserMsgForLlm(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tif (p.market && typeof p.market === \"object\") payload.market = p.market;\n\t\t\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 80);\n\t\t\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 500);\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t} catch {\n\t\t\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\t\t\treturn out;\n\t\t\t}\n\t\t}\n\n\t\t// For sell, keep compact position/context (LLM needs exit context).\n\t\tif (kind === \"sell\") {\n\t\t\ttry {\n\t\t\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\t\t\tconst pos = p.pos;\n\t\t\t\t\tconst keepPos = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"sizeUi\",\n\t\t\t\t\t\t\"decimals\",\n\t\t\t\t\t\t\"costSol\",\n\t\t\t\t\t\t\"hwmSol\",\n\t\t\t\t\t\t\"acquiredAt\",\n\t\t\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\t\t\"lastSellAt\",\n\t\t\t\t\t\t\"allowRebuy\",\n\t\t\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\t\t\"warmingHold\",\n\t\t\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\t\t\"tpPct\",\n\t\t\t\t\t\t\"slPct\",\n\t\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\t\t\"entryPreMin\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t\t\t}\n\t\t\t\t\t// Minimal tick snapshot.\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst t = pos.tickNow;\n\t\t\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t\t\t}\n\t\t\t} catch {}\n\n\t\t\ttry {\n\t\t\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\t\t\tconst c = p.ctx;\n\t\t\t\t\tconst keepCtx = {};\n\t\t\t\t\tfor (const k of [\n\t\t\t\t\t\t\"agentRisk\",\n\t\t\t\t\t\t\"nowTs\",\n\t\t\t\t\t\t\"curSol\",\n\t\t\t\t\t\t\"curSolNet\",\n\t\t\t\t\t\t\"pnlPct\",\n\t\t\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\t\t\"inMinHold\",\n\t\t\t\t\t\t\"inSellGuard\",\n\t\t\t\t\t\t\"hasPending\",\n\t\t\t\t\t\t\"isFastExit\",\n\t\t\t\t\t\t\"rugSev\",\n\t\t\t\t\t\t\"forceRug\",\n\t\t\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\t\t\"forceMomentum\",\n\t\t\t\t\t\t\"forceExpire\",\n\t\t\t\t\t]) {\n\t\t\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fg = c.finalGate;\n\t\t\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfg = c.cfg;\n\t\t\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst an = c.agentSignals;\n\t\t\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\t\t\tagentRisk: an.agentRisk ? String(an.agentRisk).slice(0, 16) : undefined,\n\t\t\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t}\n\n\t\t// Compact signals: keep decision-critical summaries.\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.honeypotOnchain && typeof g.honeypotOnchain === \"object\") {\n\t\t\t\t// Avoid biasing the model by calling this a \"honeypot\" gate. Treat as neutral on-chain labels.\n\t\t\t\tconst hp = g.honeypotOnchain;\n\t\t\t\tconst labels = [];\n\t\t\t\ttry {\n\t\t\t\t\tconst rs = Array.isArray(hp.reasons) ? hp.reasons : [];\n\t\t\t\t\tfor (const r of rs) {\n\t\t\t\t\t\tconst s = String(r || \"\").trim();\n\t\t\t\t\t\tif (!s) continue;\n\t\t\t\t\t\tif (s === \"token-2022\") labels.push(\"token-2022\");\n\t\t\t\t\t\telse if (/^freezeAuthority=/i.test(s)) labels.push(\"freeze-authority\");\n\t\t\t\t\t\telse if (/^mintAuthority=/i.test(s)) labels.push(\"mint-authority\");\n\t\t\t\t\t\telse labels.push(s.slice(0, 40));\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tgates.onchainLabels = {\n\t\t\t\t\tmode: hp.mode ?? null,\n\t\t\t\t\tok: (\"ok\" in hp) ? !!hp.ok : undefined,\n\t\t\t\t\tprogram: hp.program ?? null,\n\t\t\t\t\thasFreezeAuthority: (\"hasFreezeAuthority\" in hp) ? !!hp.hasFreezeAuthority : undefined,\n\t\t\t\t\thasMintAuthority: (\"hasMintAuthority\" in hp) ? !!hp.hasMintAuthority : undefined,\n\t\t\t\t\tlabels: labels.length ? labels.slice(0, 4) : undefined,\n\t\t\t\t\tnudge: labels.length ? \"soft_sell_risk\" : undefined,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, enforceForRisk: (\"enforceForRisk\" in g.entryCost) ? !!g.entryCost.enforceForRisk : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets are mostly redundant with other fields; omit to keep prompts small.\n\n\t\ttry {\n\t\t\tconst kb = (s.kpiBundle && typeof s.kpiBundle === \"object\") ? s.kpiBundle : null;\n\t\t\tconst snap = kb && kb.snapshot && typeof kb.snapshot === \"object\" ? kb.snapshot : null;\n\t\t\tconst snap2 = (!snap && s.kpi && typeof s.kpi === \"object\") ? s.kpi : null;\n\t\t\tconst k = snap || snap2;\n\t\t\tif (k) {\n\t\t\t\tsignals.kpi = {\n\t\t\t\t\tsymbol: k.symbol ?? null,\n\t\t\t\t\tpriceUsd: k.priceUsd ?? null,\n\t\t\t\t\tchg24: k.chg24 ?? null,\n\t\t\t\t\tvol24: k.vol24 ?? null,\n\t\t\t\t\tliqUsd: k.liqUsd ?? null,\n\t\t\t\t\ttx24: k.tx24 ?? null,\n\t\t\t\t\tmcap: k.mcap ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Edge fields are noisy/redundant vs sim + gate warnings; omit for compact prompts.\n\n\t\ttry {\n\t\t\tconst sim = (s.entrySim && typeof s.entrySim === \"object\") ? s.entrySim : null;\n\t\t\tif (sim) {\n\t\t\t\tsignals.entrySim = {\n\t\t\t\t\thorizonSecs: sim.horizonSecs ?? null,\n\t\t\t\t\trequiredGrossPct: sim.requiredGrossPct ?? null,\n\t\t\t\t\tpHit: sim.pHit ?? null,\n\t\t\t\t\tpTerminal: sim.pTerminal ?? null,\n\t\t\t\t\tmuPct: sim.muPct ?? null,\n\t\t\t\t\tsigmaPct: sim.sigmaPct ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst ln = (s.leaderNow && typeof s.leaderNow === \"object\") ? s.leaderNow : null;\n\t\t\tif (ln) signals.leaderNow = { pumpScore: ln.pumpScore ?? null, v1h: ln.v1h ?? null, chg5m: ln.chg5m ?? null, chg1h: ln.chg1h ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst tn = (s.tickNow && typeof s.tickNow === \"object\") ? s.tickNow : null;\n\t\t\tif (tn) signals.tickNow = { ts: tn.ts ?? null, priceUsd: tn.priceUsd ?? null, liqUsd: tn.liqUsd ?? null, change5m: tn.change5m ?? null, change1h: tn.change1h ?? null, v5mUsd: tn.v5mUsd ?? null, v1hUsd: tn.v1hUsd ?? null };\n\t\t} catch {}\n\n\t\ttry {\n\t\t\tconst past = (s.past && typeof s.past === \"object\") ? s.past : null;\n\t\t\tif (past) {\n\t\t\t\tconst last = Array.isArray(past.candles) && past.candles.length ? past.candles[past.candles.length - 1] : (Array.isArray(past.lastCandle) ? past.lastCandle : null);\n\t\t\t\tsignals.past = {\n\t\t\t\t\tsource: past.source ?? null,\n\t\t\t\t\ttimeframe: past.timeframe ?? null,\n\t\t\t\t\tstats: past.stats ?? null,\n\t\t\t\t\tquality: past.quality ?? null,\n\t\t\t\t\tlastCandle: last ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Omit wallet/budget (bot already enforces it) and other noisy/redundant scalars.\n\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 6)) : [];\n\t\t\tconst last = recent.reduce((best, r) => {\n\t\t\t\tconst ts = _safeNum(r?.ts, 0);\n\t\t\t\tif (!best) return r;\n\t\t\t\treturn ts > _safeNum(best?.ts, 0) ? r : best;\n\t\t\t}, null);\n\t\t\tif (last && (\"pnlSol\" in last || \"ts\" in last || \"kind\" in last)) {\n\t\t\t\tconst pnl = (typeof last?.pnlSol === \"number\") ? last.pnlSol : null;\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\tlastTrade: {\n\t\t\t\t\t\tts: last?.ts ?? null,\n\t\t\t\t\t\tkind: last?.kind ? String(last.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tdecisionAction: last?.decisionAction ? String(last.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\tpnlSol: pnl,\n\t\t\t\t\t\tupDown: (typeof pnl === \"number\") ? (pnl > 0 ? \"up\" : pnl < 0 ? \"down\" : \"flat\") : undefined,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) payload.signals = signals;\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _shouldLogPrompts(cfgN = null) {\n\t\ttry {\n\t\t\tconst cfgRaw = _getConfig() || {};\n\t\t\tif (cfgRaw && cfgRaw.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (cfgN && cfgN.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _ensurePromptLogGlobals() {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tif (!g.__fdvEnableAgentPromptLogs) {\n\t\t\t\tg.__fdvEnableAgentPromptLogs = (on = true) => {\n\t\t\t\t\tconst enable = !!on;\n\t\t\t\t\ttry { g.__fdvLogAgentPrompts = enable; } catch {}\n\t\t\t\t\ttry { if (g.localStorage) g.localStorage.setItem(\"fdv_agent_log_prompts\", enable ? \"1\" : \"0\"); } catch {}\n\t\t\t\t\treturn enable;\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\t}\n\n\tfunction _pushPromptLog(entry) {\n\t\ttry {\n\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\tif (!g) return;\n\t\t\tif (!g.__fdvAgentPromptLog || !Array.isArray(g.__fdvAgentPromptLog)) g.__fdvAgentPromptLog = [];\n\t\t\tg.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (g.__fdvAgentPromptLog.length > 25) g.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\ttry { _ensurePromptLogGlobals(); } catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\ttry { _ensurePromptLogGlobals(); } catch {}\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\t\tconst promptLogOn = _shouldLogPrompts(cfgN);\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t} else {\n\t\t\t\t// Default ON: full state + full signals is huge and expensive.\n\t\t\t\tconst compactOn = (() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (globalThis && globalThis.__fdvCompactAgentPrompts === true) return true;\n\t\t\t\t\t} catch {}\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn _readBoolLs(\"fdv_agent_compact_prompts\", true);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t\tif (compactOn && (kind === \"buy\" || kind === \"sell\" || kind === \"config_scan\")) {\n\t\t\t\t\tuserMsg = _compactUserMsgForLlm(userMsg);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\t// Keep system prompts short for tiny local models; the kind is also present in USER payload.\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\ttry {\n\t\t\tif (promptLogOn) {\n\t\t\t\tconst model = String(cfgN?.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mintFull = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst entry = {\n\t\t\t\t\tat: now(),\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tagent: \"gary\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tmint: mintFull || undefined,\n\t\t\t\t\tmodel,\n\t\t\t\t\tprovider: String(cfgN?.provider || \"\"),\n\t\t\t\t\tcacheKey: String(key || \"\"),\n\t\t\t\t\trequest: {\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\tuser: _redactDeep(userMsg, { maxDepth: 6, maxKeys: 220 }),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\t_pushPromptLog(entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(\"[AGENT GARY] prompt\", entry);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\ttry {\n\t\t\t\t\tconst sysLen = String(systemPromptFinal || \"\").length;\n\t\t\t\t\tconst userLen = (() => { try { return JSON.stringify(userMsg).length; } catch { return -1; } })();\n\t\t\t\t\t_log(`promptLog ON (systemChars=${sysLen}, userChars=${userLen})`, \"info\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), response: _redactDeep(d) };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.log) globalThis.console.log(\"[AGENT GARY] response\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t\ttry {\n\t\t\t\t\tif (promptLogOn) {\n\t\t\t\t\t\tconst entry = { at: now(), agent: \"gary\", kind: String(kind || \"\"), error: String(res.err || \"\"), raw: String(snippet || \"\") };\n\t\t\t\t\t\t_pushPromptLog(entry);\n\t\t\t\t\t\ttry { if (globalThis?.console?.warn) globalThis.console.warn(\"[AGENT GARY] response (invalid)\", entry); } catch {}\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 260,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 320,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _shouldLogPrompts(cfg, { logRequest = false } = {}) {\n\t\ttry {\n\t\t\tif (logRequest) return true;\n\t\t\tif (cfg && cfg.logPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (globalThis && globalThis.__fdvLogAgentPrompts === true) return true;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst ls = globalThis && globalThis.localStorage;\n\t\t\tif (!ls || typeof ls.getItem !== \"function\") return false;\n\t\t\tconst v = String(ls.getItem(\"fdv_agent_log_prompts\") || \"\").trim().toLowerCase();\n\t\t\treturn (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\");\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _dbgStore(entry) {\n\t\ttry {\n\t\t\tif (!globalThis) return;\n\t\t\tif (!globalThis.__fdvAgentPromptLog || !Array.isArray(globalThis.__fdvAgentPromptLog)) {\n\t\t\t\tglobalThis.__fdvAgentPromptLog = [];\n\t\t\t}\n\t\t\tglobalThis.__fdvAgentPromptLog.unshift(entry);\n\t\t\tif (globalThis.__fdvAgentPromptLog.length > 25) globalThis.__fdvAgentPromptLog.length = 25;\n\t\t} catch {}\n\t}\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tconst model = String(cfg.model || \"\");\n\t\t\tif (logRequest) _log(`call model=${model}`, \"info\");\n\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeCfg = { provider: String(cfg.provider || \"\"), model: String(cfg.model || \"\") };\n\t\t\t\tconst safeReq = _redactDeep({\n\t\t\t\t\tcacheKey: String(cacheKey || \"\"),\n\t\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\t\tuser: String(user || \"\"),\n\t\t\t\t});\n\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), cfg: safeCfg, req: safeReq });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] prompt`, { cfg: safeCfg, req: safeReq });\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\n\t\ttry {\n\t\t\tconst logPrompts = _shouldLogPrompts(cfg, { logRequest });\n\t\t\tif (logPrompts) {\n\t\t\t\tconst safeParsed = _redactDeep(parsed);\n\t\t\t\t_dbgStore({ at: now(), prefix: String(prefix || \"AGENT\"), res: safeParsed });\n\t\t\t\ttry {\n\t\t\t\t\tif (globalThis && globalThis.console && typeof globalThis.console.log === \"function\") {\n\t\t\t\t\t\tglobalThis.console.log(`[${String(prefix || \"AGENT\")}] response`, safeParsed);\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "b677c36c814d2f5d57a3d6e329755676341fcd49": {"t": 1785766678.8121946, "n": 40060, "h": "d47182a91ce537df", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Keep minimal state (large state tends to drown small local models).\n\t\ttry {\n\t\t\tconst st = userMsg.state;\n\t\t\tif (st && typeof st === \"object\") {\n\t\t\t\tconst keep = {};\n\t\t\t\tfor (const k of [\n\t\t\t\t\t\"riskLevel\",\n\t\t\t\t\t\"buyPct\",\n\t\t\t\t\t\"minBuySol\",\n\t\t\t\t\t\"maxBuySol\",\n\t\t\t\t\t\"slippageBps\",\n\t\t\t\t\t\"minSecsBetween\",\n\t\t\t\t\t\"coolDownSecsAfterBuy\",\n\t\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\t\"minNetEdgePct\",\n\t\t\t\t\t\"edgeSafetyBufferPct\",\n\t\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\t\"stopLossPct\",\n\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\"maxEntryCostPct\",\n\t\t\t\t\t\"entrySimMode\",\n\t\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\t\"entrySimMinTerminalProb\",\n\t\t\t\t]) {\n\t\t\t\t\tif (k in st) keep[k] = st[k];\n\t\t\t\t}\n\t\t\t\tif (Object.keys(keep).length) out.state = keep;\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\t// pos/ctx are extremely large; keep only decision-critical fields.\n\t\tif (p.pos && typeof p.pos === \"object\") {\n\t\t\tconst pos = p.pos;\n\t\t\tconst keepPos = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"sizeUi\",\n\t\t\t\t\"decimals\",\n\t\t\t\t\"costSol\",\n\t\t\t\t\"hwmSol\",\n\t\t\t\t\"acquiredAt\",\n\t\t\t\t\"lastBuyAt\",\n\t\t\t\t\"lastSellAt\",\n\t\t\t\t\"allowRebuy\",\n\t\t\t\t\"awaitingSizeSync\",\n\t\t\t\t\"warmingHold\",\n\t\t\t\t\"sellGuardUntil\",\n\t\t\t\t\"warmingMinProfitPct\",\n\t\t\t\t\"tpPct\",\n\t\t\t\t\"slPct\",\n\t\t\t\t\"trailPct\",\n\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\"entryEdgeExclPct\",\n\t\t\t\t\"entryEdgeCostPct\",\n\t\t\t\t\"entryTpBumpPct\",\n\t\t\t\t\"entryPreMin\",\n\t\t\t]) {\n\t\t\t\tif (k in pos) keepPos[k] = pos[k];\n\t\t\t}\n\t\t\t// Minimal tick snapshot.\n\t\t\ttry {\n\t\t\t\tconst t = pos.tickNow;\n\t\t\t\tif (t && typeof t === \"object\") {\n\t\t\t\t\tkeepPos.tickNow = {\n\t\t\t\t\t\tts: t.ts ?? null,\n\t\t\t\t\t\tpriceUsd: t.priceUsd ?? null,\n\t\t\t\t\t\tliqUsd: t.liqUsd ?? null,\n\t\t\t\t\t\tchange5m: t.change5m ?? null,\n\t\t\t\t\t\tchange1h: t.change1h ?? null,\n\t\t\t\t\t\tv1hUsd: t.v1hUsd ?? null,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepPos).length) payload.pos = keepPos;\n\t\t}\n\t\tif (p.ctx && typeof p.ctx === \"object\") {\n\t\t\tconst c = p.ctx;\n\t\t\tconst keepCtx = {};\n\t\t\tfor (const k of [\n\t\t\t\t\"agentRisk\",\n\t\t\t\t\"nowTs\",\n\t\t\t\t\"curSol\",\n\t\t\t\t\"curSolNet\",\n\t\t\t\t\"pnlPct\",\n\t\t\t\t\"pnlNetPct\",\n\t\t\t\t\"minNotionalSol\",\n\t\t\t\t\"inMinHold\",\n\t\t\t\t\"inSellGuard\",\n\t\t\t\t\"hasPending\",\n\t\t\t\t\"isFastExit\",\n\t\t\t\t\"rugSev\",\n\t\t\t\t\"forceRug\",\n\t\t\t\t\"forcePumpDrop\",\n\t\t\t\t\"forceObserverDrop\",\n\t\t\t\t\"forceMomentum\",\n\t\t\t\t\"forceExpire\",\n\t\t\t]) {\n\t\t\t\tif (k in c) keepCtx[k] = c[k];\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst fg = c.finalGate;\n\t\t\t\tif (fg && typeof fg === \"object\") keepCtx.finalGate = { intensity: fg.intensity ?? null, tier: fg.tier ?? null, chgSlope: fg.chgSlope ?? null, scSlope: fg.scSlope ?? null };\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst cfg = c.cfg;\n\t\t\t\tif (cfg && typeof cfg === \"object\") keepCtx.cfg = {\n\t\t\t\t\tminHoldSecs: cfg.minHoldSecs ?? null,\n\t\t\t\t\tmaxHoldSecs: cfg.maxHoldSecs ?? null,\n\t\t\t\t\ttakeProfitPct: cfg.takeProfitPct ?? null,\n\t\t\t\t\tstopLossPct: cfg.stopLossPct ?? null,\n\t\t\t\t\ttrailPct: cfg.trailPct ?? null,\n\t\t\t\t\tminProfitToTrailPct: cfg.minProfitToTrailPct ?? null,\n\t\t\t\t\tminNetEdgePct: cfg.minNetEdgePct ?? null,\n\t\t\t\t\tedgeSafetyBufferPct: cfg.edgeSafetyBufferPct ?? null,\n\t\t\t\t};\n\t\t\t} catch {}\n\t\t\ttry {\n\t\t\t\tconst an = c.agentSignals;\n\t\t\t\tif (an && typeof an === \"object\") {\n\t\t\t\t\tconst outc = (an.outcomes && typeof an.outcomes === \"object\") ? an.outcomes : null;\n\t\t\t\t\tconst recent = outc && Array.isArray(outc.recent) ? outc.recent.slice(Math.max(0, outc.recent.length - 2)) : [];\n\t\t\t\t\tkeepCtx.agentSignals = {\n\t\t\t\t\t\tagentRisk: an.agentRisk ? String(an.agentRisk).slice(0, 16) : undefined,\n\t\t\t\t\t\tfullAiControl: (typeof an.fullAiControl === \"boolean\") ? an.fullAiControl : undefined,\n\t\t\t\t\t\toutcomes: recent.length ? {\n\t\t\t\t\t\t\tsessionPnlSol: outc ? (outc.sessionPnlSol ?? null) : null,\n\t\t\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t} : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tif (Object.keys(keepCtx).length) payload.ctx = keepCtx;\n\t\t}\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 3)) : [];\n\t\t\tif (recent.length) {\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\tts: r?.ts ?? null,\n\t\t\t\t\t\tmint: r?.mint ? String(r.mint).slice(0, 12) : undefined,\n\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t})),\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tout.payload = keep;\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\t// Keep system prompts short for tiny local models; the kind is also present in USER payload.\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\t// For Gary, prefer the server-extracted parsed JSON when available.\n\t\t\t\ttry {\n\t\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\t\t\tconst p = meta && Object.prototype.hasOwnProperty.call(meta, \"parsed\") ? meta.parsed : null;\n\t\t\t\t\t\tif (p && (typeof p === \"object\" || Array.isArray(p))) {\n\t\t\t\t\t\t\ttext = JSON.stringify(p);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 260,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 320,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "fd6f5d60ae60acbf4c475be5306a7c18cc1b4046": {"t": 1785766680.4677749, "n": 36554, "h": "44dc7db932511d29", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _coerceWrongKindDecision(kind, parsed) {\n\ttry {\n\t\tconst k = String(kind || \"\").trim().toLowerCase();\n\t\tconst obj = (parsed && typeof parsed === \"object\") ? parsed : null;\n\t\tif (!obj) return parsed;\n\t\tconst action = String(obj.action || \"\").trim().toLowerCase();\n\t\tif (!action) return parsed;\n\t\t// Safety: if the model returns a sell-ish action while we asked for a buy,\n\t\t// treat it as a skip rather than hard-failing JSON validation.\n\t\tif (k === \"buy\" && (action === \"sell\" || action === \"sell_all\" || action === \"sell_partial\" || action === \"hold\" || action === \"long_hold\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"buy\",\n\t\t\t\taction: \"skip\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_sell_action_for_buy_kind\",\n\t\t\t};\n\t\t}\n\t\t// Safety: if the model returns a buy-ish action while we asked for a sell,\n\t\t// treat it as hold.\n\t\tif (k === \"sell\" && (action === \"buy\" || action === \"skip\")) {\n\t\t\treturn {\n\t\t\t\tkind: \"sell\",\n\t\t\t\taction: \"hold\",\n\t\t\t\tconfidence: 0,\n\t\t\t\treason: \"model_returned_buy_action_for_sell_kind\",\n\t\t\t};\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn parsed;\n\t}\n}\n\nfunction _compactUserMsgForGary(userMsg) {\n\ttry {\n\t\tif (!userMsg || typeof userMsg !== \"object\") return userMsg;\n\t\tconst kind = String(userMsg.kind || \"\").trim().toLowerCase();\n\t\tconst src = String(userMsg.source || \"\").slice(0, 80);\n\t\tconst out = { source: src || \"fdv_auto_trader\", kind };\n\n\t\t// Keep minimal state (large state tends to drown small local models).\n\t\ttry {\n\t\t\tconst st = userMsg.state;\n\t\t\tif (st && typeof st === \"object\") {\n\t\t\t\tconst keep = {};\n\t\t\t\tfor (const k of [\n\t\t\t\t\t\"riskLevel\",\n\t\t\t\t\t\"buyPct\",\n\t\t\t\t\t\"minBuySol\",\n\t\t\t\t\t\"maxBuySol\",\n\t\t\t\t\t\"slippageBps\",\n\t\t\t\t\t\"minSecsBetween\",\n\t\t\t\t\t\"coolDownSecsAfterBuy\",\n\t\t\t\t\t\"minHoldSecs\",\n\t\t\t\t\t\"maxHoldSecs\",\n\t\t\t\t\t\"minNetEdgePct\",\n\t\t\t\t\t\"edgeSafetyBufferPct\",\n\t\t\t\t\t\"takeProfitPct\",\n\t\t\t\t\t\"stopLossPct\",\n\t\t\t\t\t\"trailPct\",\n\t\t\t\t\t\"minProfitToTrailPct\",\n\t\t\t\t\t\"maxEntryCostPct\",\n\t\t\t\t\t\"entrySimMode\",\n\t\t\t\t\t\"entrySimHorizonSecs\",\n\t\t\t\t\t\"entrySimMinWinProb\",\n\t\t\t\t\t\"entrySimMinTerminalProb\",\n\t\t\t\t]) {\n\t\t\t\t\tif (k in st) keep[k] = st[k];\n\t\t\t\t}\n\t\t\t\tif (Object.keys(keep).length) out.state = keep;\n\t\t\t}\n\t\t} catch {}\n\n\t\t// Compact payload/signals aggressively.\n\t\tconst p = (userMsg.payload && typeof userMsg.payload === \"object\") ? userMsg.payload : {};\n\t\tconst payload = {};\n\t\tif (typeof p.mint === \"string\") payload.mint = p.mint;\n\t\tif (p.proposed && typeof p.proposed === \"object\") payload.proposed = p.proposed;\n\t\tif (p.pos && typeof p.pos === \"object\") payload.pos = p.pos;\n\t\tif (p.ctx && typeof p.ctx === \"object\") payload.ctx = p.ctx;\n\t\tif (p.allowedKeys && Array.isArray(p.allowedKeys)) payload.allowedKeys = p.allowedKeys.slice(0, 60);\n\t\tif (p.note) payload.note = String(p.note || \"\").slice(0, 260);\n\t\tif (Object.keys(payload).length) out.payload = payload;\n\n\t\tconst s = (p.signals && typeof p.signals === \"object\") ? p.signals : {};\n\t\tconst signals = {};\n\t\tif (s.agentRisk) signals.agentRisk = String(s.agentRisk || \"\").slice(0, 16);\n\t\tif (typeof s.fullAiControl === \"boolean\") signals.fullAiControl = s.fullAiControl;\n\n\t\t// Gates: keep only the numeric facts and ok flags.\n\t\ttry {\n\t\t\tconst g = (s.gates && typeof s.gates === \"object\") ? s.gates : {};\n\t\t\tconst gates = {};\n\t\t\tif (g.finalGateReady != null) gates.finalGateReady = !!g.finalGateReady;\n\t\t\tif (g.cooldown && typeof g.cooldown === \"object\") gates.cooldown = { ok: !!g.cooldown.ok, recentAgeMs: g.cooldown.recentAgeMs ?? null, minRebuyMs: g.cooldown.minRebuyMs ?? null };\n\t\t\tif (g.buyWarmup && typeof g.buyWarmup === \"object\") gates.buyWarmup = { ready: !!g.buyWarmup.ready, ageMs: g.buyWarmup.ageMs ?? null, minMs: g.buyWarmup.minMs ?? null, seen: g.buyWarmup.seen ?? null, minSeen: g.buyWarmup.minSeen ?? null, seriesN: g.buyWarmup.seriesN ?? null, minSeries: g.buyWarmup.minSeries ?? null };\n\t\t\tif (g.manualEdge && typeof g.manualEdge === \"object\") gates.manualEdge = { ok: !!g.manualEdge.ok, edgeExclPct: g.manualEdge.edgeExclPct ?? null, minNetEdgePct: g.manualEdge.minNetEdgePct ?? null };\n\t\t\tif (g.entryCost && typeof g.entryCost === \"object\") gates.entryCost = { on: !!g.entryCost.on, ok: (\"ok\" in g.entryCost) ? !!g.entryCost.ok : undefined, risk: g.entryCost.risk ?? null, edgeCostPct: g.entryCost.edgeCostPct ?? null, maxEntryCostPct: g.entryCost.maxEntryCostPct ?? null };\n\t\t\tif (g.sim && typeof g.sim === \"object\") gates.sim = { ready: !!g.sim.ready, mode: g.sim.mode ?? null, horizonSecs: g.sim.horizonSecs ?? null, pHit: g.sim.pHit ?? null, pTerminal: g.sim.pTerminal ?? null, minWinProb: g.sim.minWinProb ?? null, minTerminalProb: g.sim.minTerminalProb ?? null };\n\t\t\tif (Object.keys(gates).length) signals.gates = gates;\n\t\t} catch {}\n\n\t\t// Targets and outcomes: keep tiny summaries only.\n\t\ttry {\n\t\t\tconst t = (s.targets && typeof s.targets === \"object\") ? s.targets : {};\n\t\t\tconst targets = {};\n\t\t\tfor (const k of [\"sessionPnlSol\", \"minNetEdgePct\", \"edgeExclPct\", \"edgeSafetyBufferPct\", \"baseGoalPct\", \"requiredGrossTpPct\", \"takeProfitPct\"]) {\n\t\t\t\tif (k in t) targets[k] = t[k];\n\t\t\t}\n\t\t\tif (Object.keys(targets).length) signals.targets = targets;\n\t\t} catch {}\n\t\ttry {\n\t\t\tconst o = (s.outcomes && typeof s.outcomes === \"object\") ? s.outcomes : {};\n\t\t\tconst recent = Array.isArray(o.recent) ? o.recent.slice(Math.max(0, o.recent.length - 3)) : [];\n\t\t\tif (recent.length) {\n\t\t\t\tsignals.outcomes = {\n\t\t\t\t\tsessionPnlSol: o.sessionPnlSol ?? null,\n\t\t\t\t\trecent: recent.map((r) => ({\n\t\t\t\t\t\tts: r?.ts ?? null,\n\t\t\t\t\t\tmint: r?.mint ? String(r.mint).slice(0, 12) : undefined,\n\t\t\t\t\t\tkind: r?.kind ? String(r.kind).slice(0, 24) : undefined,\n\t\t\t\t\t\tpnlSol: r?.pnlSol ?? null,\n\t\t\t\t\t\tdecisionAction: r?.decisionAction ? String(r.decisionAction).slice(0, 20) : undefined,\n\t\t\t\t\t})),\n\t\t\t\t};\n\t\t\t}\n\t\t} catch {}\n\n\t\tif (Object.keys(signals).length) {\n\t\t\tif (!out.payload) out.payload = {};\n\t\t\tout.payload.signals = signals;\n\t\t}\n\n\t\t// For config_scan, keep extra minimal: only market/allowedKeys/note.\n\t\tif (kind === \"config_scan\") {\n\t\t\ttry {\n\t\t\t\tconst pp = out.payload && typeof out.payload === \"object\" ? out.payload : {};\n\t\t\t\tconst keep = {};\n\t\t\t\tif (pp.market && typeof pp.market === \"object\") keep.market = pp.market;\n\t\t\t\tif (pp.allowedKeys && Array.isArray(pp.allowedKeys)) keep.allowedKeys = pp.allowedKeys;\n\t\t\t\tif (pp.note) keep.note = pp.note;\n\t\t\t\tout.payload = keep;\n\t\t\t} catch {}\n\t\t}\n\n\t\treturn out;\n\t} catch {\n\t\treturn userMsg;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst cfgN = (() => {\n\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t})();\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState(),\n\t\t\t\t(String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? { maxDepth: 4, maxKeys: 90 }\n\t\t\t\t\t: undefined\n\t\t\t);\n\t\tlet userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\t\t// Gary local models are small; keep prompts compact and avoid noisy context.\n\t\ttry {\n\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() === \"gary\") {\n\t\t\t\tuserMsg = _compactUserMsgForGary(userMsg);\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\t\tconst systemPromptFinal = (() => {\n\t\t\ttry {\n\t\t\t\tif (String(cfgN?.provider || \"\").toLowerCase() !== \"gary\") return systemPrompt;\n\t\t\t\t// Keep system prompts short for tiny local models; the kind is also present in USER payload.\n\t\t\t\treturn String(systemPrompt || \"\").slice(0, 3500);\n\t\t\t} catch {\n\t\t\t\treturn systemPrompt;\n\t\t\t}\n\t\t})();\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPromptFinal,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tlet uploadToGary = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {}\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet parsed = _safeJsonParse(text);\n\t\tparsed = _coerceWrongKindDecision(kind, parsed);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tlet uploadToGary = null;\n\t\t\t\ttry {\n\t\t\t\t\tconst cfgN = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\t\tif (String(cfgN.provider || \"\").toLowerCase() === \"gary\" && String(cfgN.apiKey || \"\").trim()) {\n\t\t\t\t\t\tuploadToGary = {\n\t\t\t\t\t\t\tprovider: \"gary\",\n\t\t\t\t\t\t\tbaseUrl: String(cfgN.baseUrl || \"\").trim(),\n\t\t\t\t\t\t\tapiKey: String(cfgN.apiKey || \"\").trim(),\n\t\t\t\t\t\t\thmacSecret: String(cfgN.hmacSecret || \"\").trim(),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch {}\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPromptFinal || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries, uploadToGary }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s === \"gary-predictions-v1\" || s.startsWith(\"gary-\")) return \"gary\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\" || llmProvider === \"gary\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst garyKey = (o && (o.garyApiKey || o.garyKey || (provider === \"gary\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.garyApiKey || o.garyKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gary_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyKey\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\t\t\t\tconst garyBaseUrl = String((o && (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.garyBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gary_base_url\", \"http://127.0.0.1:8088\")\n\t\t\t\t).trim() || \"http://127.0.0.1:8088\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gary\")\n\t\t\t\t\t\t\t? garyBaseUrl\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: (() => {\n\t\t\t\t\t\tconst fallback = (provider === \"gary\")\n\t\t\t\t\t\t\t? _readLs(\"fdv_gary_timeout_ms\", \"45000\")\n\t\t\t\t\t\t\t: _readLs(\"fdv_openai_timeout_ms\", \"12000\");\n\t\t\t\t\t\treturn _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", fallback), (provider === \"gary\") ? 45000 : 12000);\n\t\t\t\t\t})(),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst cfgN = (() => {\n\t\t\t\ttry { return normalizeLlmConfig(_getConfig() || {}); } catch { return null; }\n\t\t\t})();\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 60).map(String) : [],\n\t\t\t\t// Key hints are huge; tiny local models do better without them.\n\t\t\t\tkeyHints: (String(cfgN?.provider || \"\").toLowerCase() === \"gary\")\n\t\t\t\t\t? undefined\n\t\t\t\t\t: ((keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined),\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tomitState: true,\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 260,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tomitState: true,\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 320,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "438560ba90cf10992f0a14fd2eb352e018d8b4e6": {"t": 1785766682.1197412, "n": 27420, "h": "e7d5a13be909286e", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\nimport { TRAINING_CAPTURE } from \"../config/env.js\";\nimport { appendTrainingCapture, clearTrainingCaptures, downloadTrainingCapturesJsonl, getTrainingCaptures, isTrainingCaptureEnabled } from \"./training.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\t// Debug helpers (browser): export/clear captures.\n\ttry {\n\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\tif (g && !g.__fdvTraining) {\n\t\t\tg.__fdvTraining = {\n\t\t\t\tenabled: () => isTrainingCaptureEnabled(),\n\t\t\t\tget: () => getTrainingCaptures(),\n\t\t\t\tclear: () => clearTrainingCaptures(),\n\t\t\t\tdownloadJsonl: () => downloadTrainingCapturesJsonl({ filenamePrefix: \"fdv-gary-captures\" }),\n\t\t\t\tcfg: () => {\n\t\t\t\t\ttry { return TRAINING_CAPTURE || {}; } catch { return {}; }\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t} catch {}\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\ttry {\n\t\t\t\tif (TRAINING_CAPTURE?.enabled) {\n\t\t\t\t\tappendTrainingCapture({\n\t\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\t\tok: false,\n\t\t\t\t\t\terr: String(res.err || \"\"),\n\t\t\t\t\t\tuserMsg,\n\t\t\t\t\t\tsystem: String(systemPrompt || \"\").slice(0, 8000),\n\t\t\t\t\t\ttext: \"\",\n\t\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries }).catch(() => {});\n\t\t\t\t}\n\t\t\t} catch {}\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\n\t\ttry {\n\t\t\tconst shouldCapture = !!TRAINING_CAPTURE?.enabled && (res.ok || !!TRAINING_CAPTURE?.includeBad);\n\t\t\tif (shouldCapture) {\n\t\t\t\tappendTrainingCapture({\n\t\t\t\t\tmode: \"inference\",\n\t\t\t\t\tsource: \"fdv_auto_trader\",\n\t\t\t\t\tkind: String(kind || \"\"),\n\t\t\t\t\tok: !!res.ok,\n\t\t\t\t\terr: res.ok ? \"\" : String(res.err || \"\"),\n\t\t\t\t\tuserMsg,\n\t\t\t\t\tsystem: String(systemPrompt || \"\").slice(0, 8000),\n\t\t\t\t\ttext: String(text || \"\").slice(0, 20000),\n\t\t\t\t\tparsed: _redactDeep(parsed),\n\t\t\t\t\tdecision: _redactDeep(validated),\n\t\t\t\t\tmeta: _redactDeep(meta),\n\t\t\t\t}, { storageKey: TRAINING_CAPTURE.storageKey, maxEntries: TRAINING_CAPTURE.maxEntries }).catch(() => {});\n\t\t\t}\n\t\t} catch {}\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "c68573fdae1639c0f1215a285d230ac9e11417c8": {"t": 1785766684.05542, "n": 25567, "h": "2d03b3dfaed74393", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\" && action !== \"long_hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"long_hold\") {\n\t\tconst raw = Number(obj.holdSeconds);\n\t\tconst hs = Number.isFinite(raw) ? _clampNum(raw, 5, 120) : 30;\n\t\tout.holdSeconds = Math.floor(hs);\n\t}\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "5908baca1bf89628b035867051ec7f60f1819193": {"t": 1785766685.6920135, "n": 25367, "h": "e8add4dbdbaa7aa2", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { getGarySystemPrompt } from \"./personas/agent.gary.prompts.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = getGarySystemPrompt(kind, { evolveSummary });\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "584f32b56e0e0f7371780432cb8a599bb66791d3": {"t": 1785766687.3425593, "n": 25400, "h": "9504df504c2f3cae", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./personas/agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateForecast(forecast) {\n\ttry {\n\t\tif (!forecast || typeof forecast !== \"object\") return null;\n\t\tconst out = {};\n\n\t\tconst horizonSecs = Math.floor(_safeNum(forecast.horizonSecs, NaN));\n\t\tif (Number.isFinite(horizonSecs)) out.horizonSecs = _clampNum(horizonSecs, 30, 24 * 60 * 60);\n\n\t\tconst upProb = _safeNum(forecast.upProb, NaN);\n\t\tconst downProb = _safeNum(forecast.downProb, NaN);\n\t\tif (Number.isFinite(upProb)) out.upProb = _clampNum(upProb, 0, 1);\n\t\tif (Number.isFinite(downProb)) out.downProb = _clampNum(downProb, 0, 1);\n\n\t\tconst expectedMovePct = _safeNum(forecast.expectedMovePct, NaN);\n\t\tif (Number.isFinite(expectedMovePct)) out.expectedMovePct = _clampNum(expectedMovePct, -250, 250);\n\n\t\tconst regime = String(forecast.regime || \"\").trim();\n\t\tif (regime) out.regime = regime.slice(0, 40);\n\n\t\tconst note = String(forecast.note || \"\").trim();\n\t\tif (note) out.note = note.slice(0, 160);\n\n\t\t// If only one probability is present, allow it; consumers may infer the other.\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\tconst forecast = _validateForecast(obj.forecast);\n\tif (forecast) out.forecast = forecast;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "3878b3f9f31ed2f7fec6e950e95563fcfb08c92e": {"t": 1785766689.125022, "n": 24147, "h": "ad3c9fbf3afb378e", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./personas/agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "ff68cf1744afd473b919943d39047bed92848816": {"t": 1785766690.855134, "n": 24202, "h": "a39f02e34698247c", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./personas/agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconsole.log(\"Agent Driver User Message:\", userMsg);\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "51d9e9d4da67a49f39ea777be12ed44d48baf32e": {"t": 1785766692.6018252, "n": 24147, "h": "ad3c9fbf3afb378e", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./personas/agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s === \"deepseek-chat\" || s === \"deepseek-reasoner\" || s.startsWith(\"deepseek-\")) return \"deepseek\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"grok\" || llmProvider === \"deepseek\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst deepseekKey = (o && (o.deepseekApiKey || o.deepseekKey || (provider === \"deepseek\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.deepseekApiKey || o.deepseekKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\t\t\t\tconst deepseekBaseUrl = String((o && (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.deepseekBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_deepseek_base_url\", \"https://api.deepseek.com\")\n\t\t\t\t).trim() || \"https://api.deepseek.com\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: (provider === \"deepseek\")\n\t\t\t\t\t\t\t\t\t? deepseekBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "161ba5e234a1e4b433440403a78fea769fd4b3c4": {"t": 1785766694.2094078, "n": 23354, "h": "e4eec9f99eda78bc", "c": "import { createChatClientFromConfig, normalizeLlmConfig } from \"./frameworks/index.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\t// Heuristic recovery: strip code fences / surrounding prose and parse the first JSON object/array.\n\t\ttry {\n\t\t\tlet t = String(s || \"\");\n\t\t\t// ```json ... ``` or ``` ... ```\n\t\t\tt = t.replace(/^\\s*```(?:json)?\\s*/i, \"\").replace(/\\s*```\\s*$/i, \"\");\n\t\t\tconst firstObj = t.indexOf(\"{\");\n\t\t\tconst lastObj = t.lastIndexOf(\"}\");\n\t\t\tif (firstObj >= 0 && lastObj > firstObj) {\n\t\t\t\treturn JSON.parse(t.slice(firstObj, lastObj + 1));\n\t\t\t}\n\t\t\tconst firstArr = t.indexOf(\"[\");\n\t\t\tconst lastArr = t.lastIndexOf(\"]\");\n\t\t\tif (firstArr >= 0 && lastArr > firstArr) {\n\t\t\t\treturn JSON.parse(t.slice(firstArr, lastArr + 1));\n\t\t\t}\n\t\t} catch {}\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./personas/agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Enable if any supported key is present (OpenAI or Gemini via normalized config).\n\t\t\tconst c = normalizeLlmConfig(cfg);\n\t\t\tconst k = String(c.apiKey || cfg.llmApiKey || cfg.apiKey || cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\treturn createChatClientFromConfig(cfg);\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true, err: \"disabled\" };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = normalizeLlmConfig(_getConfig() || {});\n\t\t\tconst model = String(cfg.model || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\n\t\t\t\tconst _inferProviderForModel = (modelName) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst s = String(modelName || \"\").trim().toLowerCase();\n\t\t\t\t\t\tif (!s) return \"openai\";\n\t\t\t\t\t\tif (s.startsWith(\"gemini-\")) return \"gemini\";\n\t\t\t\t\t\tif (s.startsWith(\"grok-\")) return \"grok\";\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn \"openai\";\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst llmProvider = String(\n\t\t\t\t\t(o && (o.llmProvider || o.provider)) ? (o.llmProvider || o.provider) : _readLs(\"fdv_llm_provider\", \"\")\n\t\t\t\t).trim().toLowerCase();\n\n\t\t\t\tconst llmModel = String(\n\t\t\t\t\t(o && (o.llmModel || o.model || o.openaiModel))\n\t\t\t\t\t\t? (o.llmModel || o.model || o.openaiModel)\n\t\t\t\t\t\t: _readLs(\"fdv_llm_model\", _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"))\n\t\t\t\t).trim() || \"gpt-4o-mini\";\n\n\t\t\t\tconst provider = (llmProvider === \"gemini\" || llmProvider === \"openai\")\n\t\t\t\t\t? llmProvider\n\t\t\t\t\t: _inferProviderForModel(llmModel);\n\n\t\t\t\tconst openaiKey = (o && (o.openaiApiKey || o.apiKey || o.llmApiKey) && provider === \"openai\")\n\t\t\t\t\t? String(o.openaiApiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_openai_key\", \"\");\n\n\t\t\t\tconst geminiKey = (o && (o.geminiApiKey || o.geminiKey || (provider === \"gemini\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.geminiApiKey || o.geminiKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_gemini_key\", \"\");\n\n\t\t\t\tconst grokKey = (o && (o.grokApiKey || o.grokKey || (provider === \"grok\" ? (o.apiKey || o.llmApiKey) : \"\")))\n\t\t\t\t\t? String(o.grokApiKey || o.grokKey || o.apiKey || o.llmApiKey)\n\t\t\t\t\t: _readLs(\"fdv_grok_key\", \"\");\n\n\t\t\t\tconst llmApiKey = String(\n\t\t\t\t\t(o && (o.llmApiKey || o.apiKey))\n\t\t\t\t\t\t? (o.llmApiKey || o.apiKey)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiKey\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokKey\n\t\t\t\t\t\t\t\t: openaiKey\n\t\t\t\t).trim();\n\n\t\t\t\tconst openaiBaseUrl = String((o && o.openaiBaseUrl) ? o.openaiBaseUrl : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\")).trim() || \"https://api.openai.com/v1\";\n\t\t\t\tconst geminiBaseUrl = String((o && (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.geminiBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_gemini_base_url\", \"https://generativelanguage.googleapis.com/v1beta\")\n\t\t\t\t).trim() || \"https://generativelanguage.googleapis.com/v1beta\";\n\t\t\t\tconst grokBaseUrl = String((o && (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t? (o.grokBaseUrl || o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t: _readLs(\"fdv_grok_base_url\", \"https://api.x.ai/v1\")\n\t\t\t\t).trim() || \"https://api.x.ai/v1\";\n\n\t\t\t\tconst llmBaseUrl = String(\n\t\t\t\t\t(o && (o.llmBaseUrl || o.baseUrl))\n\t\t\t\t\t\t? (o.llmBaseUrl || o.baseUrl)\n\t\t\t\t\t\t: (provider === \"gemini\")\n\t\t\t\t\t\t\t? geminiBaseUrl\n\t\t\t\t\t\t\t: (provider === \"grok\")\n\t\t\t\t\t\t\t\t? grokBaseUrl\n\t\t\t\t\t\t\t\t: openaiBaseUrl\n\t\t\t\t).trim();\n\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\t// Provider-agnostic keys used by the framework factory.\n\t\t\t\t\tllmProvider: provider,\n\t\t\t\t\tllmApiKey,\n\t\t\t\t\tllmModel,\n\t\t\t\t\tllmBaseUrl,\n\t\t\t\t\tllmTimeoutMs: _safeNum((o && o.llmTimeoutMs) ? o.llmTimeoutMs : _readLs(\"fdv_llm_timeout_ms\", _readLs(\"fdv_openai_timeout_ms\", \"12000\")), 12000),\n\n\t\t\t\t\t// Back-compat: older fields expected by legacy paths/logging.\n\t\t\t\t\topenaiApiKey: provider === \"openai\" ? openaiKey : \"\",\n\t\t\t\t\topenaiModel: provider === \"openai\" ? llmModel : \"\",\n\t\t\t\t\topenaiBaseUrl: provider === \"openai\" ? llmBaseUrl : openaiBaseUrl,\n\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n\nexport function createAgentJsonRunner({\n\tlog,\n\tgetConfig,\n\tfetchFn,\n\tprefix = \"AGENT\",\n\tcacheTtlMs = 2500,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\tconst p = String(prefix || \"\").trim();\n\t\t\t_rawLog(p ? `[${p}] ${String(msg ?? \"\")}` : String(msg ?? \"\"), type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({});\n\n\tconst cache = new Map();\n\n\tfunction _getCfgSafe() {\n\t\ttry {\n\t\t\treturn normalizeLlmConfig(_getConfig() || {});\n\t\t} catch {\n\t\t\treturn { enabled: false, apiKey: \"\" };\n\t\t}\n\t}\n\n\tfunction _enabled(cfg) {\n\t\ttry {\n\t\t\tif (!cfg || cfg.enabled === false) return false;\n\t\t\treturn !!String(cfg.apiKey || \"\").trim();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _getClient(cfg) {\n\t\ttry {\n\t\t\tconst c = cfg && typeof cfg === \"object\" ? cfg : {};\n\t\t\treturn createChatClientFromConfig(c, { fetchFn });\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheGet(key) {\n\t\ttry {\n\t\t\tconst rec = cache.get(key);\n\t\t\tif (!rec) return null;\n\t\t\tif ((now() - rec.at) > cacheTtlMs) { cache.delete(key); return null; }\n\t\t\treturn rec.res;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tfunction _cacheSet(key, res) {\n\t\ttry { cache.set(key, { at: now(), res }); } catch {}\n\t}\n\n\tasync function chatJsonWithMeta({\n\t\tsystem,\n\t\tuser,\n\t\ttemperature = 0.1,\n\t\tmaxTokens = null,\n\t\tcacheKey = \"\",\n\t\tlogRequest = false,\n\t} = {}) {\n\t\tconst cfg = _getCfgSafe();\n\t\tif (!_enabled(cfg)) return { ok: false, disabled: true };\n\t\tconst client = _getClient(cfg);\n\t\tif (!client) return { ok: false, disabled: true };\n\n\t\tconst ck = String(cacheKey || \"\");\n\t\tif (ck) {\n\t\t\tconst prev = _cacheGet(ck);\n\t\t\tif (prev) return prev;\n\t\t}\n\n\t\ttry {\n\t\t\tif (logRequest) {\n\t\t\t\tconst model = String(cfg.model || \"\");\n\t\t\t\t_log(`call model=${model}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tlet meta = null;\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: String(system || \"\"),\n\t\t\t\tuser: String(user || \"\"),\n\t\t\t\ttemperature: Number.isFinite(Number(temperature)) ? Number(temperature) : 0.1,\n\t\t\t\tmaxTokens: Math.max(120, Number.isFinite(Number(maxTokens)) ? Number(maxTokens) : Number(cfg.maxTokens || 350)),\n\t\t\t};\n\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\ttext = String(meta?.text || \"\");\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}`, \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tif (ck) _cacheSet(ck, res);\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst res = { ok: true, text, parsed, meta };\n\t\tif (ck) _cacheSet(ck, res);\n\t\treturn res;\n\t}\n\n\treturn { chatJsonWithMeta };\n}\n"}, "bb1c050a090366ea928d09f794420a237263644d": {"t": 1785766696.1760712, "n": 16511, "h": "ddafc618cace5058", "c": "import { createOpenAIChatClient } from \"./frameworks/open.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateConfigScanDecision(obj) {\n\ttry {\n\t\tif (!obj || typeof obj !== \"object\") return null;\n\t\tconst action = String(obj.action || \"\").toLowerCase();\n\t\tif (action !== \"apply\" && action !== \"skip\") return null;\n\t\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\t\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\t\tconst out = { kind: \"config_scan\", action, confidence, reason };\n\t\tif (obj.config && typeof obj.config === \"object\") {\n\t\t\tconst cfg = {};\n\t\t\tlet n = 0;\n\t\t\tfor (const [k, v] of Object.entries(obj.config)) {\n\t\t\t\tn++;\n\t\t\t\tif (n > 120) break;\n\t\t\t\tconst key = String(k || \"\").slice(0, 80);\n\t\t\t\tif (!key) continue;\n\t\t\t\tconst t = typeof v;\n\t\t\t\tif (t === \"number\" || t === \"boolean\" || t === \"string\") cfg[key] = v;\n\t\t\t}\n\t\t\tif (Object.keys(cfg).length) out.config = cfg;\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Only enable if an API key is present.\n\t\t\tconst k = String(cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\tconst apiKey = String(cfg.openaiApiKey || \"\").trim();\n\t\tif (!apiKey) return null;\n\t\treturn createOpenAIChatClient({\n\t\t\tapiKey,\n\t\t\tbaseUrl: String(cfg.openaiBaseUrl || \"https://api.openai.com/v1\").trim() || \"https://api.openai.com/v1\",\n\t\t\tmodel: String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\",\n\t\t\ttimeoutMs: Math.max(5000, Number(cfg.timeoutMs || 12_000)),\n\t\t});\n\t}\n\n\tasync function _run(kind, payload, opts = null) {\n\t\tif (!_enabled()) return { ok: false, disabled: true };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true };\n\t\tconst body = _redactDeep(payload);\n\t\tconst options = (opts && typeof opts === \"object\") ? opts : {};\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\tconst ck = String(payload?.cacheKey || options?.cacheKey || \"\");\n\t\t\t\treturn `${kind}:${mint || ck || \"global\"}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = options.omitState\n\t\t\t? {}\n\t\t\t: _redactDeep((\"stateOverride\" in options) ? options.stateOverride : _getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\tlet meta = null;\n\t\ttry {\n\t\t\tconst req = {\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: Number.isFinite(Number(options.temperature)) ? Number(options.temperature) : 0.15,\n\t\t\t\tmaxTokens: Math.max(\n\t\t\t\t\t120,\n\t\t\t\t\tNumber.isFinite(Number(options.maxTokens))\n\t\t\t\t\t\t? Number(options.maxTokens)\n\t\t\t\t\t\t: Number((_getConfig() || {}).maxTokens || 350)\n\t\t\t\t),\n\t\t\t};\n\t\t\tif (client && typeof client.chatJsonWithMeta === \"function\") {\n\t\t\t\tmeta = await client.chatJsonWithMeta(req);\n\t\t\t\ttext = String(meta?.text || \"\");\n\t\t\t} else {\n\t\t\t\ttext = await client.chatJson(req);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\ttry {\n\t\t\tif (meta && typeof meta === \"object\") {\n\t\t\t\tconst est = Number(meta.estPromptTokens || 0);\n\t\t\t\tconst u = meta.usage || null;\n\t\t\t\tconst pt = u && Number.isFinite(Number(u.promptTokens)) ? Number(u.promptTokens) : null;\n\t\t\t\tconst ct = u && Number.isFinite(Number(u.completionTokens)) ? Number(u.completionTokens) : null;\n\t\t\t\tconst tt = u && Number.isFinite(Number(u.totalTokens)) ? Number(u.totalTokens) : null;\n\t\t\t\tconst parts = [];\n\t\t\t\tif (est > 0) parts.push(`estPrompt≈${est}`);\n\t\t\t\tif (pt !== null) parts.push(`prompt=${pt}`);\n\t\t\t\tif (ct !== null) parts.push(`completion=${ct}`);\n\t\t\t\tif (tt !== null) parts.push(`total=${tt}`);\n\t\t\t\tif (parts.length) _log(`tokens ${parts.join(\" \")}`, \"info\");\n\t\t\t}\n\t\t} catch {}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\")\n\t\t\t? _validateBuyDecision(parsed)\n\t\t\t: (kind === \"sell\")\n\t\t\t\t? _validateSellDecision(parsed)\n\t\t\t\t: (kind === \"config_scan\")\n\t\t\t\t\t? _validateConfigScanDecision(parsed)\n\t\t\t\t\t: null;\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\topenaiApiKey: (o && o.openaiApiKey) ? String(o.openaiApiKey) : _readLs(\"fdv_openai_key\", \"\"),\n\t\t\t\t\topenaiModel: (o && o.openaiModel) ? String(o.openaiModel) : _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"),\n\t\t\t\t\topenaiBaseUrl: (o && o.openaiBaseUrl) ? String(o.openaiBaseUrl) : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\"),\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\n\t\tasync scanConfig({ market, allowedKeys, keyHints, note, stateSummary } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t// Keep request cheap: omit keyHints by default unless explicitly provided.\n\t\t\t\tkeyHints: (keyHints && typeof keyHints === \"object\" && Object.keys(keyHints).length) ? keyHints : undefined,\n\t\t\t\tnote: String(note || \"\").slice(0, 500),\n\t\t\t};\n\t\t\tconst first = await _run(\"config_scan\", payload, {\n\t\t\t\tcacheKey: \"startup\",\n\t\t\t\ttemperature: 0.1,\n\t\t\t\tmaxTokens: 650,\n\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t});\n\n\t\t\t// Retry once if the model produced truncated/invalid JSON.\n\t\t\tif (first && first.ok === false && String(first.err || \"\") === \"invalid_json\") {\n\t\t\t\tconst payload2 = {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\tmarket: market && typeof market === \"object\" ? market : {},\n\t\t\t\t\tallowedKeys: Array.isArray(allowedKeys) ? allowedKeys.slice(0, 80).map(String) : [],\n\t\t\t\t\tnote: (String(note || \"\").slice(0, 380) + \"\\nRetry: return minimal JSON, only keys you would change. No prose.\").slice(0, 500),\n\t\t\t\t};\n\t\t\t\treturn await _run(\"config_scan\", payload2, {\n\t\t\t\t\tcacheKey: \"startup_retry\",\n\t\t\t\t\ttemperature: 0.05,\n\t\t\t\t\tmaxTokens: 950,\n\t\t\t\t\tstateOverride: stateSummary && typeof stateSummary === \"object\" ? stateSummary : {},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn first;\n\t\t},\n\t};\n}\n"}, "84574b9db03ad944ea1b5d342d109bccfd3a62a2": {"t": 1785766697.7209725, "n": 12696, "h": "123e2cac705988d1", "c": "import { createOpenAIChatClient } from \"./frameworks/open.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Only enable if an API key is present.\n\t\t\tconst k = String(cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\tconst apiKey = String(cfg.openaiApiKey || \"\").trim();\n\t\tif (!apiKey) return null;\n\t\treturn createOpenAIChatClient({\n\t\t\tapiKey,\n\t\t\tbaseUrl: String(cfg.openaiBaseUrl || \"https://api.openai.com/v1\").trim() || \"https://api.openai.com/v1\",\n\t\t\tmodel: String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\",\n\t\t\ttimeoutMs: Math.max(5000, Number(cfg.timeoutMs || 12_000)),\n\t\t});\n\t}\n\n\tasync function _run(kind, payload) {\n\t\tif (!_enabled()) return { ok: false, disabled: true };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true };\n\t\tconst body = _redactDeep(payload);\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\treturn `${kind}:${mint}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = _redactDeep(_getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\ttext = await client.chatJson({\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: 0.15,\n\t\t\t\tmaxTokens: Math.max(120, Number((_getConfig() || {}).maxTokens || 350)),\n\t\t\t});\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\") ? _validateBuyDecision(parsed) : _validateSellDecision(parsed);\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\topenaiApiKey: (o && o.openaiApiKey) ? String(o.openaiApiKey) : _readLs(\"fdv_openai_key\", \"\"),\n\t\t\t\t\topenaiModel: (o && o.openaiModel) ? String(o.openaiModel) : _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"),\n\t\t\t\t\topenaiBaseUrl: (o && o.openaiBaseUrl) ? String(o.openaiBaseUrl) : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\"),\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\t};\n}\n"}, "144f7088298eba29d532ad93c5f392b7754f9220": {"t": 1785766699.1014092, "n": 12823, "h": "5e95fdd661cbec71", "c": "import { createOpenAIChatClient } from \"./frameworks/open.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\t\tset(\"minNetEdgePct\", tune.minNetEdgePct, -25, 25, 0.25);\n\t\tset(\"edgeSafetyBufferPct\", tune.edgeSafetyBufferPct, 0, 5, 0.05);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\t// Optional evolve feedback (ignored by bot execution, used for long-term improvement).\n\ttry {\n\t\tif (obj.evolve && typeof obj.evolve === \"object\") {\n\t\t\tconst outcomeTs = _safeNum(obj.evolve.outcomeTs, 0);\n\t\t\tconst selfCritique = String(obj.evolve.selfCritique || \"\").slice(0, 220);\n\t\t\tconst lesson = String(obj.evolve.lesson || \"\").slice(0, 220);\n\t\t\tif (Number.isFinite(outcomeTs) && outcomeTs > 0 && (selfCritique || lesson)) {\n\t\t\t\tout.evolve = { outcomeTs };\n\t\t\t\tif (selfCritique) out.evolve.selfCritique = selfCritique;\n\t\t\t\tif (lesson) out.evolve.lesson = lesson;\n\t\t\t}\n\t\t}\n\t} catch {}\n\treturn out;\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readLsJson(key, fallback = null) {\n\ttry {\n\t\tconst raw = _readLs(key, \"\");\n\t\tif (!raw) return fallback;\n\t\tconst json = _safeJsonParse(raw);\n\t\treturn json ?? fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Only enable if an API key is present.\n\t\t\tconst k = String(cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\tconst apiKey = String(cfg.openaiApiKey || \"\").trim();\n\t\tif (!apiKey) return null;\n\t\treturn createOpenAIChatClient({\n\t\t\tapiKey,\n\t\t\tbaseUrl: String(cfg.openaiBaseUrl || \"https://api.openai.com/v1\").trim() || \"https://api.openai.com/v1\",\n\t\t\tmodel: String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\",\n\t\t\ttimeoutMs: Math.max(5000, Number(cfg.timeoutMs || 12_000)),\n\t\t});\n\t}\n\n\tasync function _run(kind, payload) {\n\t\tif (!_enabled()) return { ok: false, disabled: true };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true };\n\t\tconst body = _redactDeep(payload);\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\treturn `${kind}:${mint}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = _redactDeep(_getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tconst evolveSummary = (() => {\n\t\t\ttry {\n\t\t\t\tconst s = _readLsJson(\"fdv_agent_evolve_summary_v1\", null);\n\t\t\t\tconst txt = String(s?.text || \"\").trim();\n\t\t\t\treturn txt ? txt.slice(0, 1400) : \"\";\n\t\t\t} catch {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t})();\n\t\tconst systemPrompt = evolveSummary\n\t\t\t? `${GARY_SYSTEM_PROMPT}\\n\\n${evolveSummary}`\n\t\t\t: GARY_SYSTEM_PROMPT;\n\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\ttext = await client.chatJson({\n\t\t\t\tsystem: systemPrompt,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: 0.15,\n\t\t\t\tmaxTokens: Math.max(120, Number((_getConfig() || {}).maxTokens || 350)),\n\t\t\t});\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\") ? _validateBuyDecision(parsed) : _validateSellDecision(parsed);\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\tconst riskRaw = String((o && o.riskLevel) ? o.riskLevel : _readLs(\"fdv_agent_risk\", \"safe\")).trim().toLowerCase();\n\t\t\t\tconst riskLevel = (riskRaw === \"safe\" || riskRaw === \"medium\" || riskRaw === \"degen\") ? riskRaw : \"safe\";\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\triskLevel,\n\t\t\t\t\topenaiApiKey: (o && o.openaiApiKey) ? String(o.openaiApiKey) : _readLs(\"fdv_openai_key\", \"\"),\n\t\t\t\t\topenaiModel: (o && o.openaiModel) ? String(o.openaiModel) : _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"),\n\t\t\t\t\topenaiBaseUrl: (o && o.openaiBaseUrl) ? String(o.openaiBaseUrl) : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\"),\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\t};\n}\n"}, "e194b2c331263097e295d6e4b2e81df826650855": {"t": 1785766700.6217494, "n": 10847, "h": "66fc070df8490db6", "c": "import { createOpenAIChatClient } from \"./frameworks/open.js\";\n\nfunction now() {\n\treturn Date.now();\n}\n\nfunction _safeNum(v, fallback = 0) {\n\tconst n = Number(v);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction _clampNum(n, min, max) {\n\tconst v = _safeNum(n, min);\n\treturn Math.max(min, Math.min(max, v));\n}\n\nfunction _safeJsonParse(s) {\n\ttry {\n\t\treturn JSON.parse(String(s || \"\"));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _redactDeep(obj, { maxDepth = 6, maxKeys = 200 } = {}, _depth = 0, _seen = new WeakSet()) {\n\ttry {\n\t\tif (_depth > maxDepth) return \"[truncated]\";\n\t\tif (obj == null) return obj;\n\t\tif (typeof obj === \"string\") {\n\t\t\t// avoid leaking secrets in raw strings\n\t\t\tif (/sk-|secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(obj)) return \"[redacted]\";\n\t\t\treturn obj.length > 2000 ? obj.slice(0, 2000) + \"…\" : obj;\n\t\t}\n\t\tif (typeof obj === \"number\" || typeof obj === \"boolean\") return obj;\n\t\tif (typeof obj === \"bigint\") return String(obj);\n\t\tif (typeof obj === \"function\") return `[fn ${obj.name || \"anonymous\"}]`;\n\t\tif (typeof obj !== \"object\") return String(obj);\n\t\tif (_seen.has(obj)) return \"[cycle]\";\n\t\t_seen.add(obj);\n\n\t\tif (Array.isArray(obj)) {\n\t\t\treturn obj.slice(0, 50).map((v) => _redactDeep(v, { maxDepth, maxKeys }, _depth + 1, _seen));\n\t\t}\n\n\t\tconst out = {};\n\t\tlet nKeys = 0;\n\t\tfor (const k of Object.keys(obj)) {\n\t\t\tnKeys++;\n\t\t\tif (nKeys > maxKeys) {\n\t\t\t\tout.__truncated__ = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst key = String(k || \"\");\n\t\t\tif (/secret|private|seed|keypair|secretKey|autoWalletSecret|rpcHeaders|authorization|bearer/i.test(key)) {\n\t\t\t\tout[key] = \"[redacted]\";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tout[key] = _redactDeep(obj[key], { maxDepth, maxKeys }, _depth + 1, _seen);\n\t\t}\n\t\treturn out;\n\t} catch {\n\t\treturn \"[unserializable]\";\n\t}\n}\n\nimport { GARY_SYSTEM_PROMPT } from \"./agent.gary.js\";\n\nfunction _validateTune(tune) {\n\ttry {\n\t\tif (!tune || typeof tune !== \"object\") return null;\n\t\tconst out = {};\n\t\tconst set = (k, v, min, max, round = null) => {\n\t\t\tconst n = _safeNum(v, NaN);\n\t\t\tif (!Number.isFinite(n)) return;\n\t\t\tlet clamped = _clampNum(n, min, max);\n\t\t\tif (round === \"int\") clamped = Math.floor(clamped);\n\t\t\telse if (typeof round === \"number\") clamped = Math.round(clamped / round) * round;\n\t\t\tout[k] = clamped;\n\t\t};\n\n\t\t// Risk / exit tuning\n\t\tset(\"takeProfitPct\", tune.takeProfitPct, 0, 250, 0.25);\n\t\tset(\"stopLossPct\", tune.stopLossPct, 0, 99, 0.25);\n\t\tset(\"trailPct\", tune.trailPct, 0, 99, 0.25);\n\t\tset(\"minProfitToTrailPct\", tune.minProfitToTrailPct, 0, 200, 0.25);\n\n\t\t// Hold tuning\n\t\tset(\"minHoldSecs\", tune.minHoldSecs, 0, 20_000, \"int\");\n\t\tset(\"maxHoldSecs\", tune.maxHoldSecs, 10, 20_000, \"int\");\n\n\t\t// Buy sizing / gating\n\t\tset(\"buyPct\", tune.buyPct, 0.01, 0.5, 0.005);\n\t\tset(\"minNetEdgePct\", tune.minNetEdgePct, -25, 25, 0.25);\n\t\tset(\"edgeSafetyBufferPct\", tune.edgeSafetyBufferPct, 0, 5, 0.05);\n\n\t\t// Entry simulation tuning\n\t\tset(\"entrySimMinWinProb\", tune.entrySimMinWinProb, 0, 1, 0.01);\n\t\tset(\"entrySimHorizonSecs\", tune.entrySimHorizonSecs, 30, 600, \"int\");\n\n\t\treturn Object.keys(out).length ? out : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction _validateBuyDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"buy\" && action !== \"skip\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"buy\", action, confidence, reason };\n\tif (obj.buy && typeof obj.buy === \"object\") {\n\t\tconst solUi = _safeNum(obj.buy.solUi, NaN);\n\t\tconst slippageBps = _safeNum(obj.buy.slippageBps, NaN);\n\t\tout.buy = {};\n\t\tif (Number.isFinite(solUi) && solUi > 0) out.buy.solUi = solUi;\n\t\tif (Number.isFinite(slippageBps) && slippageBps > 0) out.buy.slippageBps = Math.floor(slippageBps);\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\treturn out;\n}\n\nfunction _validateSellDecision(obj) {\n\tif (!obj || typeof obj !== \"object\") return null;\n\tconst action = String(obj.action || \"\").toLowerCase();\n\tif (action !== \"sell_all\" && action !== \"sell_partial\" && action !== \"hold\") return null;\n\tconst confidence = _clampNum(obj.confidence, 0, 1);\n\tconst reason = String(obj.reason || \"\").slice(0, 220);\n\tconst out = { kind: \"sell\", action, confidence, reason };\n\tif (action === \"sell_partial\") {\n\t\tconst pct = _clampNum(obj?.sell?.pct, 1, 100);\n\t\tout.sell = { pct: Math.floor(pct) };\n\t}\n\tconst tune = _validateTune(obj.tune);\n\tif (tune) out.tune = tune;\n\treturn out;\n}\n\nfunction _readLs(key, fallback = \"\") {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return fallback;\n\t\treturn String(localStorage.getItem(String(key || \"\")) || fallback);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nfunction _readBoolLs(key, fallback = false) {\n\ttry {\n\t\tconst v = _readLs(key, \"\").trim().toLowerCase();\n\t\tif (!v) return fallback;\n\t\tif (v === \"1\" || v === \"true\" || v === \"yes\" || v === \"on\") return true;\n\t\tif (v === \"0\" || v === \"false\" || v === \"no\" || v === \"off\") return false;\n\t\treturn fallback;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\nexport function createAutoTraderAgentDriver({\n\tlog,\n\tgetState,\n\tgetConfig,\n} = {}) {\n\tconst _rawLog = typeof log === \"function\" ? log : () => {};\n\tconst _log = (msg, type) => {\n\t\ttry {\n\t\t\t_rawLog(`[AGENT GARY] ${String(msg ?? \"\")}`, type);\n\t\t} catch {\n\t\t\ttry { _rawLog(String(msg ?? \"\"), type); } catch {}\n\t\t}\n\t};\n\tconst _getState = typeof getState === \"function\" ? getState : () => ({});\n\tconst _getConfig = typeof getConfig === \"function\" ? getConfig : () => ({ });\n\n\tconst cache = new Map();\n\tconst CACHE_TTL_MS = 2500;\n\n\tfunction _enabled() {\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tif (cfg.enabled === false) return false;\n\t\t\t// Only enable if an API key is present.\n\t\t\tconst k = String(cfg.openaiApiKey || \"\").trim();\n\t\t\treturn !!k;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tfunction _fmtShortJson(obj, maxLen = 2200) {\n\t\ttry {\n\t\t\tconst s = JSON.stringify(obj);\n\t\t\tif (typeof s !== \"string\") return \"\";\n\t\t\treturn s.length > maxLen ? s.slice(0, maxLen) + \"…\" : s;\n\t\t} catch {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tfunction _getClient() {\n\t\tconst cfg = _getConfig() || {};\n\t\tconst apiKey = String(cfg.openaiApiKey || \"\").trim();\n\t\tif (!apiKey) return null;\n\t\treturn createOpenAIChatClient({\n\t\t\tapiKey,\n\t\t\tbaseUrl: String(cfg.openaiBaseUrl || \"https://api.openai.com/v1\").trim() || \"https://api.openai.com/v1\",\n\t\t\tmodel: String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\",\n\t\t\ttimeoutMs: Math.max(5000, Number(cfg.timeoutMs || 12_000)),\n\t\t});\n\t}\n\n\tasync function _run(kind, payload) {\n\t\tif (!_enabled()) return { ok: false, disabled: true };\n\t\tconst client = _getClient();\n\t\tif (!client) return { ok: false, disabled: true };\n\t\tconst body = _redactDeep(payload);\n\n\t\tconst key = (() => {\n\t\t\ttry {\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\");\n\t\t\t\treturn `${kind}:${mint}`;\n\t\t\t} catch {\n\t\t\t\treturn `${kind}:unknown`;\n\t\t\t}\n\t\t})();\n\t\tconst prev = cache.get(key);\n\t\tif (prev && (now() - prev.at) < CACHE_TTL_MS) {\n\t\t\ttry {\n\t\t\t\tconst cfg = _getConfig() || {};\n\t\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model} (cache-hit)`, \"info\");\n\t\t\t\tconst js = _fmtShortJson(body);\n\t\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t\t\tif (prev?.res?.ok) _log(`decision ${_fmtShortJson(prev.res.decision || {}, 1200)}`, \"info\");\n\t\t\t} catch {}\n\t\t\treturn prev.res;\n\t\t}\n\n\t\ttry {\n\t\t\tconst cfg = _getConfig() || {};\n\t\t\tconst model = String(cfg.openaiModel || \"gpt-4o-mini\").trim() || \"gpt-4o-mini\";\n\t\t\tconst mint = String(payload?.mint || payload?.targetMint || \"\").slice(0, 8);\n\t\t\t_log(`call kind=${String(kind)} mint=${mint} model=${model}`, \"info\");\n\t\t\tconst js = _fmtShortJson(body);\n\t\t\tif (js) _log(`payload ${js}`, \"info\");\n\t\t} catch {}\n\n\t\tconst state = _redactDeep(_getState());\n\t\tconst userMsg = {\n\t\t\tsource: \"fdv_auto_trader\",\n\t\t\tkind,\n\t\t\tstate,\n\t\t\tpayload: body,\n\t\t};\n\n\t\tlet text = \"\";\n\t\ttry {\n\t\t\ttext = await client.chatJson({\n\t\t\t\tsystem: GARY_SYSTEM_PROMPT,\n\t\t\t\tuser: JSON.stringify(userMsg),\n\t\t\t\ttemperature: 0.15,\n\t\t\t\tmaxTokens: Math.max(120, Number((_getConfig() || {}).maxTokens || 350)),\n\t\t\t});\n\t\t} catch (e) {\n\t\t\ttry { _log(`request failed: ${String(e?.message || e || \"\")}` , \"warn\"); } catch {}\n\t\t\tconst res = { ok: false, err: String(e?.message || e || \"\") };\n\t\t\tcache.set(key, { at: now(), res });\n\t\t\treturn res;\n\t\t}\n\n\t\tconst parsed = _safeJsonParse(text);\n\t\tconst validated = (kind === \"buy\") ? _validateBuyDecision(parsed) : _validateSellDecision(parsed);\n\t\tconst res = validated\n\t\t\t? { ok: true, decision: validated }\n\t\t\t: { ok: false, err: \"invalid_json\" };\n\t\ttry {\n\t\t\tif (res.ok) {\n\t\t\t\tconst d = res.decision || {};\n\t\t\t\t_log(`decision ${_fmtShortJson(d, 1200)}`, \"info\");\n\t\t\t} else {\n\t\t\t\tconst snippet = String(text || \"\").slice(0, 600);\n\t\t\t\t_log(`bad response err=${String(res.err || \"unknown\")} raw=${_fmtShortJson(_redactDeep(snippet), 800)}`, \"warn\");\n\t\t\t}\n\t\t} catch {}\n\t\tcache.set(key, { at: now(), res });\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tgetConfigFromRuntime() {\n\t\t\ttry {\n\t\t\t\t// Priority: explicit overrides on window -> localStorage.\n\t\t\t\tconst g = (typeof window !== \"undefined\") ? window : globalThis;\n\t\t\t\tconst o = g && g.__fdvAgentOverrides && typeof g.__fdvAgentOverrides === \"object\" ? g.__fdvAgentOverrides : null;\n\t\t\t\tconst lsEnabled = _readBoolLs(\"fdv_agent_enabled\", true);\n\t\t\t\treturn {\n\t\t\t\t\tenabled: o && (\"enabled\" in o) ? !!o.enabled : lsEnabled,\n\t\t\t\t\topenaiApiKey: (o && o.openaiApiKey) ? String(o.openaiApiKey) : _readLs(\"fdv_openai_key\", \"\"),\n\t\t\t\t\topenaiModel: (o && o.openaiModel) ? String(o.openaiModel) : _readLs(\"fdv_openai_model\", \"gpt-4o-mini\"),\n\t\t\t\t\topenaiBaseUrl: (o && o.openaiBaseUrl) ? String(o.openaiBaseUrl) : _readLs(\"fdv_openai_base_url\", \"https://api.openai.com/v1\"),\n\t\t\t\t\ttimeoutMs: _safeNum((o && o.timeoutMs) ? o.timeoutMs : _readLs(\"fdv_openai_timeout_ms\", \"12000\"), 12000),\n\t\t\t\t\tmaxTokens: _safeNum((o && o.maxTokens) ? o.maxTokens : _readLs(\"fdv_openai_max_tokens\", \"350\"), 350),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn { enabled: false, openaiApiKey: \"\" };\n\t\t\t}\n\t\t},\n\n\t\tasync decideBuy({ mint, proposedBuySolUi, proposedSlippageBps, signals } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tproposed: {\n\t\t\t\t\tbuySolUi: _safeNum(proposedBuySolUi, 0),\n\t\t\t\t\tslippageBps: Math.floor(_safeNum(proposedSlippageBps, 0)),\n\t\t\t\t},\n\t\t\t\tsignals: signals && typeof signals === \"object\" ? signals : {},\n\t\t\t};\n\t\t\treturn await _run(\"buy\", payload);\n\t\t},\n\n\t\tasync decideSell({ mint, pos, ctx } = {}) {\n\t\t\tconst payload = {\n\t\t\t\tmint: String(mint || \"\").trim(),\n\t\t\t\tpos: pos && typeof pos === \"object\" ? pos : {},\n\t\t\t\tctx: ctx && typeof ctx === \"object\" ? ctx : {},\n\t\t\t};\n\t\t\treturn await _run(\"sell\", payload);\n\t\t},\n\t};\n}\n"}}}} |