Repository navigation
Make rToken findings explicit and restore Overview publishing #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ import { createClient } from "@/lib/supabase/server"; | |
| import { aiJsonWithFallback, configuredAiProviders } from "@/lib/ai-fallback"; | ||
| import { getBitgetAsset, getBitgetCandles, getBitgetStockPerp, STOCK_PERP_UNIVERSE } from "@/lib/bitget-market"; | ||
| import { buildSignals, prepareBacktestCandles } from "@/lib/backtest"; | ||
| import { extractFlowTickers, gateFlowTrade, normalizeFlowAssessments, sourceOnlyAssessment, type FlowAssessment, type FlowSource } from "@/lib/agent-flow-policy"; | ||
| import { extractFlowTickers, gateFlowTrade, normalizeFlowAssessments, sourceOnlyAssessment, tokenizedMarketReview, type FlowAssessment, type FlowSource } from "@/lib/agent-flow-policy"; | ||
| import { gatherIssuerEvidence, type FlowEvidence } from "@/lib/agent-flow-sources"; | ||
| import { agentKeyIssuerEvidence } from "@/lib/agentkey-flow-sources"; | ||
|
|
||
|
|
@@ -163,7 +163,7 @@ export async function POST(request: Request) { | |
| ] : []; | ||
| stage("Alpha Factory", signals.length ? "complete" : "blocked", `${snap.ticker}: completed-candle rule`, signals.length ? `SMA 20/50 checked on ${completed.length} completed 4H bars; ${signals.at(-1) !== signals.at(-2) ? "fresh transition" : "no fresh transition"}.` : "Too few completed candles to test the fixed rule.", snap.ticker); | ||
| stage("Trading agent", gate.tradeable ? "complete" : "blocked", `${snap.ticker}: ${gate.tradeable ? "eligible for order review" : "research only"}`, gate.tradeable ? "Source coverage, market freshness, confidence, and the fixed Alpha rule align. You must still choose and confirm any action." : gateReasons.join(" "), snap.ticker); | ||
| return { ticker: snap.ticker, issuer: snap.issuer, summary: assessment.summary, stance: assessment.stance, confidence: assessment.confidence, confidenceCapped: Boolean(assessment.confidenceCapped), | ||
| return { ticker: snap.ticker, issuer: snap.issuer, summary: assessment.summary, stance: assessment.stance, confidence: assessment.confidence, confidenceCapped: Boolean(assessment.confidenceCapped), ...(tokenizedMarketQuestion ? { tokenizedMarketReview: tokenizedMarketReview({ ticker: snap.ticker, spotSymbol: snap.spot?.symbol ?? null, spotPrice: snap.spot?.lastPrice ?? null, spotTimestamp: snap.spot?.providerTimestamp ?? null, now }) } : {}), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For ordinary stock questions containing a short word beginning with Useful? React with 👍 / 👎. |
||
| tradeable: gate.tradeable, gateReasons, sources: snap.sources.slice(0, 20).map(({ title, url, publisher, publishedAt, via }): FlowSource => ({ title, url, publisher, publishedAt, via })), | ||
| market: { perpSymbol: snap.perp?.symbol ?? null, spotSymbol: snap.spot?.symbol ?? null, perpLastPrice: snap.perp?.lastPrice ?? null, perpProviderTimestamp: snap.perp?.providerTimestamp ?? null, spotLastPrice: snap.spot?.lastPrice ?? null, spotProviderTimestamp: snap.spot?.providerTimestamp ?? null, lastPrice: snap.perp?.lastPrice ?? null, providerTimestamp: snap.perp?.providerTimestamp ?? null, interval: "4H", candles: completed.slice(-30).map(candle => ({ timestamp: candle.timestamp, close: candle.close })) }, | ||
| handoff: gate.tradeable ? { destinations, reason: "Eligible for a separate, user-confirmed order review. Paper futures and live rToken spot use different instruments and accounts." } : null, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ type StageEvent = { id: string; agent: string; status: "running" | "complete" | | |
| type Destination = { mode: "paper_futures" | "live_spot"; label: string; href: string }; | ||
| type AssetRead = { | ||
| ticker: string; issuer: string; summary: string; stance: "bullish" | "bearish" | "mixed" | "unclear"; | ||
| confidence: number; confidenceCapped?: boolean; tradeable: boolean; gateReasons: string[]; | ||
| confidence: number; confidenceCapped?: boolean; tradeable: boolean; gateReasons: string[]; tokenizedMarketReview?: { verdict: string; quoteStatus: "unavailable" | "unverified" | "fresh" | "stale"; limitations: string[] }; | ||
| evidenceBalance?: { supporting: Array<{ text: string; citedUrls: string[] }>; counter: Array<{ text: string; citedUrls: string[] }> }; | ||
| sources: Array<{ title: string; url: string; publisher: string; publishedAt: string | null; via?: "Public feeds" | "AgentKey" }>; | ||
| market: { perpSymbol: string | null; spotSymbol: string | null; lastPrice: number | null; providerTimestamp?: number | null; perpLastPrice?: number | null; perpProviderTimestamp?: number | null; spotLastPrice?: number | null; spotProviderTimestamp?: number | null }; | ||
|
|
@@ -176,7 +176,7 @@ export default function AgentFlowDesk({ initialQuestion, evaluatorMode = false } | |
| {error ? <div className="flow-error" role="alert"><b>Research paused</b><span>{error}</span>{error.toLowerCase().includes("sign in") ? <Link href="/login">Sign in ↗</Link> : <button type="button" onClick={() => void runFlow()} disabled={busy}>Try again ↗</button>}</div> : null} | ||
|
|
||
| {result ? <section className="flow-results" aria-label="Research findings"><div className="flow-section-head"><div><span className="flow-kicker">RESEARCH / {result.assets.length} {result.assets.length === 1 ? "ISSUER" : "ISSUERS"}</span><h2>One question. Separate decisions.</h2><p>Issuer coverage and token-market conditions are shown separately. Review the source links and quote timestamp before considering any next step.</p></div></div> | ||
| <div className="flow-asset-grid">{result.assets.map((asset) => <article key={asset.ticker} className="flow-asset-card"><header><div><span className="flow-kicker">{asset.ticker} / {asset.issuer}</span><h3>{asset.issuer}</h3></div><span className={`flow-verdict ${asset.tradeable ? "flow-verdict-ready" : ""}`}>{asset.tradeable ? "REVIEW AVAILABLE" : "NO TRADE HANDOFF"}</span></header><p>{asset.summary}</p><div className="flow-asset-meta"><span>Read <b>{asset.stance}</b></span><span>Model confidence <b>{Math.round(asset.confidence * 100)}%</b></span></div><section className="flow-market-references" aria-label={`${asset.ticker} instrument checks`}><b>Instrument checks</b><span>Tokenized spot · {asset.market.spotSymbol ?? `R${asset.ticker}USDT`} · {asset.market.spotLastPrice != null ? `$${asset.market.spotLastPrice.toLocaleString()}` : "No verified quote"}{asset.market.spotProviderTimestamp ? ` · ${new Date(asset.market.spotProviderTimestamp).toLocaleString()}` : " · freshness unverified"}</span><span>Stock perpetual · {asset.market.perpSymbol ?? `${asset.ticker}USDT`} · {asset.market.perpLastPrice != null ? `$${asset.market.perpLastPrice.toLocaleString()}` : "No verified quote"}{asset.market.perpProviderTimestamp ? ` · ${new Date(asset.market.perpProviderTimestamp).toLocaleString()}` : " · freshness unverified"} · derivative reference</span></section><p className="flow-confidence-note" role="note">{asset.confidenceCapped ? "Evidence is limited: the score is capped until two recent, distinct publishers support the assessment. " : ""}Model-reported score, not a calibrated probability.</p><QuoteFreshness market={asset.market} now={quoteNow} />{asset.evidenceBalance && (asset.evidenceBalance.supporting.length > 0 || asset.evidenceBalance.counter.length > 0) ? <section className="flow-evidence-balance" aria-label={`${asset.ticker} evidence balance`}><header><b>Evidence balance</b><span>Source-linked points only</span></header><div className="flow-evidence-columns">{([["Supports this reading", asset.evidenceBalance.supporting], ["Challenges this reading", asset.evidenceBalance.counter]] as const).map(([label, points]) => <section className="flow-evidence-side" key={label}><h4>{label}</h4>{points.length ? <ul>{points.map((point, index) => <li key={index}><p>{point.text}</p><div>{point.citedUrls.map((url) => { const source = asset.sources.find((item) => item.url === url); return source ? <a key={url} href={url} target="_blank" rel="noopener noreferrer">{source.publisher} ↗</a> : null; })}</div></li>)}</ul> : <p className="flow-evidence-empty">No source-linked points were established for this side.</p>}</section>)}</div></section> : null}{asset.gateReasons?.length ? <div className="flow-gates"><b>Decision gates</b><ul>{asset.gateReasons.map((reason, index) => <li key={index}>{reason}</li>)}</ul></div> : null}<details className="flow-sources"><summary>Inspect {asset.sources.length} {asset.sources.length === 1 ? "source" : "sources"}</summary>{asset.sources.length ? <ul>{asset.sources.map((source, index) => <li key={`${source.url}-${index}`}><span>{source.publisher}{source.publishedAt ? ` · ${new Date(source.publishedAt).toLocaleDateString()}` : ""}{source.via ? <em className="flow-source-via"> · {source.via}</em> : null}</span><a href={source.url} target="_blank" rel="noopener noreferrer">{source.title} ↗</a></li>)}</ul> : <p>No source links were returned; the trading gate remains closed.</p>}</details></article>)}</div> | ||
| <div className="flow-asset-grid">{result.assets.map((asset) => <article key={asset.ticker} className="flow-asset-card"><header><div><span className="flow-kicker">{asset.ticker} / {asset.issuer}</span><h3>{asset.issuer}</h3></div><span className={`flow-verdict ${asset.tradeable ? "flow-verdict-ready" : ""}`}>{asset.tradeable ? "REVIEW AVAILABLE" : "NO TRADE HANDOFF"}</span></header><p>{asset.summary}</p>{asset.tokenizedMarketReview ? <section className="flow-token-verdict" aria-label="Tokenized market finding"><span className="flow-kicker">R TOKEN / MARKET FINDING</span><b>{asset.tokenizedMarketReview.verdict}</b><span className={`flow-token-quote flow-token-quote-${asset.tokenizedMarketReview.quoteStatus}`}>Token quote freshness: {asset.tokenizedMarketReview.quoteStatus}</span><ul>{asset.tokenizedMarketReview.limitations.map((limitation) => <li key={limitation}>{limitation}</li>)}</ul></section> : null}<div className="flow-asset-meta"><span>Read <b>{asset.stance}</b></span><span>Model confidence <b>{Math.round(asset.confidence * 100)}%</b></span></div><section className="flow-market-references" aria-label={`${asset.ticker} instrument checks`}><b>Instrument checks</b><span>Tokenized spot · {asset.market.spotSymbol ?? `R${asset.ticker}USDT`} · {asset.market.spotLastPrice != null ? `$${asset.market.spotLastPrice.toLocaleString()}` : "No verified quote"}{asset.market.spotProviderTimestamp ? ` · ${new Date(asset.market.spotProviderTimestamp).toLocaleString()}` : " · freshness unverified"}</span><span>Stock perpetual · {asset.market.perpSymbol ?? `${asset.ticker}USDT`} · {asset.market.perpLastPrice != null ? `$${asset.market.perpLastPrice.toLocaleString()}` : "No verified quote"}{asset.market.perpProviderTimestamp ? ` · ${new Date(asset.market.perpProviderTimestamp).toLocaleString()}` : " · freshness unverified"} · derivative reference</span></section><p className="flow-confidence-note" role="note">{asset.confidenceCapped ? "Evidence is limited: the score is capped until two recent, distinct publishers support the assessment. " : ""}Model-reported score, not a calibrated probability.</p><QuoteFreshness market={asset.market} now={quoteNow} />{asset.evidenceBalance && (asset.evidenceBalance.supporting.length > 0 || asset.evidenceBalance.counter.length > 0) ? <section className="flow-evidence-balance" aria-label={`${asset.ticker} evidence balance`}><header><b>Evidence balance</b><span>Source-linked points only</span></header><div className="flow-evidence-columns">{([["Supports this reading", asset.evidenceBalance.supporting], ["Challenges this reading", asset.evidenceBalance.counter]] as const).map(([label, points]) => <section className="flow-evidence-side" key={label}><h4>{label}</h4>{points.length ? <ul>{points.map((point, index) => <li key={index}><p>{point.text}</p><div>{point.citedUrls.map((url) => { const source = asset.sources.find((item) => item.url === url); return source ? <a key={url} href={url} target="_blank" rel="noopener noreferrer">{source.publisher} ↗</a> : null; })}</div></li>)}</ul> : <p className="flow-evidence-empty">No source-linked points were established for this side.</p>}</section>)}</div></section> : null}{asset.gateReasons?.length ? <div className="flow-gates"><b>Decision gates</b><ul>{asset.gateReasons.map((reason, index) => <li key={index}>{reason}</li>)}</ul></div> : null}<details className="flow-sources"><summary>Inspect {asset.sources.length} {asset.sources.length === 1 ? "source" : "sources"}</summary>{asset.sources.length ? <ul>{asset.sources.map((source, index) => <li key={`${source.url}-${index}`}><span>{source.publisher}{source.publishedAt ? ` · ${new Date(source.publishedAt).toLocaleDateString()}` : ""}{source.via ? <em className="flow-source-via"> · {source.via}</em> : null}</span><a href={source.url} target="_blank" rel="noopener noreferrer">{source.title} ↗</a></li>)}</ul> : <p>No source links were returned; the trading gate remains closed.</p>}</details></article>)}</div> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a tokenized result remains open for more than two minutes, this badge continues to render the server-computed Useful? React with 👍 / 👎. |
||
| {tradeableAssets.length ? <div className="flow-handoff" ref={handoffRef} tabIndex={-1} role="region" aria-label="Choose a trading review desk"><div className="flow-handoff-intro"><span className="flow-kicker">NEXT / YOUR DECISION</span><h2>Review the market before acting.</h2><p>The research gates found a scenario worth inspecting. Choose paper futures, or review a Bitget tokenized stock spot order in demo or live account mode. You can review both desks. Every order still requires your own approval and risk checks.</p></div><div className="flow-handoff-list">{tradeableAssets.map((asset) => <div className="flow-handoff-asset" key={asset.ticker}><div><b>{asset.ticker}</b><span>{asset.handoff?.reason}</span></div><div className="flow-handoff-actions">{asset.handoff?.destinations.flatMap((destination) => { const href = safeDeskLink(destination); if (!href) return []; if (destination.mode === "paper_futures") return [<Link key="paper" href={href} onClick={() => setMode("pro")}>Review paper futures <span>↗</span></Link>]; const demo = `${href}${href.includes("?") ? "&" : "?"}intent=demo`; const live = `${href}${href.includes("?") ? "&" : "?"}intent=live`; return [<Link key="demo" href={demo} onClick={() => setMode("pro")}>Review Bitget demo spot <span>↗</span></Link>, <Link key="live" href={live} onClick={() => setMode("pro")}>Review Bitget live spot <span>↗</span></Link>]; })}</div></div>)}</div><small>Research can be uncertain. A review link never places a trade or switches your account into live mode.{mode === "mini" ? " Trade reviews open the Pro workspace controls." : ""}</small></div> : <div className="flow-no-handoff"><span className="flow-kicker">RISK GATE / HOLD</span><b>No trading handoff for this read.</b><p>Review the sources and decision gates above. You can return when fresher or stronger evidence is available.</p><Link href="/markets">Explore the market map ↗</Link></div>} | ||
| </section> : null} | ||
| </div>; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When research is launched through the Overview or Tide Guide flows,
IntelligenceProviderpasses this payload throughparseIntelligenceResult, whoseIntelligenceAssetconstruction dropstokenizedMarketReviewand the token-specific market fields; the registry UI consequently never receives or renders the explicit finding. Update that validated result path and its renderer as well, otherwise the new result appears only on the standalone/flowpage despite these consumers using the same route.Useful? React with 👍 / 👎.