diff --git a/ui/src/components/ChatInterface.tsx b/ui/src/components/ChatInterface.tsx
index 9307f614b54dc38bb088b6e024fa6abed29a1788..6ee249b7532befb764d1fd875128d4c8a8cf5c28 100644
--- a/ui/src/components/ChatInterface.tsx
+++ b/ui/src/components/ChatInterface.tsx
@@ -29,6 +29,7 @@ import DirectoryPickerModal from "./DirectoryPickerModal";
import { useVersionChecker } from "./VersionChecker";
import TerminalWidget from "./TerminalWidget";
import ModelPicker from "./ModelPicker";
+import SystemPromptView from "./SystemPromptView";
// Ephemeral terminal instance (not persisted to database)
interface EphemeralTerminal {
@@ -1251,8 +1252,15 @@ function ChatInterface({
return null;
});
+ // Find system message to render at the top
+ const systemMessage = messages.find((m) => m.type === "system");
+
// Append ephemeral terminals at the end
- return [...rendered, ...terminalElements];
+ return [
+ systemMessage && ,
+ ...rendered,
+ ...terminalElements,
+ ];
};
return (
diff --git a/ui/src/components/SystemPromptView.tsx b/ui/src/components/SystemPromptView.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7a231d4c5c8ef28c5c1ca3755d642f6b8b232d3b
--- /dev/null
+++ b/ui/src/components/SystemPromptView.tsx
@@ -0,0 +1,83 @@
+import React, { useState } from "react";
+import { Message, LLMContent } from "../types";
+
+interface SystemPromptViewProps {
+ message: Message;
+}
+
+function SystemPromptView({ message }: SystemPromptViewProps) {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ // Extract system prompt text from llm_data
+ let systemPromptText = "";
+ if (message.llm_data) {
+ try {
+ const llmData =
+ typeof message.llm_data === "string" ? JSON.parse(message.llm_data) : message.llm_data;
+ if (llmData && llmData.Content && Array.isArray(llmData.Content)) {
+ const textContent = llmData.Content.find((c: LLMContent) => c.Type === 2 && c.Text);
+ if (textContent) {
+ systemPromptText = textContent.Text;
+ }
+ }
+ } catch (err) {
+ console.error("Failed to parse system prompt:", err);
+ }
+ }
+
+ if (!systemPromptText) {
+ return null;
+ }
+
+ // Count lines and approximate size
+ const lineCount = systemPromptText.split("\n").length;
+ const charCount = systemPromptText.length;
+ const sizeKb = (charCount / 1024).toFixed(1);
+
+ return (
+
+
setIsExpanded(!isExpanded)}>
+
+ 📋
+ System Prompt
+
+ {lineCount} lines, {sizeKb} KB
+
+
+
+
+
+ {isExpanded && (
+
+ )}
+
+ );
+}
+
+export default SystemPromptView;
diff --git a/ui/src/styles.css b/ui/src/styles.css
index fbaba0c0c3f618806242685cb3c8775de84bd16c..9a2c2dd0fe1988f37f9df2778e92e5a589ab26a2 100644
--- a/ui/src/styles.css
+++ b/ui/src/styles.css
@@ -4801,3 +4801,84 @@ svg {
margin-top: 1rem;
padding-bottom: 0.5rem;
}
+
+/* System Prompt View */
+.system-prompt-view {
+ background: var(--gray-100);
+ border-radius: 0.5rem;
+ margin: 0.5rem 0;
+ width: 100%;
+ border: 1px dashed var(--border);
+}
+
+.dark .system-prompt-view {
+ background: var(--gray-800);
+}
+
+.system-prompt-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.75rem 1rem;
+ cursor: pointer;
+ user-select: none;
+}
+
+.system-prompt-header:hover {
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 0.5rem;
+}
+
+.dark .system-prompt-header:hover {
+ background: rgba(255, 255, 255, 0.02);
+}
+
+.system-prompt-summary {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ flex: 1;
+ min-width: 0;
+}
+
+.system-prompt-icon {
+ font-size: 1rem;
+ flex-shrink: 0;
+}
+
+.system-prompt-label {
+ font-family: var(--font-mono);
+ font-size: 0.875rem;
+ color: var(--text-primary);
+ font-weight: 500;
+}
+
+.system-prompt-meta {
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ flex-shrink: 0;
+}
+
+.system-prompt-content {
+ padding: 0 1rem 1rem 1rem;
+}
+
+.system-prompt-text {
+ font-family: var(--font-mono);
+ font-size: 0.75rem;
+ line-height: 1.5;
+ color: var(--text-secondary);
+ background: var(--bg-base);
+ border: 1px solid var(--border);
+ border-radius: 0.375rem;
+ padding: 1rem;
+ margin: 0;
+ max-height: 400px;
+ overflow: auto;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+.dark .system-prompt-text {
+ background: var(--gray-900);
+}