Commit Graph

8334 Commits

Author SHA1 Message Date
Katia Bulatova adae596279 fix(webapp): keep the delete-chat confirmation alive outside the history popover
The confirmation dialog was rendered inside the non-modal history popover, so
taking focus dismissed the popover and unmounted the dialog with it. It now
lives in the header as a sibling of the popover, and the panel's Escape handler
only fires for events whose target is inside the panel.
2026-08-09 14:50:29 +00:00
Katia Bulatova ed67d38c02 fix(webapp): drop a watch draft when the panel changes chat
A half-configured watch card, and its client request id, survived a chat switch
and a new chat, so it was submitted against whatever chat happened to be open.
Chat changes now go through one function that both resets the card and
invalidates in-flight opens - including the chat a submitted watch lands in.
2026-08-09 14:50:27 +00:00
Katia Bulatova 6d22d17367 fix(webapp): count unread chats from the list instead of subtracting per visit
Opening a chat subtracted from the unread count twice, and again on every
revisit; the read effect's cleanup also restored the on-screen chat id after the
panel had let go, which hid that chat's later wakes for the rest of the session.
The count is now derived from the chat list, reading a chat settles both its
wake and its work, and both counts re-seed when the environment changes.
2026-08-09 14:50:26 +00:00
Katia Bulatova 5125f7c2fb fix(dashboard-agent): fail a watch investigation whose card refuses to close
Only a deleted chat is swallowed now. A refused close left the card in_progress while the
action reported success, so the panel span forever with nothing to retry it.
2026-08-09 14:50:24 +00:00
Katia Bulatova 34c24a9048 fix(webapp): let a wait-time watch end when its queue is gone or unreadable
The reader always reported a live queue, so a queue_oldest_age watch on a deleted queue sat
pending for its whole window and an engine failure read as a healthy zero wait.
2026-08-09 14:50:22 +00:00
Katia Bulatova 07e60f931d fix(webapp): normalise an error watch's fingerprint before its identity
The errors page cites `error_<fingerprint>` and the agent's tools cite the bare one, so
the same error could carry two watches — two wakes and two emails per recurrence — and the
prefixed spelling produced an unresolvable link in the wake.
2026-08-09 14:50:21 +00:00
Katia Bulatova ef148eaf66 fix(webapp): claim the alert dispatch before the watch sweep fans out
The sweep alerted without claiming, so the wake it then scheduled claimed successfully
and alerted again — a recovered watch emailed twice.
2026-08-09 14:50:19 +00:00
Katia Bulatova 137c79abc8 fix(webapp): send a task queue's stored name to the watch it offers
The queue detail page presents a task queue with its `task/` prefix stripped, but
`TaskQueue.name` keeps it, so every watch the page offered on a task's own queue was
refused as a missing target.
2026-08-09 14:50:17 +00:00
Katia Bulatova 4fc64a2473 test(webapp): give the watch container tests a timeout that fits booting one
Each test replays the migrations inside its own budget, which overruns vitest's
5s default on a loaded CI host and times out a different test each run.
2026-08-09 14:50:15 +00:00
Katia Bulatova 227c1c8ce7 test(webapp): regenerate the watch wording snapshot for the untruncated error id
Sibling tests were updated when the fingerprint stopped truncating; the snapshot
was not.
2026-08-09 14:50:12 +00:00
Katia Bulatova c25dd6dc53 refactor(dashboard-agent): factor the repeated shapes out of render_view and schedule_watch
The block catalog and the watch spec spelled every variant out in full, and the
serialized tool definitions are paid on every call of every chat. Two collapses:
the seven simple evidence kinds become one member with a `kind` enum, and the
watch kinds that take the same fields share a member each. Same payloads accepted,
same payloads refused.
2026-08-09 14:50:10 +00:00
Katia Bulatova 804c0c9397 fix(dashboard-agent): tell get_queue who feeds a custom queue
A custom queue's name has nothing to do with any task id, but the agent searched the deployed task list for a task named after the queue, found none, and reported the queue's tasks as deleted or renamed.

