616bafa848
* fix(backend): allow IDLE office sessions to accept follow-up prompts The IDLE session state (introduced with the office feature) was missing from checkSessionPromptable's accept bucket, so any prompt to an IDLE office session fell into the default branch and returned ErrAgentPromptInProgress with the misleading message "agent is currently processing a prompt, session is in IDLE state" — even though IDLE means the agent process is in fact gone (torn down between turns) and the conversation is parked waiting to resume. Net effect before this fix: once an office session went IDLE it could not be re-engaged from either the UI composer or MCP message_task_kandev. The five IDLE sessions sitting in the local DB exhibit this exactly. Fix: - Add TaskSessionStateIdle to the accept bucket in checkSessionPromptable. ensureSessionRunning already does the right thing for IDLE: it detects the missing in-memory execution, finds the preserved executors_running row, and routes through ResumeSession (which uses the stored ACP resume token). - Introduce ErrSessionNotPromptable for STARTING / CREATED / FAILED / CANCELLED rejections. The previous code wrapped those in ErrAgentPromptInProgress, which is semantically wrong and breaks any caller doing errors.Is(err, ErrAgentPromptInProgress) checks (the MCP promptWithAutoResume retry path keys on a different sentinel, so this doesn't regress, but the categorisation is now honest). Tests: - TestPromptTask_IdleSession_IsPromptable — reproduces the bug end-to-end. - TestCheckSessionPromptable_StateMatrix — pins down every state's classification, including the IDLE→accept and the STARTING/CREATED/FAILED/CANCELLED → ErrSessionNotPromptable (NOT ErrAgentPromptInProgress) split. - TestEnsureSessionRunning_IdleSessionTriggersResume — confirms IDLE sessions reach ResumeSession (the second half of the recovery path). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: preserve requeue for STARTING/CREATED + drop time.Sleep in test Address PR #1119 review feedback from Greptile, cubic, and Claude: P1: ErrSessionNotPromptable broke the requeue guard in executeQueuedMessage and the auto-start retry loop. Pre-PR, STARTING/CREATED bucketed into ErrAgentPromptInProgress and matched isAgentPromptInProgressError, so queued messages hitting a booting session were retried; post-PR they would have been silently dropped (no DLQ — see TODO at event_handlers_agent.go). Introduce isSessionBusyError covering both sentinels and swap it into the two requeue call sites. Pinned by TestIsSessionBusyError. P2: Replace the 50ms time.Sleep in TestEnsureSessionRunning_IdleSessionTriggersResume with a deterministic state-watching poll (waits for STARTING from persistResumeState before flipping to WAITING_FOR_INPUT). No CI flake exposure, no timing assumption. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: replace test poll loop with ticker+select per AGENTS.md Address CodeRabbit + Claude review on PR #1119: the 5ms time.Sleep poll loop in the launchAgentFunc watcher goroutine flunked the backend convention of "channel-based synchronization over sleep-based waits." testing/synctest doesn't help here (SQLite repo runs real I/O goroutines that synctest can't observe as idle), so use a ticker + select instead, keeping the 5s timeout as the safety bound. 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>