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:
Brooklyn Nicholson 2026-05-11 21:38:47 -04:00
parent fdf73f0adf
commit d208f2c2c0
64 changed files with 5614 additions and 2703 deletions

View file

@ -432,11 +432,13 @@ canvas {
padding-left: var(--message-text-indent);
}
/* Tool/thinking blocks now live at message-text alignment (no leading
chevron column to escape into), so their headers and bodies share a
common left edge with the model's text. */
[data-slot='aui_assistant-message-content'] > [data-slot='tool-block'],
[data-slot='aui_assistant-message-content'] > [data-slot='aui_thinking-disclosure'] {
margin-inline-start: calc(-1 * var(--message-text-indent));
width: calc(100% + var(--message-text-indent));
max-width: calc(100% + var(--message-text-indent));
width: 100%;
max-width: 100%;
}
[data-slot='aui_assistant-message-content'] .aui-md [data-streamdown='code-block'] code {
@ -525,3 +527,55 @@ canvas {
width: 0.875rem;
height: 0.875rem;
}
/* Streaming caret + comet tail. Anchored to the cursor (not the block's
right edge) so it reads on short last lines. Spread inflates the bg-color
veil so it overlays text instead of haloing the tiny dither block. */
[data-slot='aui_assistant-message-content']
[data-status='running']
> div
> *:last-child::after {
content: '' !important;
display: inline-block;
position: relative;
z-index: 2;
width: 0.6em;
height: 1em;
margin-left: 0.18em;
vertical-align: middle;
border-radius: 1.5px;
background: repeating-conic-gradient(currentColor 0% 25%, transparent 0% 50%) 0 0 / 2px 2px;
color: color-mix(in srgb, var(--dt-foreground) 72%, transparent);
box-shadow:
-0.8ch 0 1.4ch 0.55em color-mix(in srgb, var(--dt-background) 80%, transparent),
-3ch 0 2.4ch 0.55em color-mix(in srgb, var(--dt-background) 64%, transparent),
-6.5ch 0 3.6ch 0.5em color-mix(in srgb, var(--dt-background) 44%, transparent),
-11ch 0 5ch 0.45em color-mix(in srgb, var(--dt-background) 28%, transparent),
-16ch 0 6.4ch 0.4em color-mix(in srgb, var(--dt-background) 16%, transparent),
-22ch 0 7.6ch 0.35em color-mix(in srgb, var(--dt-background) 8%, transparent),
0 0 0.4ch color-mix(in srgb, var(--dt-foreground) 28%, transparent);
animation: hermes-stream-caret 1.1s ease-in-out infinite;
}
@keyframes hermes-stream-caret {
0%, 100% { opacity: 0.95; }
50% { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
[data-slot='aui_assistant-message-content']
[data-status='running']
> div
> *:last-child::after {
animation: none;
opacity: 0.8;
}
}
/* Live thinking preview window. Pairs with the ResizeObserver in
ThinkingDisclosure that pins scrollTop to the bottom older lines fade
into the top mask while the latest tokens settle in below. */
.thinking-preview {
-webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 28%, black 100%);
mask-image: linear-gradient(to bottom, transparent 0%, black 28%, black 100%);
}