diff --git a/src/routes/v2/pages/RunView/RunViewV2.tsx b/src/routes/v2/pages/RunView/RunViewV2.tsx
index d642cd2f8..cbb8ae739 100644
--- a/src/routes/v2/pages/RunView/RunViewV2.tsx
+++ b/src/routes/v2/pages/RunView/RunViewV2.tsx
@@ -9,6 +9,7 @@ import { useEffect, useRef } from "react";
import { InfoBox } from "@/components/shared/InfoBox";
import { LoadingScreen } from "@/components/shared/LoadingScreen";
import { RemoteAuthErrorView } from "@/components/shared/RemoteAuthErrorView";
+import { useFlagValue } from "@/components/shared/Settings/useFlags";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Paragraph } from "@/components/ui/typography";
import type { ComponentSpec } from "@/models/componentSpec";
@@ -23,6 +24,7 @@ import {
ExecutionDataProvider,
useExecutionData,
} from "@/providers/ExecutionDataProvider";
+import { AiChatStoreProvider } from "@/routes/v2/shared/components/AiChat/AiChatStoreContext";
import { useDockAreaAccordion } from "@/routes/v2/shared/hooks/useDockAreaAccordion";
import { NodeRegistryProvider } from "@/routes/v2/shared/nodes/NodeRegistryContext";
import { SpecProvider } from "@/routes/v2/shared/providers/SpecContext";
@@ -40,6 +42,7 @@ import { RemoteAuthError } from "@/utils/fetchWithErrorHandling";
import { RunViewFlowCanvas } from "./components/RunViewFlowCanvas";
import { RunViewMenuBar } from "./components/RunViewMenuBar/RunViewMenuBar";
+import { useAiChatWindow } from "./hooks/useAiChatWindow";
import { useFocusTaskFromUrl } from "./hooks/useFocusTaskFromUrl";
import { useRunViewSelectionSync } from "./hooks/useRunViewSelectionSync";
import { useRunViewSpecLifecycle } from "./hooks/useRunViewSpecLifecycle";
@@ -152,6 +155,9 @@ const RunViewLayout = observer(function RunViewLayout({
useRunViewSelectionSync();
useFocusTaskFromUrl(spec);
+ const aiEnabled = useFlagValue("ai-assistant");
+ useAiChatWindow(aiEnabled);
+
const { navigation } = useSharedStores();
const activeSpec = navigation.activeSpec;
@@ -199,16 +205,18 @@ export function RunViewV2() {
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
diff --git a/src/routes/v2/pages/RunView/hooks/useAiChatWindow.tsx b/src/routes/v2/pages/RunView/hooks/useAiChatWindow.tsx
new file mode 100644
index 000000000..1e305e034
--- /dev/null
+++ b/src/routes/v2/pages/RunView/hooks/useAiChatWindow.tsx
@@ -0,0 +1,32 @@
+import { useEffect } from "react";
+
+import { createRunViewToolBridge } from "@/routes/v2/pages/RunView/toolBridge/runViewToolBridge";
+import { AiChatContent } from "@/routes/v2/shared/components/AiChat/AiChatContent";
+import { useSharedStores } from "@/routes/v2/shared/store/SharedStoreContext";
+
+const AI_CHAT_WINDOW_ID = "run-ai-assistant-chat";
+
+export function useAiChatWindow(enabled: boolean) {
+ const { windows } = useSharedStores();
+
+ useEffect(() => {
+ if (!enabled) {
+ windows.closeWindow(AI_CHAT_WINDOW_ID);
+ return;
+ }
+ if (windows.getWindowById(AI_CHAT_WINDOW_ID)) return;
+
+ windows.openWindow(
+ ,
+ {
+ id: AI_CHAT_WINDOW_ID,
+ title: "AI Assistant",
+ position: { x: 100, y: 80 },
+ size: { width: 380, height: 520 },
+ disabledActions: ["close"],
+ startVisible: true,
+ persisted: true,
+ },
+ );
+ }, [enabled, windows]);
+}
diff --git a/src/routes/v2/pages/RunView/toolBridge/runViewToolBridge.ts b/src/routes/v2/pages/RunView/toolBridge/runViewToolBridge.ts
new file mode 100644
index 000000000..1ab552033
--- /dev/null
+++ b/src/routes/v2/pages/RunView/toolBridge/runViewToolBridge.ts
@@ -0,0 +1,124 @@
+/**
+ * Read-only tool bridge for the RunView AI assistant.
+ *
+ * RunView inspects a completed pipeline run, so its bridge composes the
+ * shared run-lifecycle and debug handlers but exposes a read-only CSOM
+ * slice: `getPipelineState` / `validatePipeline` work against the live
+ * spec, while every spec-mutating method short-circuits with an error.
+ * This satisfies the full `ToolBridgeApi` contract without depending on
+ * the Editor's spec-mutation actions or an undo store.
+ */
+import type { ToolBridgeApi, ValidationResult } from "@/agent/toolBridgeApi";
+import { validateSpec } from "@/models/componentSpec/validation/validateSpec";
+import { serializeSpecForAi } from "@/routes/v2/shared/components/AiChat/serializeSpecForAi";
+import { createDebugBridgeHandlers } from "@/routes/v2/shared/components/AiChat/toolBridge/debugBridge";
+import { createRunBridgeHandlers } from "@/routes/v2/shared/components/AiChat/toolBridge/runBridge";
+import type { BridgeDeps } from "@/routes/v2/shared/components/AiChat/toolBridge/utils";
+import { requireSpec } from "@/routes/v2/shared/components/AiChat/toolBridge/utils";
+
+const READ_ONLY_ERROR =
+ "This is a read-only run view — the pipeline spec cannot be edited here.";
+
+type ReadOnlyCsomHandlers = Pick<
+ ToolBridgeApi,
+ | "getPipelineState"
+ | "setPipelineName"
+ | "setPipelineDescription"
+ | "addTask"
+ | "deleteTask"
+ | "renameTask"
+ | "addInput"
+ | "deleteInput"
+ | "renameInput"
+ | "addOutput"
+ | "deleteOutput"
+ | "renameOutput"
+ | "connectNodes"
+ | "deleteEdge"
+ | "setTaskArgument"
+ | "createSubgraph"
+ | "unpackSubgraph"
+ | "validatePipeline"
+>;
+
+function createReadOnlyCsomHandlers(deps: BridgeDeps): ReadOnlyCsomHandlers {
+ return {
+ async getPipelineState() {
+ return serializeSpecForAi(requireSpec(deps), {
+ activeSubgraphPath: deps.getActiveSubgraphPath(),
+ });
+ },
+
+ async validatePipeline(): Promise {
+ const issues = validateSpec(requireSpec(deps));
+ return {
+ valid: issues.length === 0,
+ issueCount: issues.length,
+ issues: issues.map((i) => ({
+ type: i.type,
+ severity: i.severity,
+ message: i.message,
+ entityId: i.entityId,
+ issueCode: i.issueCode,
+ })),
+ };
+ },
+
+ async setPipelineName() {
+ return { success: false };
+ },
+ async setPipelineDescription() {
+ return { success: false };
+ },
+ async addTask() {
+ return { success: false, error: READ_ONLY_ERROR };
+ },
+ async deleteTask() {
+ return { success: false };
+ },
+ async renameTask() {
+ return { success: false };
+ },
+ async addInput() {
+ return { success: false, inputId: "", name: "" };
+ },
+ async deleteInput() {
+ return { success: false };
+ },
+ async renameInput() {
+ return { success: false };
+ },
+ async addOutput() {
+ return { success: false, outputId: "", name: "" };
+ },
+ async deleteOutput() {
+ return { success: false };
+ },
+ async renameOutput() {
+ return { success: false };
+ },
+ async connectNodes() {
+ return { success: false, error: READ_ONLY_ERROR };
+ },
+ async deleteEdge() {
+ return { success: false };
+ },
+ async setTaskArgument() {
+ return { success: false, error: READ_ONLY_ERROR };
+ },
+ async createSubgraph() {
+ return { success: false, error: READ_ONLY_ERROR };
+ },
+ async unpackSubgraph() {
+ return { success: false };
+ },
+ };
+}
+
+export function createRunViewToolBridge(deps: BridgeDeps): ToolBridgeApi {
+ return {
+ ...createReadOnlyCsomHandlers(deps),
+ ...createRunBridgeHandlers(deps),
+ ...createDebugBridgeHandlers(deps),
+ };
+}