Commit Graph

4 Commits

Author SHA1 Message Date
João Salavisa 5f4b3ec0c0 feat(web): allow renaming quick-chat tabs locally (#830)
* feat(web): allow renaming quick-chat tabs locally

Double-click a quick-chat tab to rename it. The name is stored in
localStorage and overlays the SSR-derived task title on hydration, so
renames survive reload without touching the backend.

* fix(web): address quick-chat rename review feedback

- Drop unused sessionId prop from QuickChatTabItem
- Enter handler now blurs the input instead of calling commit(),
  preventing a duplicate commit when onBlur fires on unmount
- Add hydrator tests covering the localStorage name overlay

* fix(web): make Escape actually cancel quick-chat rename

Escape was setting isEditing(false), which unmounted the focused input,
causing onBlur (commit) to fire synchronously with the typed draft still
in the closure — committing the rename instead of discarding it.

Cancel now flips a cancelledRef and blurs the input; commit short-circuits
when the ref is set, so onBlur is the single edit-exit path for both
Enter and Escape. Adds a render test covering both flows.

* fix(web): guard quick-chat rename against IME and orphan storage writes

- Skip Enter/Escape handling while IME composition is active so candidate
  confirmation does not commit/cancel the rename
- renameQuickChatSession only writes to localStorage when the session was
  actually found in state, preventing orphan entries for stale session IDs

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-05-05 21:47:35 +01:00
José Almeida 7e8b4bb30e feat(quick-chat): eager-init agent on profile selection (#747)
* feat(quick-chat): eager-init agent on profile selection

Switch quick-chat backend from `IntentPrepare` to `IntentStart` so the
agent process boots immediately when the user picks a profile, instead
of waiting for the first prompt. The ACP `session/new` handshake fires
before the user types anything, so the slash menu, mode dropdown, and
model selector are populated up-front. Fixes the empty-slash-menu UX
in quick chat for ACP agents (OpenCode, Claude, Codex, etc.).

Frontend picker shows a per-card spinner + disables sibling cards
during boot. Rapid profile switches (and other picker-bypassing
actions like opening a new tab, switching tabs, or closing the modal)
supersede the in-flight start via a monotonic request id; the
resolved task is cleaned up via `deleteQuickChatTask` so we never
orphan an ephemeral task or its agent process.

Backend rollback uses a fresh background context so `DeleteTask`
runs even when the request context has already been cancelled by
the client.

`mock-agent` now emits `available_commands_update` from `NewSession`
and `LoadSession` (matching real OpenCode/Claude) so the eager-init
pipeline is exercised in e2e. Existing prompt-time emit kept as an
idempotent fallback.

* test+cleanup(quick-chat): unit tests for useAgentSelection; minor cleanups

- Add 5 unit tests for useAgentSelection covering happy path, rapid-pick
  supersession, reset() during in-flight, and error toast vs silent-on-superseded.
  Exercises the orphan-task cleanup that the eager-init flow depends on.
  (claude bot review)
- Drop redundant isDisabled alias in AgentPickerView. (claude bot review)
- Drop unused ctx parameter from emitAvailableCommandsAfterDelay — it was
  always called with context.Background() so the Done() branch was dead
  code. (claude bot review)
- Re-format with prettier after the rebase.

* fix(quick-chat): use constants.TaskDeleteTimeout for rollback ctx

Match the other DeleteTask call sites in task_http_handlers.go (line 851)
instead of an ad-hoc 10s timeout. (claude bot review)
2026-05-01 00:21:53 +01:00
José Almeida 89332e1cbb fix: render passthrough terminal in quick chat (#744)
* fix(backend): start agent immediately for passthrough quick-chat sessions

Passthrough profiles need a running PTY for the terminal to attach. When
launchPrepare is called with a passthrough profile, upgrade the request to
launchStart so the PTY exists by the time the frontend renders the terminal.

* fix(web): render passthrough terminal in quick chat for passthrough profiles

Quick chat used MessageList + ChatInputArea unconditionally, so passthrough
profiles showed an empty chat with no agent output (PTY never attached).
Render PassthroughTerminal when the active session is passthrough, mirroring
the task-page branch. Passthrough is detected via session.is_passthrough or,
before WS hydrates, via the agent profile's cli_passthrough flag — looked up
through a new agentProfileId stored on quickChat session entries (set by the
picker on selection and seeded by SSR for restored sessions).

* style(web): apply prettier formatting

* fix(web): address review feedback on quick-chat passthrough rendering

- Narrow `metadata.agent_profile_id` to string at runtime via a
  `readAgentProfileId` helper instead of a blind type cast.
- Collapse `useIsQuickChatPassthrough` into a single `useShallow`
  selector to avoid three separate store subscriptions.

* fix(backend): prevent launchPrepare ↔ launchStart recursion for passthrough

When passthrough quick-chat sessions hit a workflow step that blocks
auto-start, launchStart's downgrade to launchPrepare would have re-upgraded
back into launchStart and looped forever. Skip the passthrough upgrade when
AutoStart=true, since that flag uniquely identifies the downgrade path.

Adds tests for `isPassthroughProfile` and a regression test that runs the
launchStart→launchPrepare path under a deadline so future recursions surface.

* test(web): cover existing agentProfileId preservation on reopen

* style(web): apply prettier formatting

* test(backend): seed task with blocked workflow step in recursion regression

Without persisted task and step, shouldBlockAutoStart short-circuited to
false, the test bypassed the launchStart→launchPrepare downgrade entirely,
and removing the recursion guard would still pass. Seed the task with a
WorkflowStepID mapping to a step lacking auto_start_agent so the guard is
the only thing preventing the bounce. Verified by temporarily removing the
guard — the test now times out as expected.

* test(backend): cover ResolveAgentProfile error path in isPassthroughProfile
2026-04-30 18:36:26 +01:00
João Salavisa d84b8873ea feat: quick chat implementation (#393) 2026-03-08 22:37:37 +00:00