diff --git a/package-lock.json b/package-lock.json
index 012c834..f1313d2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,9 +13,9 @@
"@msgpack/msgpack": "^3.1.1",
"@tanstack/react-query": "^5.80.3",
"@tanstack/react-table": "^8.21.3",
- "@trustgraph/client": "^1.5.3",
+ "@trustgraph/client": "^1.7.1",
"@trustgraph/react-provider": "^1.4.0",
- "@trustgraph/react-state": "^1.5.4",
+ "@trustgraph/react-state": "^1.7.2",
"@types/dagre": "^0.7.53",
"compute-cosine-similarity": "^1.1.0",
"dagre": "^0.8.5",
@@ -2617,9 +2617,9 @@
}
},
"node_modules/@trustgraph/client": {
- "version": "1.5.3",
- "resolved": "https://registry.npmjs.org/@trustgraph/client/-/client-1.5.3.tgz",
- "integrity": "sha512-1gzsD5RakMgP8VzfqIVsrlmJsgaltNbMT6dMawJSJJ1vqujpgEARKHf/Qw3pEfpXZrRvmesRCKE2x9q6DrW7wg==",
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@trustgraph/client/-/client-1.7.2.tgz",
+ "integrity": "sha512-9Ps/hfukTDvuabF/lXQtOGvs4P5/g6yv6Bq1vU3bLOFN5rMQNJZa6kYAOburV4RE8yK25qLi+WC/En2H/qjKOg==",
"license": "Apache-2.0"
},
"node_modules/@trustgraph/react-provider": {
@@ -2633,9 +2633,9 @@
}
},
"node_modules/@trustgraph/react-state": {
- "version": "1.5.4",
- "resolved": "https://registry.npmjs.org/@trustgraph/react-state/-/react-state-1.5.4.tgz",
- "integrity": "sha512-2uPA+npf7RkLIRmsysDkfAmGhnM1OgKk8P9B11tNbJcqnkPlyKqEd2g03G4XjdQ8uG4dHPY0c1qpzol0TD5S0w==",
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@trustgraph/react-state/-/react-state-1.7.2.tgz",
+ "integrity": "sha512-cdHjnUVlRnQL1k9yLuOFl28WukN0oyK/kYdegrJRSMMd48/TGyRsKdaRKcfmqCS2glLJNvI06zZU/KvXhmccJQ==",
"license": "MIT",
"dependencies": {
"@trustgraph/react-provider": "^1.4.0",
@@ -3237,9 +3237,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
- "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4716,9 +4716,9 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
+ "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5103,9 +5103,9 @@
}
},
"node_modules/cosmiconfig/node_modules/yaml": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
- "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+ "version": "1.10.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
+ "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"license": "ISC",
"engines": {
"node": ">= 6"
@@ -5966,9 +5966,9 @@
}
},
"node_modules/flatted": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
- "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"dev": true,
"license": "ISC"
},
@@ -6680,15 +6680,15 @@
}
},
"node_modules/lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT"
},
"node_modules/lodash-es": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
- "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"license": "MIT"
},
"node_modules/lodash.merge": {
@@ -7318,9 +7318,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8892,9 +8892,9 @@
"license": "MIT"
},
"node_modules/vite": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
- "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
+ "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9281,9 +9281,9 @@
"license": "ISC"
},
"node_modules/yaml": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
- "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
+ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"dev": true,
"license": "ISC",
"optional": true,
diff --git a/package.json b/package.json
index f5ca4a4..98db779 100644
--- a/package.json
+++ b/package.json
@@ -20,9 +20,9 @@
"@msgpack/msgpack": "^3.1.1",
"@tanstack/react-query": "^5.80.3",
"@tanstack/react-table": "^8.21.3",
- "@trustgraph/client": "^1.5.3",
+ "@trustgraph/client": "^1.7.1",
"@trustgraph/react-provider": "^1.4.0",
- "@trustgraph/react-state": "^1.5.4",
+ "@trustgraph/react-state": "^1.7.2",
"@types/dagre": "^0.7.53",
"compute-cosine-similarity": "^1.1.0",
"dagre": "^0.8.5",
diff --git a/src/components/chat/ChatMessage.tsx b/src/components/chat/ChatMessage.tsx
index 9ab51e9..7a5cf8f 100644
--- a/src/components/chat/ChatMessage.tsx
+++ b/src/components/chat/ChatMessage.tsx
@@ -2,7 +2,7 @@ import { useState } from "react";
import { Box, Flex, Avatar, Badge, IconButton } from "@chakra-ui/react";
import { Brain, Eye, CheckCircle, ChevronDown, ChevronRight } from "lucide-react";
import Markdown from "react-markdown-it";
-import { useSettings, useExplainabilityStore } from "@trustgraph/react-state";
+import { useExplainabilityStore } from "@trustgraph/react-state";
import ExplainabilityPanel from "./ExplainabilityPanel";
// Smart truncation: first sentence if short, otherwise ~100 chars at word boundary
@@ -26,19 +26,14 @@ const ChatMessage = ({ message }) => {
const isUser = message.role === "human";
const messageType = message.type || "normal";
- // Settings for feature switches
- const { settings } = useSettings();
- const explainabilityEnabled = settings.featureSwitches.explainability;
-
// Check if session exists for this message
const sessionExists = useExplainabilityStore(
(state) => !!message.explainSessionId && !!state.sessions[message.explainSessionId]
);
- // Determine if explainability panel should be shown
+ // Show explainability panel if this message has a session with data
const showExplainability =
!isUser &&
- explainabilityEnabled &&
message.explainSessionId &&
sessionExists;
diff --git a/src/components/chat/ExplainabilityPanel.tsx b/src/components/chat/ExplainabilityPanel.tsx
index de0b8ba..6812808 100644
--- a/src/components/chat/ExplainabilityPanel.tsx
+++ b/src/components/chat/ExplainabilityPanel.tsx
@@ -8,6 +8,7 @@ import {
ExplorationSection,
FocusSection,
SourceDrawer,
+ AgentExplainTimeline,
} from "./explainability";
interface ExplainabilityPanelProps {
@@ -25,7 +26,9 @@ const ExplainabilityPanel = ({ sessionId }: ExplainabilityPanelProps) => {
if (!session) return null;
// Check if there's any meaningful data to show
+ const hasAgentSteps = session.agentSteps && session.agentSteps.length > 0;
const hasData =
+ hasAgentSteps ||
session.question?.query ||
session.exploration?.edgeCount !== undefined ||
(session.focus?.selectedEdges && session.focus.selectedEdges.length > 0);
@@ -67,17 +70,23 @@ const ExplainabilityPanel = ({ sessionId }: ExplainabilityPanelProps) => {
- How I found this answer
+ {hasAgentSteps ? "How the agent reasoned" : "How I found this answer"}
- {session.question && }
- {session.exploration && }
- {session.focus && (
-
+ {hasAgentSteps ? (
+
+ ) : (
+ <>
+ {session.question && }
+ {session.exploration && }
+ {session.focus && (
+
+ )}
+ >
)}
diff --git a/src/components/chat/explainability/AgentExplainTimeline.tsx b/src/components/chat/explainability/AgentExplainTimeline.tsx
new file mode 100644
index 0000000..bdaa8c5
--- /dev/null
+++ b/src/components/chat/explainability/AgentExplainTimeline.tsx
@@ -0,0 +1,122 @@
+import { Box, Text, Badge, VStack, Flex } from "@chakra-ui/react";
+import {
+ HelpCircle,
+ GitBranch,
+ Wrench,
+ MessageSquare,
+ CheckCircle,
+} from "lucide-react";
+import type {
+ StructuredExplainEvent,
+ AgentQuestionEvent,
+ DecompositionEvent,
+ AnalysisEvent,
+} from "@trustgraph/react-state";
+
+const STEP_CONFIG: Record<
+ string,
+ { color: string; icon: React.ReactNode; defaultLabel: string }
+> = {
+ "agent-question": {
+ color: "orange",
+ icon: ,
+ defaultLabel: "Question",
+ },
+ decomposition: {
+ color: "yellow",
+ icon: ,
+ defaultLabel: "Decomposition",
+ },
+ analysis: {
+ color: "purple",
+ icon: ,
+ defaultLabel: "Analysis",
+ },
+ reflection: {
+ color: "cyan",
+ icon: ,
+ defaultLabel: "Observation",
+ },
+ conclusion: {
+ color: "green",
+ icon: ,
+ defaultLabel: "Conclusion",
+ },
+};
+
+/** Render type-specific detail below the badge */
+const StepDetail = ({ step }: { step: StructuredExplainEvent }) => {
+ switch (step.type) {
+ case "agent-question": {
+ const q = (step as AgentQuestionEvent).query;
+ return q ? (
+ "{q}"
+ ) : null;
+ }
+ case "decomposition": {
+ const goals = (step as DecompositionEvent).goals;
+ return goals.length > 0 ? (
+
+ {goals.map((g, i) => (
+ - {g}
+ ))}
+
+ ) : null;
+ }
+ case "analysis": {
+ const a = step as AnalysisEvent;
+ return a.action ? (
+
+ {a.action}
+ {a.arguments && (
+
+ {a.arguments.length > 80
+ ? a.arguments.substring(0, 80) + "..."
+ : a.arguments}
+
+ )}
+
+ ) : null;
+ }
+ default:
+ return null;
+ }
+};
+
+interface AgentExplainTimelineProps {
+ steps: StructuredExplainEvent[];
+}
+
+const AgentExplainTimeline = ({ steps }: AgentExplainTimelineProps) => {
+ if (steps.length === 0) return null;
+
+ return (
+
+ {steps.map((step, i) => {
+ const config = STEP_CONFIG[step.type] || STEP_CONFIG.reflection;
+ const label =
+ ("label" in step && step.label) || config.defaultLabel;
+
+ return (
+
+
+
+ {config.icon}
+ {label}
+
+
+
+
+
+
+ );
+ })}
+
+ );
+};
+
+export default AgentExplainTimeline;
diff --git a/src/components/chat/explainability/index.ts b/src/components/chat/explainability/index.ts
index 2ff967c..e60a5d4 100644
--- a/src/components/chat/explainability/index.ts
+++ b/src/components/chat/explainability/index.ts
@@ -3,3 +3,4 @@ export { default as ExplorationSection } from "./ExplorationSection";
export { default as FocusSection } from "./FocusSection";
export { default as SourceLink } from "./SourceLink";
export { default as SourceDrawer } from "./SourceDrawer";
+export { default as AgentExplainTimeline } from "./AgentExplainTimeline";