wip: first run of implementation

This commit is contained in:
2026-02-25 20:29:01 +01:00
parent 2e203fa3a9
commit 20ea499a6f
40 changed files with 2170 additions and 22 deletions

View File

@@ -4,6 +4,7 @@ import { resolveAssistantEditorContext } from '../../navigation/assistantPromptC
import { planAssistantRequest } from '../../navigation/assistantConversation';
import { dispatchAssistantAction } from '../../navigation/assistantActionDispatcher';
import { extractAssistantResponseContent, type AssistantPanelElement } from '../../navigation/assistantPanelSpec';
import { toClarificationElements } from '../../navigation/protocolNeedsInput';
import { ensureConversationId } from '../../navigation/chatSession';
import { getChatSurfaceMode } from '../../navigation/chatSurfaceMode';
import { useChatMessageSender } from '../../navigation/useChatMessageSender';
@@ -22,6 +23,7 @@ export const AssistantSidebar: React.FC = () => {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [conversationId, setConversationId] = useState<string | null>(null);
const [panelElements, setPanelElements] = useState<AssistantPanelElement[]>([]);
const [actionPolicies, setActionPolicies] = useState<Record<string, 'silent' | 'confirm' | 'danger'>>({});
const [actionError, setActionError] = useState<string | null>(null);
const {
@@ -127,10 +129,23 @@ export const AssistantSidebar: React.FC = () => {
throw new Error(sendResult.error || 'Failed to send assistant message');
}
if (sendResult.message) {
if (sendResult.envelope) {
finalizeAssistantTurn(resolvedConversationId, sendResult.envelope.assistantText);
const uiElements = Array.isArray(sendResult.envelope.ui?.elements)
? (sendResult.envelope.ui?.elements as AssistantPanelElement[])
: toClarificationElements(sendResult.envelope.needsInput);
setPanelElements(uiElements);
setActionPolicies(
sendResult.envelope.actions.reduce<Record<string, 'silent' | 'confirm' | 'danger'>>((accumulator, action) => {
accumulator[action.action] = action.policy;
return accumulator;
}, {}),
);
} else if (sendResult.message) {
const parsedResponse = extractAssistantResponseContent(sendResult.message);
finalizeAssistantTurn(resolvedConversationId, parsedResponse.displayText);
setPanelElements(parsedResponse.panelSpec?.elements ?? []);
setActionPolicies({});
} else {
appendAssistantMessage(resolvedConversationId, tr('chat.errorEmptyResponse'));
stopStreaming();
@@ -146,6 +161,61 @@ export const AssistantSidebar: React.FC = () => {
};
const handleAssistantAction = (action: string, payload?: Record<string, unknown>) => {
if (action === 'submitNeedsInput' && conversationId) {
const values = payload?.values;
if (!values || typeof values !== 'object') {
setActionError(tr('assistantSidebar.error.actionFailed'));
return;
}
const clarificationMessage = `needs_input_response: ${JSON.stringify(values)}`;
beginUserTurn(conversationId, clarificationMessage);
void sendChatMessage({
conversationId,
message: clarificationMessage,
metadata: { surface: 'sidebar' },
}).then((sendResult) => {
if (!sendResult.success) {
appendAssistantMessage(
conversationId,
tr('chat.errorPrefix', { error: sendResult.error || tr('chat.errorNoResponse') }),
);
stopStreaming();
return;
}
if (sendResult.envelope) {
finalizeAssistantTurn(conversationId, sendResult.envelope.assistantText);
const uiElements = Array.isArray(sendResult.envelope.ui?.elements)
? (sendResult.envelope.ui?.elements as AssistantPanelElement[])
: toClarificationElements(sendResult.envelope.needsInput);
setPanelElements(uiElements);
setActionPolicies(
sendResult.envelope.actions.reduce<Record<string, 'silent' | 'confirm' | 'danger'>>((accumulator, action) => {
accumulator[action.action] = action.policy;
return accumulator;
}, {}),
);
return;
}
if (sendResult.message) {
const parsedResponse = extractAssistantResponseContent(sendResult.message);
finalizeAssistantTurn(conversationId, parsedResponse.displayText);
setPanelElements(parsedResponse.panelSpec?.elements ?? []);
setActionPolicies({});
}
}).catch((error) => {
console.error('Failed to submit assistant clarification:', error);
appendAssistantMessage(conversationId, tr('chat.errorGeneric'));
stopStreaming();
});
return;
}
const result = dispatchAssistantAction(
{
action,
@@ -230,7 +300,7 @@ export const AssistantSidebar: React.FC = () => {
)}
{panelElements.length > 0 && (
<AssistantPanelControls elements={panelElements} onAction={handleAssistantAction} />
<AssistantPanelControls elements={panelElements} onAction={handleAssistantAction} actionPolicies={actionPolicies} />
)}
</div>
);