Commit Graph

27 Commits

Author SHA1 Message Date
Carlos Florêncio b01e6abc06 fix: retry CLI passthrough launch without resume flag after fast-fail (#810) 2026-05-04 08:45:20 +01:00
José Almeida fcc7ceb8b6 fix(approvals): drop stuck pending permission_request messages (#723)
* fix(web): exclude permission_request from turn-complete safety net

The session.turn.completed handler iterated over messages and forced
metadata.status="complete" on anything carrying tool_call_id. Permission
prompts also carry tool_call_id (for FE pairing with their tool_call),
so this loop overwrote the user's "approved" decision on every prompt
in the turn — and the FE component re-rendered the Approve/Deny buttons.

* fix(backend): protect permission_request rows from tool_call sweeps

Three permission_request invariants the prior code violated, mirroring
the FE bug fix:

- CompletePendingToolCallsForTurn: SQL UPDATE filtered by tool_call_id
  presence, also catching permission_request rows. Add type filter so
  the safety net only touches actual tool messages.
- GetMessageByToolCallID: returned the oldest row carrying that
  tool_call_id, which races to the permission_request when timing
  shifts. Exclude permission_request type so a tool_update can never
  land on the user's approve/reject row.
- applyToolCallMessageUpdate: defensive guard that refuses to overwrite
  a permission_request, making the invariant explicit at the layer that
  does the writing.

Adds a regression test for CompletePendingToolCallsForTurn covering
approved/rejected/pending permission rows.

* test(e2e): cover approval-reappear regression with mock-agent scenario

Adds a multi-permission mock-agent scenario (three sequential bash
tools, each requiring approval) and an E2E test that approves all
three and asserts no permission action row remains visible after
the agent's turn ends, and that the resolved state survives a reload.

Supporting test infrastructure:
- data-testid hooks on PermissionActionRow.
- permissionActionRows() / permissionApproveButtons() on SessionPage.
- backend fixture force-disables AGENTCTL_AUTO_APPROVE_PERMISSIONS=false
  so a developer's local AUTO_APPROVE=true env doesn't silently bypass
  the permission UI in tests that exercise the approve/reject flow.

* test(backend): cover GetMessageByToolCallID permission_request exclusion

Addresses claude-review feedback on the approvals fix:
- Adds a regression test that seeds both a permission_request and a
  tool_call with the same tool_call_id (permission_request created
  first, so an ORDER BY created_at ASC sweep without the type filter
  would race to it) and asserts the lookup returns the tool_call row.
- Bumps the applyToolCallMessageUpdate invariant guard from Warn to
  Error: reaching that branch means a caller bug, not a transient
  condition, and Warn would silently swallow the violation.

* fix(e2e): scope auto-approve override to permission-approval test only

The previous fixture force-disabled AGENTCTL_AUTO_APPROVE_PERMISSIONS
globally, which broke session-isolation.spec.ts and other tests that
use scenarios with permission-required tools (e.g. /e2e:read-and-edit)
and rely on auto-approve to let the agent finish without UI clicks.

Default the env to "true" in the fixture; let permission-approval.spec
flip it to "false" via a backend.restart() override in beforeAll, then
restore in afterAll. Adds an envOverrides parameter to backend.restart.
2026-05-01 00:25:51 +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
Carlos Florêncio 74d44bd26b feat(acp): support claude-acp Monitor tool and fix incremental tool_call updates (#698) 2026-04-25 16:26:20 +01:00
Carlos Florêncio 66f0cc2a16 fix(web): persist agent profile mode through bulk-edit save (#626) 2026-04-19 11:34:43 +01:00
Carlos Florêncio 181a64a5fb fix: prevent utility agents settings page crash on null models (#602) 2026-04-17 14:22:34 +01:00
Carlos Florêncio 2ae996a2c1 fix(agentctl): register MCP tools with _kandev suffix to match sysprompt (#572) 2026-04-10 17:17:42 +01:00
Carlos Florêncio e519d182fb chore: add dedicated sub-agents for skill delegation (#497) 2026-03-30 10:17:22 +01:00
Carlos Florêncio 3a50cf42e5 fix(web): show both send and cancel buttons when agent is busy (#487) 2026-03-29 22:13:39 +01:00
Carlos Florêncio 116895b4ff fix: resolve symlinks in file tree so symlink-to-directory entries show as folders (#467) 2026-03-25 23:00:05 +00:00
Carlos Florêncio df70eee749 fix(web): merge chat status bar into single row and switch task on archive (#460) 2026-03-24 23:22:20 +00:00
José Almeida f06577e989 fix: include uncommitted changes in review dialog cumulative diff (#447)
* fix: include uncommitted changes in review dialog cumulative diff

The review dialog only showed uncommitted changes instead of the full
diff from the task's base commit. Two issues combined:

1. Backend used `git diff base..HEAD` (committed only); changed to
   `git diff base` which compares against the working tree.
2. Frontend gave priority to git status over cumulative diff; reversed
   the merge order so cumulative diff takes precedence.

Adds E2E test with a mock-agent scenario that commits changes then
leaves uncommitted modifications, verifying the review dialog shows
the complete base→working-tree diff.

* fix(e2e): remove incorrect BASE_CONTENT assertion in review cumulative diff test

The file is created after the base commit, so the entire file shows as
added in the cumulative diff. BASE_CONTENT was replaced and doesn't
appear. The key assertion is that COMMITTED_CHANGE is visible — proving
the diff includes committed changes (not just the uncommitted portion).

* fix: address CodeRabbit review feedback

- Update 3 docstrings to reflect working-tree diff (not HEAD)
- Generate scenario list dynamically in error message
- Use data-testid selector in E2E test for resilience

* style: format E2E test file

* fix(e2e): use toContainText and click file to ensure diff is scrollable

Pierre diff viewer content inside a scroll container may not be
considered "visible" by Playwright. Switch to toContainText which
checks text presence without element-level visibility. Also click
the file in the tree to ensure its diff section is scrolled into view.
2026-03-18 00:24:10 +00:00
Carlos Florêncio bb0c63e73b fix: resolve model selector mismatch for ACP agents (#410) 2026-03-10 22:13:28 +00:00
Carlos Florêncio bb1caaba11 feat: add ACP agent variants for Claude, Codex, Copilot, and Amp (#387) 2026-03-10 17:30:04 +00:00
João Salavisa 815fb5b24e fix: refresh diff view when untracked files change (#372) 2026-03-05 15:18:30 +00:00
Carlos Florêncio a34693e993 feat: improve session recovery and context reset (#369) 2026-03-05 00:30:50 +00:00
Carlos Florêncio c73fa7471b fix: resolve clarification MCP timeout with cancel-and-resume flow (#362) 2026-03-05 00:18:57 +00:00
Carlos Florêncio 124fe588f8 feat: improve TUI agents session resume on restart (#367) 2026-03-04 23:34:26 +00:00
João Salavisa b0a85e6471 fix: restore git status update in workspace polling loop (#364) 2026-03-04 12:22:50 +00:00
José Almeida c79d138c45 feat(web): add diff expansion with expand-all in review panel (#340)
* feat(backend): add support for fetching file content at git ref

Add GetFileContentAtRef method to WorkspaceTracker for retrieving file content
at specific git refs (branches, commits, HEAD). This enables diff expansion by
fetching old file versions without checking out.

Changes:
- Extract maxFileSize constant to package level (10MB)
- Add GetFileContentAtRef to workspace_files.go using git show
- Add workspace.file.get_at_ref WebSocket action and handler
- Add agentctl client method for the new endpoint
- Register handler in workspace file handlers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(web): add diff expansion support for code review

Add lazy-loaded diff expansion to show more context in code reviews. When expansion
is enabled, users can expand hunks to see more surrounding code by clicking expand
buttons. Full file content is fetched on-demand via WebSocket.

Changes:
- Add useExpandableDiff hook for lazy-loading old/new file content
- Add requestFileContentAtRef to workspace-files.ts
- Integrate expansion into diff viewer components
- Add enableExpansion and baseRef props to DiffViewer
- Auto-load expansion content when enabled
- Pass expansion state through diff viewer hierarchy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(web): gracefully handle expansion props for Monaco diff viewer

Monaco diff viewer has built-in expansion that doesn't require fetching
old file content. Strip pierre-diffs-only expansion props (enableExpansion,
baseRef) when Monaco is selected to avoid passing unsupported props.

This ensures the feature works correctly regardless of which diff viewer
the user selects in settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(backend): add diff expansion e2e scenario and fix unstaged diff base ref

- Add scenarioDiffExpansionSetup mock-agent scenario that creates a
  200-line file, commits it, then modifies lines 50 and 150 to produce
  a two-hunk uncommitted diff for testing diff expansion in the UI
- Add workspace_file_handlers for workspace.file.get_at_ref WS action
- Fix enrichWithDiffData to always diff against HEAD for unstaged/staged
  content so locally committed files show correct diffs
- Add unit tests for GetFileContentAtRef (6 sub-tests)

* feat(web): add diff expansion UI and e2e test

- Wire useExpandableDiff hook into FileDiffViewer and ReviewDiffList
  with enableExpansion and baseRef props
- Gracefully handle expansion props for Monaco diff viewer fallback
- Add e2e Playwright test for diff expansion between hunks

* fix(web): fix diff expansion by correcting hunk separators and line splitting

Three bugs prevented @pierre/diffs expand buttons from rendering:
- hunkSeparators was "simple" (no expand UI); changed to "line-info" when expansion enabled
- expandUnchanged:true showed all lines expanded (no separators); removed the option
- .split("\n") stripped trailing newlines; now uses SPLIT_WITH_NEWLINES regex to match library

* feat(web): add editor provider settings UI

Add an "Editor Providers" section to the Editors settings page with
Select dropdowns for each configurable context (Code Editor, Diff Viewer,
Chat Code Blocks, Chat Diff Blocks). Reads from and writes to the
existing localStorage-backed editor resolver store. Changes apply
immediately without requiring a save action.

* test(web): add e2e tests for editor provider settings

Verify that the editor provider dropdowns render with correct defaults,
update on selection, and persist changes across page reloads via
localStorage.

* feat(web): add expand/collapse unchanged regions toggle to diff toolbar

Refactor diff header toolbar into composable ToolbarButtons component and
add a toggle button to expand or collapse unchanged regions in both
Pierre Diffs and Monaco diff viewers. Pass enableExpansion through to
Monaco to control hideUnchangedRegions.

* fix(web): handle deleted files in diff expansion

Treat "file not found" / "no such file" errors as empty content instead
of throwing, so diff expansion works correctly for deleted files.

* chore(web): add diff expansion debug e2e helpers

* test(web): split diff expansion tests by provider (Pierre Diffs + Monaco)

Restructure diff-expansion e2e tests into two describe blocks, one per
diff viewer provider. Each block sets the provider via localStorage
before navigation and verifies the correct viewer renders:

- Pierre Diffs: checks for diffs-container element and shadow DOM
  expand buttons between hunks
- Monaco: checks for .monaco-diff-viewer wrapper and the built-in
  hideUnchangedRegions widget with "Show Unchanged Region" link

Both suites verify that clicking expand reveals collapsed middle lines.

* fix(web): fix Pierre Diffs expand button selectors in e2e tests

Use shadow DOM data attributes ([data-expand-button], [data-expand-up])
instead of XPath parent traversal which doesn't work inside shadow DOM.
Wait for expand buttons to load asynchronously before clicking.

* fix(e2e): make diff-expansion-setup scenario idempotent

The mock-agent scenario now removes and re-commits the test file before
each run, so subsequent tests in the same repo don't fail with "git
commit failed" when the file already exists. Also increase test timeouts
to match other heavy e2e tests.

* refactor(web): remove editor provider settings UI, enable expand-all in review

Remove the editor provider switching UI and its tests — Monaco's diff
expansion doesn't work and exposing provider switching adds confusion.
Show Pierre Diffs header toolbar in the review panel (remove hideHeader)
so the expand-all button is accessible. Add e2e test for expand-all.

* style(web): fix prettier formatting and lint warnings

Run prettier on files with formatting drift. Extract ToolbarBtn helper
in diff-header-toolbar to stay within max-lines-per-function limit.
Consolidate useMonacoDiffViewerState to reduce line count.

* chore(web): remove debug scaffolding, revert Monaco changes, drop stray console.log

Revert Monaco diff-viewer-toolbar and monaco-diff-viewer to main
(expansion not functional with Monaco). Remove debug-expansion
screenshot scripts and spec. Strip console.log from commit-detail-panel.
Strip enableExpansion from Monaco path in diff-viewer-resolver.

* fix(web): use commit-relative base ref for diff expansion

Use `${commitSha}^` instead of hardcoded `HEAD~1` so expansion fetches
the correct old content when viewing non-HEAD commits.

* fix(web,backend): address CodeRabbit review feedback

- Add aria-label to icon-only toolbar buttons for screen reader access
- Guard auto-load expansion effect against error state to prevent retry
  loops when WebSocket fetch fails
- Invalidate expansion cache on file/ref change with request versioning
  to prevent stale content
- Preflight blob size with git cat-file -s before git show to avoid
  memory spikes on large files

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 00:05:39 +00:00
Carlos Florêncio ee59ccdfbb feat: tui agents with workflows and code quality improvements (#360) 2026-03-03 22:59:22 +00:00
Carlos Florêncio 2aab682cd8 fix: sidebar task switcher shows outdated session state (#349) 2026-03-02 20:15:48 +00:00
Carlos Florêncio 44811da702 feat: web e2e tests (#304) 2026-03-02 08:54:58 +00:00
Carlos Florêncio b3f1eaa037 feat: mermaid support (#293) 2026-02-26 13:24:40 +00:00
Carlos Florêncio 61ef224ec7 style: fix format issues (#255) 2026-02-19 22:18:01 +00:00
Carlos Florêncio fd4ae1eb54 fix: resolve all backend golangci-lint violations (#245) 2026-02-18 18:24:24 +00:00
Carlos Florêncio 4d41155158 feat: mock agent (#208) 2026-02-07 17:48:04 +00:00