diff --git a/packages/app/src/pages/orchestrate.tsx b/packages/app/src/pages/orchestrate.tsx index 4f7660f..c67284f 100644 --- a/packages/app/src/pages/orchestrate.tsx +++ b/packages/app/src/pages/orchestrate.tsx @@ -94,6 +94,7 @@ const PHASE_LABELS: Record = { failed: "Failed", } const NO_WORKERS_WARNING_GRACE_MS = 5000 +const PLAN_QUALITY_GATE_FAILURE_LINE_RE = /^plan quality gate failed(?: before registration)?[:.]?/i type RunModelRole = keyof OrchestrateRoleModels type RunModelOption = { @@ -239,6 +240,28 @@ function parseConflictFiles(text?: string): string[] { return [...discovered] } +type PlanRetryPresentation = { + summary: string + details: string +} + +function planRetryPresentation(text: string): PlanRetryPresentation | undefined { + const trimmed = text.trim() + if (!trimmed) return undefined + const lines = trimmed + .split(/\r?\n/) + .map((line) => line.trim()) + .filter((line) => line.length > 0) + if (lines.length === 0) return undefined + if (!lines.some((line) => PLAN_QUALITY_GATE_FAILURE_LINE_RE.test(line))) return undefined + + const detailLines = lines.filter((line) => !PLAN_QUALITY_GATE_FAILURE_LINE_RE.test(line)) + return { + summary: "Retrying plan after quality gate failure.", + details: detailLines.length > 0 ? detailLines.join("\n") : trimmed, + } +} + function taskStatusLabel(status: OrchestrateTask["status"]): string { switch (status) { case "completed": @@ -4267,16 +4290,36 @@ function SessionTurnTimeline(props: {
{(message) => ( -
-
- - Assistant - - {new Date(message.timestamp).toLocaleTimeString()} - -
-
{message.text}
-
+ (() => { + const compact = planRetryPresentation(message.text) + return ( +
+
+ + Assistant + + {new Date(message.timestamp).toLocaleTimeString()} + +
+ {message.text}
} + > + {(presented) => ( +
+
{presented().summary}
+
+ Show details +
+ {presented().details} +
+
+
+ )} + +
+ ) + })() )} @@ -4330,6 +4373,7 @@ function SessionTurnTimeline(props: { {(() => { const thoughtItem = item as Extract + const compact = planRetryPresentation(thoughtItem.thought.reasoning) return (
@@ -4350,12 +4394,33 @@ function SessionTurnTimeline(props: { {new Date(thoughtItem.thought.timestamp).toLocaleTimeString()}
-
- {thoughtItem.thought.reasoning} -
+ + {thoughtItem.thought.reasoning} +
+ } + > + {(presented) => ( +
+
{presented().summary}
+
+ Show details +
+ {presented().details} +
+
+
+ )} +
) })()} diff --git a/packages/desktop/src-tauri/src/cli.rs b/packages/desktop/src-tauri/src/cli.rs index 8e8d341..c895cbc 100644 --- a/packages/desktop/src-tauri/src/cli.rs +++ b/packages/desktop/src-tauri/src/cli.rs @@ -540,11 +540,16 @@ pub fn serve( ) -> (CommandChild, oneshot::Receiver) { let (exit_tx, exit_rx) = oneshot::channel::(); - tracing::info!(port, "Spawning sidecar"); + // Isolate each desktop sidecar worker so stale/orphaned processes cannot + // consume Temporal activities for newly-started runs. + let temporal_task_queue = format!("oneshot-orchestrator-v2-desktop-{port}"); + + tracing::info!(port, temporal_task_queue, "Spawning sidecar"); let envs = [ ("OPENCODE_SERVER_USERNAME", "oneshot".to_string()), ("OPENCODE_SERVER_PASSWORD", password.to_string()), + ("TEMPORAL_TASK_QUEUE", temporal_task_queue), ]; let (events, child) = spawn_command(