Chat stream: translate the app's own delta lines before the delta branch, so the answer-text code stays as merged

This commit is contained in:
Niko1221 committed 2026-10-18 21:17:43 +02:00
1 parent 714ca744dc
commit 2832586641
1 file changed
+3 -4
+3 -4
View File
@@ -1499,9 +1499,6 @@ export async function approvePlan(sessionId: string, planId: string) {
/** "Start as goal" (and Resume on a paused plan): the host streams the run like any chat turn. */
export async function startPlanGoal(planId: string) { await sendMessage('', { planId }); }
/** The text of a `delta` frame. A line from the app itself carries `key` (the English sentence) so it is shown in the person's language. */
const deltaText = (frame: Record<string, unknown>) => typeof frame.key === 'string' ? tr(frame.key, Array.isArray(frame.values) ? frame.values : undefined) : String(frame.text ?? '');
export async function sendMessage(text: string, options: { fromLoop?: boolean; goalAction?: 'resume'; planId?: string; shown?: string; title?: string; markdown?: boolean } = {}) {
if (state.streaming) {
// Typed while the assistant works: behind a running goal it waits in the queue; otherwise there is nothing to do but wait.
@@ -1607,6 +1604,8 @@ export async function sendMessage(text: string, options: { fromLoop?: boolean; g
workspaceId: state.activeWorkspace,
},
(frame) => {
// A line from the app itself carries `key` (the English sentence): shown in the person's language.
if (frame.type === 'delta' && typeof frame.key === 'string') frame = { ...frame, text: tr(frame.key, Array.isArray(frame.values) ? frame.values : undefined) };
const type = frame.type as string;
if (type === 'start' && frame.agent === true) { const turn = liveTurns.get(sessionId); if (turn) turn.agent = true; }
if (type === 'queued') {
@@ -1653,7 +1652,7 @@ export async function sendMessage(text: string, options: { fromLoop?: boolean; g
} else if (type === 'agent') {
patchSession(sessionId, s => ({ ...s, agents: updateAgent(s.agents ?? [], frame.agent) }));
} else if (type === 'delta') {
patchAssistant((m) => { const content=tidyAnswer(m.content+deltaText(frame)); if (m.reasoningStartedAt && m.reasoningMs === undefined) m = { ...m, reasoningMs: Date.now() - m.reasoningStartedAt }; const count=Math.max(1,Math.ceil((content.length+(m.reasoning?.length??0))/4)); return {...m,content,...(m.blocks?{blocks:appendText(m.blocks,deltaText(frame))}:{}),tokens:m.usageSource==='estimated'&&m.tps?m.tokens:count,inputTokens:m.inputTokens??Math.ceil((body.length+prior.reduce((n,p)=>n+p.content.length,0))/4),usageSource:'estimated'}; });
patchAssistant((m) => { const content=tidyAnswer(m.content+String(frame.text??'')); if (m.reasoningStartedAt && m.reasoningMs === undefined) m = { ...m, reasoningMs: Date.now() - m.reasoningStartedAt }; const count=Math.max(1,Math.ceil((content.length+(m.reasoning?.length??0))/4)); return {...m,content,...(m.blocks?{blocks:appendText(m.blocks,String(frame.text??''))}:{}),tokens:m.usageSource==='estimated'&&m.tps?m.tokens:count,inputTokens:m.inputTokens??Math.ceil((body.length+prior.reduce((n,p)=>n+p.content.length,0))/4),usageSource:'estimated'}; });
} else if (type === 'deliverable') {
const file = readDeliverable(frame);
if (file) patchAssistant(m => ({ ...m, deliverables: mergeDeliverable(m.deliverables, file) }));