Add AI-generated agent descriptions and fix HITL workflow cleanup - #5
Conversation
Generate short task descriptions via bridgeclient at agent creation time, replacing raw prompt text in the RHS dashboard. Descriptions fall back to the prompt when the Agents plugin is unavailable or the LLM call fails. Also fix a bug where stopping or archiving a planner agent left the HITL workflow stuck in "planning" phase, blocking new agents in the thread. Both cancel and archive now transition associated workflows to rejected, and the mention handler detects stale planners retroactively. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds an optional AI-generated Description to Agent records/responses, populates it on agent creation/launch, rejects HITL workflows on cancel/archive or stale planners, updates poller/WebSocket payloads, and surfaces Description in the web UI with corresponding test updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Handler as launchNewAgent()
participant Generator as generateDescription()
participant Bridge as BridgeClient
participant KV as KV Store
participant WS as WebSocket
Client->>Handler: Create agent with prompt
Handler->>KV: Save initial AgentRecord
Handler->>Generator: generateDescription(prompt)
Generator->>Bridge: List agents / request short description
Bridge-->>Generator: Description text
Generator-->>Handler: Description (truncated)
Handler->>KV: Update AgentRecord.Description
Handler->>WS: publish agent_created (includes description)
WS-->>Client: WebSocket event with description
sequenceDiagram
participant User
participant Thread as handleMentionInThread()
participant WorkflowStore as Workflow KV
participant PlannerChecker as isPlannerStale()
participant Rejecter as rejectWorkflowForAgent()
participant AgentStore as Agent KV
participant Publisher as EventPublisher
User->>Thread: Mention in HITL planning thread
Thread->>WorkflowStore: Get workflow by thread
WorkflowStore-->>Thread: Workflow (planning)
Thread->>PlannerChecker: Check planner agent status
PlannerChecker->>AgentStore: Get planner AgentRecord
AgentStore-->>PlannerChecker: Agent status
alt Planner is stale
PlannerChecker-->>Thread: stale
Thread->>Rejecter: rejectWorkflowForAgent(plannerID)
Rejecter->>WorkflowStore: Get workflow by agent
Rejecter->>WorkflowStore: Set phase=rejected, persist
Rejecter->>Publisher: Publish phase change
Thread-->>User: No reply (workflow cleaned up)
else Planner active
PlannerChecker-->>Thread: active
alt mention from workflow user
Thread->>WorkflowStore: Append PendingFeedback, SaveWorkflow
Thread->>Publisher: Notify feedback queued
Thread-->>User: Reply "Feedback will be applied"
else
Thread-->>User: Reply "Planning agent is running"
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 🧹 Recent nitpick comments
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.5.0)Error: unknown linters: 'modernize', run 'golangci-lint help linters' to see the list of supported linters Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@server/handlers.go`:
- Around line 144-152: The current branch that handles mentions during planning
returns after posting "please wait" and never queues the user's parsed prompt;
update the branch where workflow.Phase == kvstore.PhasePlanning (inside the
handler that calls p.isPlannerStale and p.postBotReply) to push the parsed
prompt into the workflow's PendingFeedback (HITLWorkflow.PendingFeedback) using
the same enqueue mechanism used by non-mention replies, and ensure the pending
feedback will trigger a planner iteration when the current planner finishes;
keep the existing stale check path (p.rejectWorkflowForAgent) intact, and after
enqueueing optionally call p.postBotReply to acknowledge the feedback was
queued.
🧹 Nitpick comments (2)
server/hitl.go (1)
326-333: Consider extracting description context resolution to a helper.The same cascading logic (
ApprovedContext→EnrichedContext→OriginalPrompt) is duplicated here and inlaunchImplementerFromWorkflow(lines 745-752). A small helper would reduce duplication and ensure consistency.♻️ Optional refactor
// getDescriptionContext returns the best available context for description generation. func (wf *kvstore.HITLWorkflow) getDescriptionContext() string { if wf.ApprovedContext != "" { return wf.ApprovedContext } if wf.EnrichedContext != "" { return wf.EnrichedContext } return wf.OriginalPrompt }Then use:
- descCtx := workflow.ApprovedContext - if descCtx == "" { - descCtx = workflow.EnrichedContext - } - if descCtx == "" { - descCtx = workflow.OriginalPrompt - } - agentRecord.Description = p.generateDescription(descCtx) + agentRecord.Description = p.generateDescription(workflow.getDescriptionContext())server/handlers.go (1)
366-366: Generate descriptions from the unwrapped prompt to avoid system-instruction leakage.
promptTextis wrapped with system instructions before this call, so the description model may summarize the instructions instead of the task. Capture the pre-wrapped prompt for description generation.♻️ Suggested tweak
- // Step 5: Wrap prompt with system instructions for the Cursor agent. - promptText = p.wrapPromptWithSystemInstructions(promptText) + // Step 5: Wrap prompt with system instructions for the Cursor agent. + descriptionSource := promptText + promptText = p.wrapPromptWithSystemInstructions(promptText) ... - agentRecord.Description = p.generateDescription(promptText) + agentRecord.Description = p.generateDescription(descriptionSource)
Two fixes: 1. Webapp: WORKFLOW_PHASE_CHANGED now propagates phase to agents in Redux state, so the AgentCard PhaseBadge updates in real-time without requiring a full page refresh. Matches agents by workflow_id or by planner/implementer agent IDs from the event data. 2. Server: Poller re-reads agent record from KV after the Cursor API call to detect concurrent changes (e.g., cancel handler setting status to STOPPED). If the fresh record is already terminal, the poller skips processing, preventing a race where it would overwrite a cancellation with "Agent finished!". Also adds feedback queueing for @cursor mentions during planning phase. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@webapp/src/reducer.test.ts`:
- Around line 376-380: Add a blank line before the inline comment that starts
"// agents ref should be preserved (no unnecessary copy)." in the test
containing the expect(state.agents).toBe(prevState.agents); — locate the test
block around the reducer.test.ts function where }); is followed by that comment
and insert an empty line between the closing "});" and the comment so the linter
no longer flags a missing blank line.
- Around line 330-334: Add a blank line before the inline comment "// Unrelated
agent should not be touched." in the test assertions block (near the expects for
state.agents['planner-1'] in reducer.test.ts) so the linter rule about a blank
line before comments is satisfied; place an empty line between the
expect(state.agents['planner-1'].plan_iteration_count).toBe(1); statement and
the comment.
🧹 Nitpick comments (1)
server/handlers.go (1)
822-828: Consider reducingMaxGeneratedTokensfor short outputs.The prompt requests 5-10 words, but
MaxGeneratedTokens: 2048allows far more. A smaller value (e.g., 64-128) would be sufficient and could improve response times. This is a minor optimization.♻️ Suggested change
result, err := p.bridgeClient.AgentCompletion(agentID, bridgeclient.CompletionRequest{ Posts: []bridgeclient.Post{ {Role: "system", Message: descriptionPrompt}, {Role: "user", Message: contextText}, }, - MaxGeneratedTokens: 2048, + MaxGeneratedTokens: 64, })
Pass ProfileImagePath to EnsureBot so the cursor bot displays the proper Cursor logo instead of the default robot icon. Also fix pre-existing lint issues: gocritic ifElseChain in handlers.go and missing blank lines before comments in reducer.test.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
planningphase, permanently blocking new agents in the thread.Test plan
@cursor fix thisin a thread with context -- verify the RHS card shows a generated description instead of "fix this"@cursorin the same thread -- should launch a new agent (not blocked)@cursorin the same thread -- should launch a new agentgo test ./server/...passescd webapp && npm test -- --watchAll=falsepasses🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests