feat: live progress updates during orchestrator tool loop

The thinking bubble now shows real-time status instead of a static spinner:
   Round 1 — thinking…
   Round 1 — web_search
   Round 2 — thinking…
   Generating response…

Implementation: async on_progress callback passed from _run_job into both
orchestrators (_run_from_messages / _run_from_contents). Callback writes to
job["progress"] under the jobs lock; poll responses include the field;
app.js displays it in the thinking bubble when present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-06-16 22:55:14 -04:00
parent 4fdb9fd0c7
commit c31eba111f
4 changed files with 37 additions and 4 deletions

View File

@@ -81,6 +81,7 @@ class JobStatusResponse(BaseModel):
gemini_summary: str | None = None
error: str | None = None
pending_confirmation: dict | None = None # {tools: [{name, args}], message: str}
progress: str | None = None # live status text shown in UI during run
# ---------------------------------------------------------------------------
@@ -112,6 +113,7 @@ async def orchestrate(req: OrchestrateRequest) -> OrchestrateResponse:
"gemini_summary": None,
"error": None,
"pending_confirmation": None,
"progress": None,
"_user": user,
"_off_record": req.off_record,
}
@@ -197,6 +199,11 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
async with _jobs_lock:
_jobs[job_id]["status"] = "running"
async def _on_progress(msg: str) -> None:
async with _jobs_lock:
if job_id in _jobs:
_jobs[job_id]["progress"] = msg
try:
from session_store import load as load_session, save as save_session, generate_session_id
@@ -240,6 +247,7 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
max_risk=max_risk,
risk_whitelist=risk_wl,
risk_blacklist=risk_bl,
on_progress=_on_progress,
)
else:
gemini_key = (
@@ -262,6 +270,7 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
max_risk=max_risk,
risk_whitelist=risk_wl,
risk_blacklist=risk_bl,
on_progress=_on_progress,
)
if result.checkpoint: