* feat: add automation system with triggers, backend-driven metadata, and PR association
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(automations): execution_mode + flat /settings/automations
Layered on top of #406's automation subsystem:
- Add execution_mode to Automation (task | run, default task). Run-mode
firings create an ephemeral kanban-hidden task tagged with
origin=automation_run. The session pipeline still runs against it, but
users surface them through the AutomationRun history instead of the
kanban. Pragmatic shim while AutomationRun-as-true-session-owner is
deferred to a follow-up.
- Thread IsEphemeral + Origin through ReviewTaskRequest +
reviewTaskCreatorAdapter so the orchestrator can create the hidden
task in one call.
- Run-mode always auto-starts (the user never sees the task to drag).
- Flat /settings/automations route replaces the per-workspace sidebar
entry. Picks the only workspace, shows a picker for many, or shows
a CTA for zero workspaces.
- Editor exposes execution mode + table shows it as a badge column.
Spec at docs/specs/office/automations-settings.md documents the design,
deferred items, and scenarios.
* feat(automations): per-automation repository picker
Adds a repository_id field on Automation so users can pin scheduled and
webhook automations to a specific workspace repository instead of always
falling through to "first repo". github_pr triggers ignore the field and
keep using the PR's own repo — the editor disables the picker with a
hint when a PR trigger is selected.
Backend:
- repository_id column folded into the canonical CREATE TABLE (no
migration needed — PR #406 introduces the table).
- RepositoryID threaded through model, DTOs, store INSERT/UPDATE, and
service.CreateAutomation.
- resolveAutomationRepository prefers the explicit ID, falls back to
workspace-first on empty.
Frontend:
- repository_id on the Automation TS type + Create/Update payloads.
- Repository SelectField in ConfigSection backed by useRepositories;
"Auto" sentinel represents the empty/fallback choice.
- Disabled with help-text for github_pr triggers.
* feat(automations): repo picker shows discovered repos too
Matches the task-creation dialog's UX: the editor's repository picker
now lists registered workspace repos AND filesystem-discovered repos
under the workspace's roots. Picking a discovered repo registers it
with the workspace at save time (via createRepositoryAction), captures
the new id, and writes it onto the automation. After the first save
the picker selection is promoted from "discovered" to "registered" so
re-saving doesn't re-register.
Form state holds a RepositorySelection union (auto | registered |
discovered) rather than a plain id string so the picker can carry the
metadata needed to register.
github_pr triggers still disable the picker — PR events always override
any configured repo with the PR's own.
* fix(automations): add idempotent ALTERs for in-branch columns
The canonical CREATE TABLE covers fresh installs, but `CREATE TABLE
IF NOT EXISTS` is a no-op for DBs already initialised from an earlier
commit on this branch (PR #406's original schema). Without these
ALTERs, dev DBs created before execution_mode + repository_id existed
on the row hit "no such column" errors.
Three idempotent ALTERs: task_title_template, execution_mode,
repository_id. SQLite returns a duplicate-column error when the column
already exists; we swallow it.
* fix(automations): use generateUUID in non-secure contexts
crypto.randomUUID is only defined in secure contexts (HTTPS or
localhost). PR #406's editor used it directly, which throws "crypto.
randomUUID is not a function" when the app is reached via a plain
hostname like nova:58204. Switch to lib/utils.generateUUID, which
falls back to a Math.random-based implementation when crypto isn't
available.
* fix(automations): polish run-mode lifecycle, validation, and editor UX
Run-mode automations were partly broken end-to-end:
- workflow_id was sent for ephemeral tasks (rejected by task service)
and was required by the editor + automation service, so "Create
Automation" was always disabled and silently failed
- a permission_request hanging the agent left the run pinned at
task_created forever; nothing reaped the ephemeral worktree
- trigger config inputs (cron, branches, authors) did not re-sync
with the loaded automation on page reload
- runs row never linked back to the underlying task for task-mode
automations, and the table header highlighted on hover
Backend
- skip workflow fields in CreateReviewTask / StartTask for run-mode
- relax automation.Service.CreateAutomation: workflow only required
for task-mode
- auto-reject permission prompts on ephemeral automation tasks and
mark the AutomationRun failed with the requested action
- on agent completion / failure for ephemeral automation tasks,
flip the run to succeeded/failed and remove the worktree + branch
immediately (new RunStatusSucceeded + MarkRunSucceededByTaskID)
- wire worktree.Manager into the orchestrator service
Frontend
- show Execution Mode first; hide Workflow / Workflow Step in run-mode
- canSave no longer requires workflow in run-mode
- surface save errors via toast instead of silent failure
- sync trigger-config local input state with loaded prop value
- runs table: clickable row → /tasks/<id> for task-mode runs,
non-clickable in run-mode; suppress header hover
* fix(automations): address PR review feedback
Backend
- automation/scheduler.go: parseCronFields returns an error for
unsupported expressions instead of silently defaulting to hourly
- automation/webhook.go: constant-time secret compare, header-only
(no more ?secret= query param), reject with 409 when no enabled
webhook trigger exists
- automation/service.go: enforce max_concurrent_runs in FireTrigger
via maybeSkipForConcurrencyCap (records a skipped run when capped)
- cmd/kandev/e2e_reset.go: surface automation delete errors as 500
instead of swallowing them
- orchestrator/event_handlers_git.go: auto-reject permission with
cancelled=true so the message status is "rejected" and the session
isn't flipped back to running
- orchestrator/event_handlers_agent.go: finalizeAutomationRunIfEphemeral
now runs at the top of handleAgentFailed, before the resume/recoverable
early returns, so session-backed run-mode failures get cleaned up
Frontend
- state/slices/automations/automations-slice.ts: declare _get/_api so
the call site's arity matches and CodeQL stops flagging ignored
trailing arguments
- automations/schedule-selector.tsx: clearing the input now propagates
""; cron is trimmed before validation+save
- automations/triggers-section.tsx: switching to webhook mode deletes
the existing scheduled/condition triggers so they don't keep firing
hidden in the background
- automations/trigger-configs/github-ci-config.tsx: useEffect re-sync
for checkNames, matching the other trigger configs
- hooks/domains/settings/use-automations.ts: per-workspace fetch via
loadedWorkspaceRef + render-safe loadedWorkspaceId state;
inFlightWorkspaceRef discards stale responses after workspace switch
---------
Co-authored-by: Carlos Florêncio <carlosmflorencio@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Kandev Agent <agent@kandev.dev>
* refactor: move integrations to top-level settings with install-wide configs
Jira and Linear configurations are now install-wide singletons rather than
per-workspace duplicates, and the GitHub/Jira/Linear settings pages live
under /settings/integrations instead of being nested inside each workspace.
Issue and PR watchers stay per-workspace via a workspace switcher inside
each integration page. Backend ships one-shot migrations that promote the
most-recently-updated row and copy its secret onto the new global key.
* fix: address review feedback on integrations refactor
Fix migration crash on installs whose jira_configs predate the auth-health
columns by selecting only guaranteed-present legacy columns and falling back
to defaults for the rest. Cover the empty-legacy-table and pre-health-columns
upgrade paths with new store tests.
On the frontend, refactor useIntegrationEnabled to take string parameters
instead of an inline options object so getSnapshot stays referentially stable
across renders, and stop dispatching a misleading no-op sync event when
localStorage.setItem fails in private mode.
* fix: hydrate workspace list on integration settings pages
The integration pages wrapped their content in a fresh StateProvider, which
created a nested store scope with an empty initial state and shadowed the
settings layout's hydrated workspace list. WorkspaceScopedSection then saw
zero workspaces and rendered the "Create a workspace to configure this
integration" empty state even when the user had workspaces.
Drop the redundant StateProvider from the jira and linear pages so they share
the layout's store. The github page switches to StateHydrator so it can still
seed the GitHub OAuth status without resetting the rest of the store.
* refactor(integrations): flatten watcher tables, pick workspace in create dialog
Hiding watchers behind a workspace switcher made the list invisible at a
glance — a user with multiple workspaces had to flip between each one to
see what was configured. Replace the switcher with a flat table that lists
every watch across every workspace, with a Workspace column for context, and
move workspace selection into the create dialog so the user picks once when
the watcher is born.
Backend: jira list-issue-watches and github list-review-watches /
list-issue-watches accept the workspace_id query param as optional; when
omitted, the corresponding store ListAll* method returns rows across every
workspace. Per-row mutations still require workspace_id as the IDOR guard.
Frontend: useReviewWatches / useIssueWatches / useJiraIssueWatches gain a
three-state contract — string for one workspace, undefined for all
workspaces, null to skip fetching. Watch tables grow an optional
showWorkspace column. Watch dialogs accept an optional workspaceId prop;
when absent, the dialog renders a workspace dropdown for the create flow
(disabled when editing existing rows so refs aren't orphaned). Github
review-watch-dialog crossed the 600-line lint cap so the prompt editor
moved to review-watch-prompt-field.tsx.
* fix(integrations): make watcher step dropdown reactive across workspace switches
Switching to a workspace whose workflows had to be fetched left the workflow
step dropdown stuck — the user could pick a workflow but the step Select
either showed the previous workflow's steps or appeared inert. The cause was
the per-dialog useWorkflowSteps hook deferring its empty-state reset via
Promise.resolve().then(() => setSteps([])) to dodge a setState-in-effect
lint rule, which let stale steps survive into the next render and gave Radix
nothing to anchor the popover to during the in-flight fetch.
Hoist the hook into a shared useWorkflowSteps that resets via React's
official setState-during-render pattern, exposes a loading flag, and returns
a stepPlaceholder helper so the Select can render "Loading steps…" or "No
steps in this workflow" instead of an empty popover. The Step Select is now
also disabled while loading or when there are no steps, making the state of
the dropdown unambiguous.
* fix: address PR review feedback on integrations refactor
Backend:
- linear migration: add column-presence guard mirroring the Jira fix so
installs that upgraded from the original Linear schema (without org_slug
/ last_checked_at / last_ok / last_error) don't crash startup with
"no such column"; add TestStore_MigrateLegacyPerWorkspaceTable_PreHealthColumns
- jira/handlers.go: drop the now-unused errMsgWorkspaceIDRequired constant
that was failing golangci-lint
- jira/mock_controller_test.go (new): cover setAuthHealth happy-path and
invalid-JSON-rejection so the singleton-shaped payload is locked in
Frontend:
- useIntegrationAvailable: gate the auth-health probe on loaded && enabled
so disabled integrations don't poll /config every 90s
- use-workflow-steps: initialize loading to !!workflowId so edit-mode opens
show "Loading steps…" instead of "No steps in this workflow" before the
first fetch lands
- use-workflow-steps.test.ts (new): cover the empty/non-empty workflowId,
workflow switch, fetch error, and stepPlaceholder branches
- use-linear-enabled.test: tighten the legacy-key migration assertion to
pin the migrated value rather than just verifying key cleanup
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
* feat: add Linear integration
Mirrors the existing Jira integration so a workspace can connect to Linear
with a personal API key, browse and search issues, and start a Kandev task
from any issue. Backend uses Linear's GraphQL API with a workspace-scoped
config + secret store and a 90s auth-health poller; the frontend adds a
settings page, an issue dialog with state transitions, link/import buttons
in the task UI, and a paginated /linear browse page with a Start task flow.
* fix(linear): address PR review feedback
- Route httpListStates through Service.ListStates instead of clientFor
- Extract repeated workspace_id required string into a constant
- Log GetConfig errors during credential resolution instead of dropping them
- Clear popover error state on close in import + link buttons
- Pick the first workflow with steps in the quick task launcher (don't hard-pin to workflows[0])
- Reset useLinearEnabled state on workspace change via tag-based pattern
- Add cursor-pointer to anchor + Link elements per project convention
- Add tests: handlers_test.go, linear-api.test.ts, use-linear-enabled.test.ts, linear-issue-common.test.tsx
- Document internal/jira and internal/linear in CLAUDE.md/AGENTS.md
* fix(linear): address second-round PR review feedback
- Round Linear's fractional state position instead of truncating it via
int(math.Round(s.Position)) so adjacent fractional positions don't collapse
- Force LinearSettings to remount on workspace change with key={id} so the
previous workspace's config / form / testResult / teams cannot bleed into
the next workspace before the new fetches settle
- Broaden the legacy-value useLinearEnabled test to cover "true", "1", "yes",
and "legacy" so a regression to strict value === "true" would fail
* fix(linear): address third-round PR review feedback
- Propagate context.Canceled / context.DeadlineExceeded from ListStates in
GetIssue instead of swallowing them — a caller cancellation must not return
a partial-success response
- Drop the one-off contains() test helper in handlers_test.go and use
strings.Contains directly so the assertion is self-documenting
- Add getLinearIssue URL-shape test that locks identifier-as-path-segment
vs workspaceId-as-query-param so accidentally swapping them would fail
* test(linear): extract repeated literals in linear-api.test.ts
Pulls "ws-1", the API base URL, and the auth-method literal into module-
level constants so the assertions read as the route shape they care about
and the duplicate-strings ESLint rule (currently warn, threshold 4)
stays quiet as the file grows.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
* feat(jira): add Jira integration for ticket browsing, import, and task linking
Adds a workspace-scoped Atlassian Cloud integration: per-workspace settings
(site URL, email, API token or session cookie), encrypted secret storage,
and a REST client for tickets, projects, search, and transitions. A
background poller probes credentials on a 90s cadence and the keepalive
runs immediately on save so the UI's connected indicator updates without
delay.
The /jira page provides ticket browsing with filter pills, custom JQL,
saved views (including JQL), and configurable task-launch presets that
prefill TaskCreateDialog. JiraLinkButton on the task top bar links an
existing task to a ticket by prepending its key to the title; the ticket
popover surfaces details, transitions, and re-auth prompts on session
expiry. Topbar shows separate icon buttons for GitHub and Jira when each
is configured.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(jira): address PR review feedback
- Token-based pagination (pageToken/isLast) replaces broken offset/total pagination
- Race-fix in service.clientFor: re-check cache after acquiring write lock
- sync.Mutex guards poller started/cancel state against concurrent Start/Stop
- Channel-based + atomic.Int32 test sync replaces time.Sleep polling in poller_test
- Async auth probe on SetConfig with detached context.Background goroutine
- Distinguish 4xx vs 5xx in httpSetConfig via ErrInvalidConfig sentinel
- Propagate 4xx APIErrors from ListTransitions in GetTicket
- Distinguish "not found" from real errors in jiraSecretAdapter.Exists
- Log secret Exists errors instead of swallowing
- walkADF: handle softBreak (was only hardBreak despite the doc-comment)
- Consolidate JIRA_KEY_RE in jira-ticket-common; remove duplicates
- ticket-row imports formatRelative/statusBadgeClass from common
- isFilterState type guard validates SavedView.filters shape
- task-presets readStorage preserves user's "cleared all" intent (returns [])
- task-presets-section gates draft sync on `loaded` to avoid wiping in-progress edits
- jira-api: caller `init` no longer overrides method/body via spread order
- jira-settings: disable Test button when no saved/typed secret
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(jira): record auth probe failure even when probe ctx expires
- RecordAuthHealth now uses a fresh context.Background-derived ctx with a
5s timeout for the UpdateAuthHealth DB write, so a probe that consumes
its full 15s deadline still persists the failure (greptile P1).
- SetConfig goroutine drops its redundant outer timeout — RecordAuthHealth
manages its own probe timeout, so context.Background is enough.
- Add Service.SetProbeHook so tests can synchronize on probe completion;
service_test waitForAuthProbe now blocks on the hook channel instead of
time.Sleep polling.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(jira): address coderabbit review feedback (round 3)
- url.PathEscape ticketKey in GetTicket/ListTransitions/DoTransition paths
to harden against malicious keys ('../../foo', 'KEY?override=x').
- factoryHit on the test fixture is now sync/atomic.Int32; the async probe
goroutine spawned by SetConfig races the test thread under -race.
- GetConfig logs Reveal failures (was silently dropped, leaving the UI to
show "no expiry info" indistinguishably from a freshly-saved cookie).
- DeleteConfig logs secret-delete errors instead of swallowing them.
- clientFor distinguishes a secret-store failure from "not configured" so
the UI doesn't render the configure-CTA for transient backend errors.
- Unify ErrNotConfigured wire code: HTTP and WebSocket both return
"JIRA_NOT_CONFIGURED" via a shared constant.
- JiraLinkButton strips a leading "PROJ-123: " before re-prefixing so
re-linking a task replaces the prefix instead of stacking it.
- jira-settings clears testResult on save/delete so the alert reflects
only the currently-saved credentials.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(jira): cover Reveal error path in clientFor
Reveal-error case is distinct from "no config row" — the former wraps
the underlying error, the latter returns ErrNotConfigured. Without
this test the changed contract has no regression coverage.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(jira): route secret adapter Set through Exists
Treating every Get error as "not found" turned a transient DB error on
an existing row into a constraint-violation Create that masked the real
cause. Reuse Exists so the "secret not found:" prefix is the only path
that falls through to Create.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(jira): cover jiraSecretAdapter.Set branching
Routing Set through Exists changed the contract: a transient DB error
in Get now propagates instead of cascading into a constraint-violation
Create. Add three tests so a future refactor cannot regress this without
CI catching it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(jira): split Reveal errors and guard Save for new configs
- resolveCredentials now mirrors the clientFor split: a Reveal error
surfaces as `read jira secret: %w` with a warn log instead of
collapsing into "no token stored — paste one to test".
- disableSave gains the same missing-secret guard as disableTest so a
brand-new config can no longer POST an empty secret and leave the
workspace stuck with hasSecret=false.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat(web): split file search from command panel and add inline task search
Separate the command panel (Cmd+K) from file search (Cmd+Shift+K) to
prevent file results from overpowering commands. Add inline task search
directly in the command panel — tasks appear in a "Tasks" group when
typing ≥2 characters, with workflow step badges and archived state
indicators. Archived tasks are sorted last with reduced opacity.
- Add "search-files" mode to CommandPanelMode
- Add FILE_SEARCH shortcut (Cmd/Ctrl+Shift+K)
- Move file search to dedicated mode, remove from commands mode
- Add useInlineTaskSearchEffect for commands mode task search
- Remove "Search Tasks" sub-mode entry point (now inline)
- Show file search shortcut hint in command panel footer
* feat(web): add configurable keyboard shortcuts in general settings
Add localStorage-based shortcut override system for the command panel
and file search keybindings. Users can record new key combos via the
settings UI and reset to defaults.
- Add shortcut-overrides module (get/set/reset via localStorage)
- Add KeyboardShortcutsCard with shortcut recorder UI
- Add "Keyboard Shortcuts" section to general settings page
* test(web): add e2e tests for command panel and file search split
Cover the key use cases: Cmd+K opens commands without files,
Cmd+Shift+K opens file search, inline task search with workflow step
badges, escape/backspace navigation, and panel toggle behavior.
* fix(test): remove unused seedData variable in command panel e2e test
* feat(settings): persist keyboard shortcuts to user settings API
Replace localStorage-based shortcut overrides with server-persisted
storage via the user settings API. Adds keyboard_shortcuts field to
the backend model, DTO, store, service, and controller. Frontend
reads overrides from Zustand store (hydrated from SSR/WS) and saves
via PATCH /api/v1/user/settings.
* fix(ui): prioritize tasks over commands, add step colors, fix shortcut display
- Move task results above command results in cmd+K for faster task switching
- Replace magnifier icon with task icon (IconSubtask) for task results
- Apply workflow step color to task badges
- Fix formatShortcut early return bug that dropped Shift modifier
when ctrlOrCmd was set (e.g. Cmd+Shift+K showed as Cmd+K)
- Fix same bug in shortcutToCodeMirrorKeybinding
* fix(ui): use clipboard icon for task results in command panel
* style: format command panel footer
* fix(ui): use hammer icon and map step colors to hex for task badges
* feat(ui): show repo directory and agent name on task results
Display secondary details (repository directory, agent name, last
updated) on the right side of task results in the command panel.
* fix: avoid inline selector closure to prevent getServerSnapshot loop
Select the raw repositories map from the store instead of using an
inline selector that closes over workspaceId and returns new array
references, which triggers React's getServerSnapshot infinite loop.
* feat(task): expose agent name and working directory in task DTO
Add primary_agent_name (from AgentProfileSnapshot) and
primary_working_directory (from RepositorySnapshot) to TaskDTO.
Extend the primary session info query to fetch agent_profile_snapshot
and repository_snapshot columns, and extract the new fields in the
handler layer.
* feat(ui): show agent name and working directory on command panel tasks
Replace executor name with agent name from the primary session and
display the working directory (with repository path fallback) in the
task result details.
* fix(ui): add Commands heading to separate tasks from commands in search
* fix(task): use profile display name instead of agent slug
* feat(workflow): add show_in_command_panel per-step configuration
Add a configurable boolean to each workflow step that controls whether
tasks in that step appear in the command panel (Cmd+K). Default
templates mark backlog/done steps as hidden, active steps as visible.
- Add show_in_command_panel to WorkflowStep, StepDefinition, StepPortable
- Add column with idempotent migration (DEFAULT 1 for backwards compat)
- Include field in DTO serialization and export/import round-trip
- Update all default templates (Plan & Build, Architecture, Kanban, PR Review)
- Add tests for export preservation and import round-trip
* feat(ui): wire show_in_command_panel through frontend and settings
- Add field to HTTP types (WorkflowStep, WorkflowStepDTO, StepDefinition)
- Add to kanban state types and KanbanUpdatePayload
- Map in SSR snapshot mapper and WS kanban handler
- Add "Show in command panel" checkbox to workflow step settings
- Extract StepCheckboxRow component to stay within lint limits
* feat(ui): filter command panel tasks by show_in_command_panel flag
Replace position-based heuristic (resolveExcludedStepIds) with the
per-step show_in_command_panel configuration. Tasks in steps where
the flag is false are hidden from the default Cmd+K view but still
accessible via search.
- Replace resolveExcludedStepIds with resolveVisibleStepIds
- Consolidate useInlineTaskSearchEffect params into options object
* fix(ui): differentiate task sections in command panel
Rename the inline task results heading to "Active Tasks" when showing
the default view, distinguishing it from the "Tasks" command group
that contains "Create New Task".
* fix(ui): keep auto-archive checkbox inline with other step options
* fix(ui): include show_in_command_panel in step update action
The server action was missing show_in_command_panel from its Pick type
and body builder, so the checkbox change was silently dropped.
* fix(ui): pass is_start_step and show_in_command_panel from template steps
Both normalizeWorkflowTemplate mappers (workflow-api.ts and
workspaces.ts) were only picking name/position/color/prompt/events
from backend template steps, dropping is_start_step and
show_in_command_panel. This caused all template steps to appear
with show_in_command_panel checked when creating a new workflow.
* fix(ui): fix command panel empty tasks and step update losing checkbox state
Two fixes:
- Command panel: when kanban steps haven't loaded yet, don't filter
tasks by visibility (resolveVisibleStepIds returns null for empty
steps instead of an empty Set that rejects everything)
- Workflow settings: BackendWorkflowStep type and transformWorkflowStep
were missing show_in_command_panel, so refreshing steps after an
update dropped the field and the checkbox always reverted
* fix(ui): prevent back button from stealing focus in command panel
The back button was focusable and appeared before CommandInput, so
opening file search mode focused the button instead of the input.
Add tabIndex={-1} to keep focus on the search input.
* feat(web): show archive icon for archived tasks in command panel
Replace the text badge (e.g. "COMPLETED") with an IconArchive for
archived tasks in the command panel search results. Active tasks
keep the existing IconHammer icon.
* style: fix Prettier formatting and extract useStepMaps hook
Fix Prettier formatting issues in command-panel.tsx and
workflow-pipeline-editor-panels.tsx. Extract useStepMaps hook
from useInlineTaskSearchEffect to stay within the 100-line
function limit.
* fix: address PR review feedback
- Validate keyboard shortcut payload shape before persisting (service.go)
- Normalize KeyboardShortcuts to {} on read path for legacy rows (sqlite.go)
- Deduplicate getRepoDir into getFileName (command-panel-footer.tsx)
- Show toast on shortcut persistence failure instead of swallowing (keyboard-shortcuts-card.tsx)
- Reset keyboard_shortcuts in E2E test fixture for deterministic tests (test-base.ts)
* feat: replace agent_type with agent_profile_id
- Replace agent_type field with agent_profile_id across backend and frontend
- Add ProfileResolver interface and StoreProfileResolver implementation
- Update task creation/update to use agent_profile_id
- Change default agent runtime from docker to standalone
- Fix workspace state types to include all required fields
- Fix websocket handlers type mismatches for environments, executors, workspaces
- Update integration tests to use agent_profile_id
* feat: pass model as CLI argument to agents
- Add ModelFlag field to AgentTypeConfig for specifying the CLI flag
- Set ModelFlag to '--model' for all agents (auggie, claude, codex, gemini, opencode, copilot)
- Update launchStandalone to append model flag to agent command
- Update launchDocker/buildContainerConfig to append model flag to container command
- Model is passed when both agent supports ModelFlag and profile has Model configured
* fix: make WebSocket message handling async to prevent blocking
When a long-running handler like orchestrator.prompt was executing,
it blocked the ReadPump loop, preventing other requests (like
workspace.tree.get) from being processed until the handler completed.
Now each message is handled in its own goroutine, allowing concurrent
message handling so file tree requests work while the agent is working.
* feat: show task description as first message in chat
- Add 'Task' label with distinct styling (lighter background, border)
- Pass taskDescription from page to TaskChatPanel component
- Differentiate from regular user messages visually
- Add typing indicator in TaskChatPanel that shows when agent is working
- Enhanced ToolCallCard component with expandable details section
- Backend now passes tool call args (kind, path, locations, raw_input) to comments
- Add generateUUID fallback for non-secure contexts (HTTP non-localhost)
- Configure Next.js allowedDevOrigins for cross-origin dev access
- Bind dev server to 0.0.0.0 for network access
Backend changes:
- Extract Kind, Locations, RawInput from ACP ToolCall updates
- Add Args field to ToolCallData in watcher
- Pass args through CommentCreator interface to comment metadata
Frontend changes:
- ToolCallCard shows file path, status icons, expandable args
- getToolIcon matches ACP ToolKind values (read, edit, search, execute)
- Typing indicator with animated dots when awaiting agent response
- Replace crypto.randomUUID with fallback utility across all files
- Add react-markdown with remark-breaks and remark-gfm for markdown rendering
- Improve WebSocket client with better reconnection handling
- Enhanced task chat panel with markdown support for comments
- Add comment type and metadata support for tool call rendering
- Improve connection status indicator
- Update task page with improved agent controls
- Add WebSocket router for better message handling
- Update state store with enhanced type safety