get_queue now returns consumerTasks for a custom queue — the deployed task slugs whose queue config names it — and the prompt says an absent task of that name is not evidence about the queue.
2026-08-09 14:50:08 +00:00
Katia Bulatova d51b01c71d fix(webapp): keep the wake poll running for a turn started behind a closed panel
DashboardAgent declared turnStarted but nothing set it, so a question asked
and then left with the panel closed raised no dot until the next reload.

The panel now reports turn activity up to DashboardAgent, which latches onto
the chat whose turn is running. Closing the panel reports nothing, so the
latch holds; re-opening the chat with the turn over clears it.
2026-08-09 14:50:06 +00:00
Katia Bulatova 27920f79f7 feat(webapp): poll for unread work while the panel is closed 2026-08-09 14:50:03 +00:00
Katia Bulatova c6314c6d5d feat(webapp): mark a chat unread when work finished behind a closed panel
A watch wake was the only thing that raised the dot and the highlight. An answer or a settled card that landed while the panel was closed now does the same — without a toast, which stays a wake's alone.
2026-08-09 14:50:01 +00:00
Katia Bulatova 762a6fe824 fix(dashboard-agent): mint the environment JWT with the scopes the tools actually use
Most tools spend an environment JWT with a hardcoded scope list; the delegated token's cap only ceilings that exchange. Queues were missing from the list, so the live lookup 403'd and read as a queue that does not exist.
2026-08-09 14:49:59 +00:00
Katia Bulatova c2ab6b65cc test(webapp): pin every tool's API against the scope its route authorizes on 2026-08-09 14:49:57 +00:00
Katia Bulatova 5c86d18fb6 fix(webapp): let the agent read a queue's own row, not only its metrics
Metrics authorize as a query read; paused, depth and limit live on the queue row, which is a queues read the agent's token did not carry — so a live lookup 403'd and read as a missing queue.
2026-08-09 14:49:55 +00:00
Katia Bulatova e6622174fb fix(dashboard-agent): name the state that explains the numbers, and stop reading empty as absent 2026-08-09 14:49:53 +00:00
Katia Bulatova 7791ccc8e1 fix(dashboard-agent): say a queue is paused before reciting its metrics
Metrics are a window: they cannot show a pause, and they cannot show a backlog that arrived after the window. get_queue now carries the queue's live row, and the prompt leads with it.
2026-08-09 14:49:50 +00:00
Katia Bulatova 5d40e2d7ce fix(webapp): a paused queue offers no backlog chips either
The page chips come from the page registry, not the signals — so hiding the saturation signal left both the investigate and the watch chip on a paused queue.
2026-08-09 14:49:48 +00:00
Katia Bulatova 30c90f7b03 fix(webapp): no watch offer on a paused queue, in the button or the chips
A paused queue can neither drain nor grow, so every condition it could watch for is a promise nothing keeps until someone resumes it.
2026-08-09 14:49:46 +00:00
Katia Bulatova 36de3784ba fix(dashboard-agent): stop reporting a queue as missing when only its kind was guessed wrong
The metrics route answers an unknown queue with zeroes, so asking for the wrong kind read as an idle queue. get_queue now tries the other kind before believing them.
2026-08-09 14:49:44 +00:00
Katia Bulatova b1454eddb0 docs(dashboard-agent): say where each agent button appears, and on what condition 2026-08-09 14:49:44 +00:00
Katia Bulatova ae331fe43b fix(webapp): give a watch email the same headline the chat shows 2026-08-09 14:49:42 +00:00
Katia Bulatova beb53d03fa fix(webapp): name a watched error in full, not truncated 2026-08-09 14:49:39 +00:00
Katia Bulatova aa11e9784e fix(webapp): stop saying error twice when naming a watched error 2026-08-09 14:49:35 +00:00
Katia Bulatova 994d2f4053 fix(webapp): one watch button per answer, not two
An investigation card carries its own watch button, and the prompt asked for a watch offer on top of it. The card wins — it is the one with the pre-filled spec — and the prompt now says so too.
2026-08-09 14:49:31 +00:00
Katia Bulatova da938736c5 feat(webapp): tell the user later — the dashboard agent's Watch
"Tell me when this run finishes", "ping me if that error comes back". A watch
checks on its own cadence, reports once, and stops within 24 hours. The user
confirms a pre-filled card, so nothing starts behind their back; the answer
lands in the chat, and by email if they asked for it.

Restores the feature this branch's base PR set aside, unchanged: the card and
chips, the wake banner and toast, the unread badge, the checks for runs, queues,
errors and health, the batch scheduler and its backstops, the alert channel and
its email, and the agent's schedule_watch tool with the prompt that governs it.
2026-08-09 14:49:26 +00:00
Katia Bulatova 3cd4102eeb feat(dashboard-agent): move the watch data layer in from the base PR
Pure move: the watch contracts and queries, the delivery-claim id, the chat
lock, markChatRead and the watch cancellation inside softDeleteChat, plus the
four contract types the watch schema annotates, re-narrowed here.
2026-08-09 14:49:20 +00:00
Katia Bulatova d87ca1df0f perf(webapp): restore the stripped-message cache the UI rewrite dropped
The transcript perf fix had two halves. The winners half kept its identity because a test
pinned it; the strip cache had none, so rewriting the component into chat primitives quietly
brought back the rebuild-every-render version, and every tool-calling turn re-rendered on each
streamed token again. Restore the cache and pin it the same way.

orderTranscript is not implicated: it returns a fresh array but the same message objects, which
is what the turns memoize on.
2026-08-09 14:47:46 +00:00
Katia Bulatova 43a9199b19 fix(webapp): stop the CLI's AI help link dead-ending without an AI surface
Where neither Ask AI nor the agent can open, the redirect left `?aiHelp=` on the
environment page with nothing to read it. Send the question to the docs instead.
2026-08-09 14:47:44 +00:00
Katia Bulatova f64c1d1bfe fix(webapp): stop the Ask AI host remounting the dashboard, and four review fixes
- `_app`: `AskAIRoot` is a sibling of the app, not a wrapper. Its Kapa provider
  mounts client-only, so wrapping the outlet remounted the whole signed-in tree
  once per page load on cloud. Entry points reach it through an open-request
  bridge instead of a render prop.
- The agent no longer hands a `trigger://source/...` target to `navigate`: a
  resolved GitHub URL opens in a new tab, and only a root-relative path is routed.
- Chat history reloads coalesce without answering a request with data fetched
  before it, so a new chat and its title land.
- Help & Feedback and the shortcuts sheet offer each AI surface only where the
  reader has it: Ask AI is back for users without agent access.
- Restore the `AskAgentButton` mount on the deploy blank states, dropped in the
  PR split.
- `agent-shortcuts.test.ts`: import ⌘I from `ask-ai-channels`.
2026-08-09 14:47:43 +00:00
Katia Bulatova 886e80237f fix(webapp): treat a transcript re-read that isn't a list as a failed read 2026-08-09 14:47:42 +00:00
Katia Bulatova b282c79b15 fix(webapp): key an investigation's action rows by position, not by kind 2026-08-09 14:47:40 +00:00
Katia Bulatova aaed07b648 fix(webapp): step the composer's live region instead of reading every keystroke
Between the warning point and the limit the region announced a new count
per character; it now steps in 200s and names the limit on reaching it.
2026-08-09 14:47:39 +00:00
Katia Bulatova fd11d3a75b fix(webapp): read the agent's page label from the left of the path
A preview branch named `env` put a second `env` segment in the path and
`lastIndexOf` picked it, shifting every index derived from it.
2026-08-09 14:47:37 +00:00
Katia Bulatova 8d2e2c938e fix(webapp): pin the AI-help redirect to the dashboard's own origin
new URL(environmentPath, origin) ignores origin when the path is absolute, and
the result goes straight into redirect(). Today's only caller passes a
builder-generated internal path, so this closes the gap rather than a hole.
2026-08-09 14:47:36 +00:00
Katia Bulatova 5e8b6e17e6 fix(webapp): keep the hero's heading reachable when it outgrows the panel
justify-center on a scrolling column overflows equally in both directions, and
nothing can scroll back past the origin, so at the docked panel's narrowest the
heading and composer were unreachable. The child centres with m-auto, which gives
its space up once there is none to spare.
2026-08-09 14:47:34 +00:00
Katia Bulatova 022388cd84 fix(webapp): announce the composer's counter and name the history button
The character counter's live region only entered the DOM at the warning point,
and several screen readers only announce updates for a region that was already
there; it is now always mounted and empty until there is something to say.

The history trigger's aria-label replaced the chat title it shows, so a
speech-input user could not activate it by the words on it. The title now leads
the accessible name.
2026-08-09 14:47:29 +00:00
Katia Bulatova 084d45fc1b fix(webapp): stop the URI resolver working after the panel closes
A request still in flight at unmount rejected afterwards, and the catch scheduled
a retry that fetched again and set state for a component that was gone. The hook
tracks whether it is still mounted and neither records nor reschedules once it is
not.
2026-08-09 14:47:25 +00:00
Katia Bulatova e27e80ee97 fix(webapp): send a deep-linked question once
setSearchParams only starts the navigation that drops the param, so a render
before it commits saw the question again and asked it a second time. The reader
now records what it sent and forgets it once the URL no longer carries it, so a
later visit with the same question still works.
2026-08-09 14:47:22 +00:00
Katia Bulatova a09d8a86fa fix(webapp): key a repeated view block by its own position
ViewBlocks looked each surviving block's index up with indexOf inside the render
loop: quadratic, and two occurrences of the same block object both answered with
the first index, so they collided on one React key. latestRevisionEntries carries
each survivor's position out instead.
2026-08-09 14:47:20 +00:00
Katia Bulatova e8c43e4f43 fix(webapp): time the report sparkline's bars from the report, not the reader's clock
Bar timestamps came from Date.now() during render, so the same bar reported a
different time on every re-render and a server pass disagreed with the client.
They now come from the view model's generatedAt, which the schema already
describes as the timestamp the renderer must not invent.

Moves the arithmetic into report-spark.ts to keep it clock-free and testable,
and drops the unreachable Math.max on the slice end while doing so.
2026-08-09 14:47:18 +00:00
Katia Bulatova 2c7575fa86 docs(webapp): say who Cmd-I actually opens the agent for 2026-08-09 14:47:17 +00:00
Katia Bulatova 466ed7d0fd fix(webapp): stop the agent's retry duplicating the failed message
Retry appended the last user message again, so the failed turn stayed in the
transcript and its text was sent twice. It now regenerates once the agent has
started answering, and otherwise re-sends the failed turn under its own id.
2026-08-09 14:47:15 +00:00
Katia Bulatova 4472339a05 fix(webapp): stop a zero-limit queue reading as at capacity
A limit of 0 is zero capacity, not saturation: running >= 0 holds for every
queue, so any backlog marked the queue degraded and offered Investigate, while
the agent's own suggested prompt stayed silent. One predicate now decides it
for the queue detail page, the queues list badge and the page mappers.
2026-08-09 14:47:13 +00:00
Katia Bulatova db578e0e19 fix(webapp): give icon-only tooltip buttons an accessible name
Radix tooltip content is not the accessible name of its trigger, so the
icon-only ask-agent button and the two deploy docs links announced as
unnamed controls. Name them explicitly and pass asChild so the tooltip
trigger stops wrapping them in a second button.

Adds a source scan that fails on the next SimpleTooltip with an unnamed
or double-wrapped control, with the pre-existing sites baselined.
2026-08-09 14:47:11 +00:00
Katia Bulatova 8c682da36a perf(webapp): keep report blocks out of the investigation winner pass 2026-08-09 14:47:10 +00:00
Katia Bulatova 4e278722f4 docs: say where the docs went now the header button is gone 2026-08-09 14:47:08 +00:00