diff --git a/src/components/agents/AgentTestPanel.jsx b/src/components/agents/AgentTestPanel.jsx
index 6ca5613..9c0867f 100644
--- a/src/components/agents/AgentTestPanel.jsx
+++ b/src/components/agents/AgentTestPanel.jsx
@@ -47,7 +47,7 @@ const CLASSIFICATION_COPY = {
};
/** @param {any} props */
-export function AgentTestPanel({ fields, dirty, customSkills = [] }) {
+export function AgentTestPanel({ fields, dirty, customSkills = [], onRunLive }) {
const [contextId, setContextId] = React.useState(
() => CONTEXT_OPTIONS.find((o) => fields.pages.includes(pageKeyForContext(o.contextId)))?.contextId
|| CONTEXT_OPTIONS[0].contextId
@@ -79,6 +79,7 @@ export function AgentTestPanel({ fields, dirty, customSkills = [] }) {
return {
covers,
+ disabled,
pageOnly,
scoped,
matched,
@@ -152,6 +153,33 @@ export function AgentTestPanel({ fields, dirty, customSkills = [] }) {
className="pl-9 w-full bg-surface"
/>
+ {/* Everything else on this tab is a STATIC read of where a
+ question would route. Useful, and not the same as asking. The
+ tab is called Test, so a reader reasonably expects to be able
+ to run the thing — this is that, through the existing Owliver
+ panel and the draft's own scope, so the answer comes from the
+ agent being edited rather than the published one. */}
+ {onRunLive && (
+
+ )}
diff --git a/src/components/ai-assistant/KrowAssistant.jsx b/src/components/ai-assistant/KrowAssistant.jsx
index 4a17a0f..0a46248 100644
--- a/src/components/ai-assistant/KrowAssistant.jsx
+++ b/src/components/ai-assistant/KrowAssistant.jsx
@@ -281,13 +281,21 @@ export default function KrowAssistant({
request: panelRequest, consumeRequest,
notice: panelNotice, consumeNotice,
test: panelTest, reportTest, clearTest,
+ ask: askAssistant,
} = useAssistantPanel();
/* The app's own router, not a location assignment: a full page load would
discard the thread and the panel state along with it. */
const goToPage = React.useCallback(
- (destination) => navigate(destination.route),
- [navigate]
+ (destination, question) => {
+ navigate(destination.route);
+ /* Hand the question to the panel that mounts on the other side. The
+ request lives in AssistantPanelContext, which sits ABOVE the router, so
+ it survives the navigation that discards this panel's thread. Without
+ this the reader has to retype what they just typed. */
+ if (question) askAssistant({ question });
+ },
+ [navigate, askAssistant]
);
/* Skills available here, and the roles they can act on — both read from what
diff --git a/src/components/ai-assistant/routing.js b/src/components/ai-assistant/routing.js
index f6382e4..a82fa00 100644
--- a/src/components/ai-assistant/routing.js
+++ b/src/components/ai-assistant/routing.js
@@ -159,7 +159,7 @@ export function answerableHere(context, question) {
export function navigationAnswer(destination) {
return doc(
text(`That is on **${destination.page}**. Taking you there now.`),
- note('Ask again once the page loads and I will answer from it.')
+ note('Bringing your question with me — I will answer it from that page.')
);
}
diff --git a/src/components/ai-assistant/useAssistant.js b/src/components/ai-assistant/useAssistant.js
index 6701278..a2c1df9 100644
--- a/src/components/ai-assistant/useAssistant.js
+++ b/src/components/ai-assistant/useAssistant.js
@@ -723,12 +723,20 @@ export function useConversation({
abortRef.current = null;
}
- /* Navigate after the reply is on screen, so the user reads why they moved.
- The panel re-resolves its context from the new route, which is what
- makes the next question answer from the page they land on. */
+ /* Navigate after the reply is on screen — except the reply does not
+ survive the move. The panel is page-scoped, so it re-mounts on the new
+ route and the message explaining why the reader moved is destroyed by
+ the navigation that message was explaining. The reader landed somewhere
+ else with a fresh greeting and no trace of what they asked.
+
+ So the question travels with the destination. The panel on the other
+ side asks it, which is what the reader wanted in the first place and
+ what the old copy ("ask again once the page loads") was apologising
+ for. Re-asking cannot loop: resolveIntent only navigates when the
+ destination differs from the current page. */
if (controller.signal.aborted) return;
- if (intent.kind === 'navigate') onNavigate?.(intent.destination);
+ if (intent.kind === 'navigate') onNavigate?.(intent.destination, text);
/* A skill's action runs after its reply, for the same reason: the user
should read why the form opened before it opens. */
if (intent.kind === 'skill' && intent.action) onAction?.(intent.action, intent.skill);
diff --git a/src/pages/admin/AgentDetail.jsx b/src/pages/admin/AgentDetail.jsx
index 3625525..444d5b0 100644
--- a/src/pages/admin/AgentDetail.jsx
+++ b/src/pages/admin/AgentDetail.jsx
@@ -452,6 +452,7 @@ export default function AdminAgentDetail() {
)}
{view === 'test' && (