diff --git a/src/components/widgets.tsx b/src/components/widgets.tsx
index 325eece..651f292 100644
--- a/src/components/widgets.tsx
+++ b/src/components/widgets.tsx
@@ -6,7 +6,12 @@ import { KIND_LABEL, RV, STATE_TONE } from '../api/config';
import type { Tone } from '../api/config';
import type { AiDecision, AuditEntry, Row, SimRow } from '../api/types';
import { Badge, Bar, Card, Empty, ErrorNote, Spinner } from './core';
-import { date, dateTime, inr, mt, num, pct } from '../format';
+import { date, dateTime, inr, mdParts, mt, num, pct } from '../format';
+
+/** AI prose with its **bold** honoured. */
+export function Prose({ text, className = '' }: { text: unknown; className?: string }) {
+ return
{mdParts(text).map((x, i) => (x.bold ? {x.text} : {x.text}))}
;
+}
export function StateBadge({ state }: { state: unknown }) {
const s = String(state ?? '');
@@ -75,7 +80,7 @@ export function RecCard({ rec, compact = false }: { rec: Row; compact?: boolean
{String(rec.headline ?? '')}
{inc !== 0 && 0 ? 'text-accent' : 'text-bad'}`}>{inr(inc, { sign: true })}
}
- {!compact && rec.rationale_md ? {String(rec.rationale_md)}
: null}
+ {!compact && rec.rationale_md ? : null}
{!compact && (
{rec.delta_mt ? Add {mt(rec.delta_mt)} : null}
@@ -175,7 +180,9 @@ export function SimTable({ rows, hasHypo, loading, error }: { rows: SimRow[] | n
export function InstanceTrace({ instanceId }: { instanceId: number | string }) {
const { client } = useZino();
const audit = useQuery(() => client.audit(instanceId), [instanceId], true, 15000);
- const ai = useQuery(() => client.aiDecisions(instanceId).catch(() => [] as AiDecision[]), [instanceId], true, 15000);
+ // The AI's rationale is on the audit row (ai_reasoning) and in the fields it submitted;
+ // /monitor/* is Studio-scoped on this build (403 for app users), so it is not called.
+ const ai = { data: [] as AiDecision[] };
return (
{audit.loading && !audit.data && }
@@ -193,8 +200,8 @@ export function InstanceTrace({ instanceId }: { instanceId: number | string }) {
{a.fields && a.fields.length > 0 && (
- {a.fields.filter((f) => f.value !== '' && f.value != null).slice(0, 12).map((f) => (
- - {f.label}:
- {String(f.value).slice(0, 300)}
+ {a.fields.filter((f) => f.value !== '' && f.value != null && !f.field_id.startsWith('_') && !f.label.startsWith('_')).slice(0, 14).map((f) => (
+ 160 ? 'sm:col-span-2' : ''}`}>
- {f.label}:
- {String(f.value).length > 700 ? String(f.value).slice(0, 700) + '…' : String(f.value)}
))}
)}
diff --git a/src/format.ts b/src/format.ts
index 8be054b..e1e2148 100644
--- a/src/format.ts
+++ b/src/format.ts
@@ -43,3 +43,18 @@ export function parseJson(v: unknown, fallback: T): T {
if (typeof v === 'object') return v as T;
try { return JSON.parse(String(v)) as T; } catch { return fallback; }
}
+
+/** The AI writes **bold** in its prose; render just that, as React nodes. */
+export function mdParts(text: unknown): Array<{ bold: boolean; text: string }> {
+ const t = String(text ?? '');
+ const out: Array<{ bold: boolean; text: string }> = [];
+ const re = /\*\*([^*]+)\*\*/g;
+ let last = 0; let m: RegExpExecArray | null;
+ while ((m = re.exec(t))) {
+ if (m.index > last) out.push({ bold: false, text: t.slice(last, m.index) });
+ out.push({ bold: true, text: m[1] });
+ last = m.index + m[0].length;
+ }
+ if (last < t.length) out.push({ bold: false, text: t.slice(last) });
+ return out;
+}
diff --git a/src/screens/dealer/Orders.tsx b/src/screens/dealer/Orders.tsx
index 752a8b8..3561ca9 100644
--- a/src/screens/dealer/Orders.tsx
+++ b/src/screens/dealer/Orders.tsx
@@ -6,7 +6,7 @@ import { ACT, RV, WF } from '../../api/config';
import type { Row } from '../../api/types';
import { ActivityForm } from '../../components/ActivityForm';
import { Button, Card, Empty, ErrorNote, Facts, Spinner } from '../../components/core';
-import { DataTable, InstanceTrace, KindBadge, RecCard, StateBadge } from '../../components/widgets';
+import { DataTable, InstanceTrace, KindBadge, Prose, RecCard, StateBadge } from '../../components/widgets';
import { date, inr, mt, num, pct } from '../../format';
function useOrders(customerNo: string | null | undefined, refetchMs?: number) {
@@ -80,10 +80,10 @@ export function OrderDetail() {
{o.suggested_scheme ? {String(o.suggested_scheme)} : null}{inc !== 0 && 0 ? 'text-accent' : 'text-bad'}`}>{inr(inc, { sign: true })}}
{String(o.advice_headline ?? '')}
-
{String(o.advice_rationale ?? '')}
+
-
Keyed
{mt(o.qty_mt)} · {inr(o.payout_now)}
-
Suggested
{suggested != null ? mt(suggested) : '—'}{o.suggested_pp_mt ? ` (${mt(o.suggested_pp_mt)} premium)` : ''} · {inr(o.payout_if)}
+
Keyed
{mt(o.qty_mt)}{num(o.payout_now) > 0 ? ` · ${inr(o.payout_now)}` : ''}
+
Suggested
{suggested != null ? mt(suggested) : '—'}{o.suggested_pp_mt ? ` (${mt(o.suggested_pp_mt)} premium)` : ''}{num(o.payout_if) > 0 ? ` · ${inr(o.payout_if)}` : ''}
Ship to
{String(o.suggested_ship_to || o.ship_to_code || '—')}
Act by
{date(o.advice_deadline)}
diff --git a/src/screens/supplier/Advisories.tsx b/src/screens/supplier/Advisories.tsx
index 0675342..7f65f98 100644
--- a/src/screens/supplier/Advisories.tsx
+++ b/src/screens/supplier/Advisories.tsx
@@ -5,7 +5,7 @@ import { useQuery, useZino } from '../../api/provider';
import { ACT, RV, WF } from '../../api/config';
import { ActivityForm } from '../../components/ActivityForm';
import { Button, Card, Empty, ErrorNote, Facts, Kpi, Spinner } from '../../components/core';
-import { DataTable, InstanceTrace, KindBadge, RecCard, StateBadge } from '../../components/widgets';
+import { DataTable, InstanceTrace, KindBadge, Prose, RecCard, StateBadge } from '../../components/widgets';
import { date, inr, mt, num, pct } from '../../format';
import { useScope } from './scope';
@@ -68,7 +68,7 @@ export function AdvisoryDetail() {
{isSweep ? (
<>
-
{String(a.commentary ?? '') || 'Waiting…'}
+
{a.commentary ?
: 'Waiting…'}
{String(a.nudge_list ?? '')}
>
) : (
@@ -78,7 +78,7 @@ export function AdvisoryDetail() {
{a.suggested_scheme ? {String(a.suggested_scheme)} : null}{a.incremental_payout != null && {inr(a.incremental_payout, { sign: true })}}
{String(a.advice_headline ?? '')}
-
{String(a.advice_rationale ?? '')}
+
{a.suggested_qty_mt ? Add {mt(a.suggested_qty_mt)} : null}{a.suggested_pp_mt ? Premium {mt(a.suggested_pp_mt)} : null}{a.advice_deadline ? By {date(a.advice_deadline)} : null}{a.advice_confidence ? Confidence {pct(num(a.advice_confidence) * 100, 0)} : null}
)}