feat(desktop): reconcile live tool events, polish thread chrome, harden boot
- chat-messages: match tool rows by overlapping query/context/preview values so preview-first `tool.progress` rows reliably adopt later stable-id `tool.start` payloads instead of spawning ghost rows or mis-merging parallel same-name calls; preserve prior args/result across phases. - tui_gateway: emit full args + parsed result on `tool.start` / `tool.complete`, drop redundant `tool.started` re-emit from `tool.progress`. - electron/main: prefer SOURCE_REPO_ROOT before PATH `hermes` in dev so local backend edits actually run; split hardening helpers into `electron/hardening.cjs` with tests. - thread/tool UI: one-shot enter animation keyed by stable ids, braille spinner for running rows, Cursor-like disclosure rows, drill-down + duration/count formatting via new tool-fallback-model. - composer: extract `text-utils`, drop liquid-glass overrides. - right-rail: split preview-pane into preview-console / preview-file. - runtime: incremental external-store runtime + runtime-readiness gate; onboarding store + tests; route-resume hook test. - regression tests for live tool reconciliation (parallel tools, id-less progress, preview-first rows, structured args/results).
This commit is contained in:
@@ -1591,12 +1591,12 @@ def _on_tool_start(sid: str, tool_call_id: str, name: str, args: dict):
|
||||
_emit(
|
||||
"tool.start",
|
||||
sid,
|
||||
{"tool_id": tool_call_id, "name": name, "context": _tool_ctx(name, args)},
|
||||
{"tool_id": tool_call_id, "name": name, "args": args, "context": _tool_ctx(name, args)},
|
||||
)
|
||||
|
||||
|
||||
def _on_tool_complete(sid: str, tool_call_id: str, name: str, args: dict, result: str):
|
||||
payload = {"tool_id": tool_call_id, "name": name}
|
||||
payload = {"tool_id": tool_call_id, "name": name, "args": args}
|
||||
session = _sessions.get(sid)
|
||||
snapshot = None
|
||||
started_at = None
|
||||
@@ -1606,6 +1606,10 @@ def _on_tool_complete(sid: str, tool_call_id: str, name: str, args: dict, result
|
||||
duration_s = time.time() - started_at if started_at else None
|
||||
if duration_s is not None:
|
||||
payload["duration_s"] = duration_s
|
||||
try:
|
||||
payload["result"] = json.loads(result)
|
||||
except Exception:
|
||||
payload["result"] = result
|
||||
summary = _tool_summary(name, result, duration_s)
|
||||
if summary:
|
||||
payload["summary"] = summary
|
||||
@@ -1645,7 +1649,9 @@ def _on_tool_progress(
|
||||
if not _tool_progress_enabled(sid):
|
||||
return
|
||||
if event_type == "tool.started" and name:
|
||||
_emit("tool.progress", sid, {"name": name, "preview": preview or ""})
|
||||
# `_on_tool_start` already emits the authoritative `tool.start` with
|
||||
# the stable tool id and args. Emitting another id-less progress row
|
||||
# here makes the desktop live view diverge from hydrated history.
|
||||
return
|
||||
if event_type == "reasoning.available" and preview:
|
||||
_emit("reasoning.available", sid, {"text": str(preview)})
|
||||
|
||||
Reference in New Issue
Block a user