Skip to content

Add AI-generated agent descriptions and fix HITL workflow cleanup - #5

Merged
nickmisasi merged 3 commits into
masterfrom
agents-dashboard-description
Feb 15, 2026
Merged

Add AI-generated agent descriptions and fix HITL workflow cleanup#5
nickmisasi merged 3 commits into
masterfrom
agents-dashboard-description

Conversation

@nickmisasi

@nickmisasi nickmisasi commented Feb 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Generate short AI-powered task descriptions (via bridgeclient) at agent creation time, displayed in the RHS dashboard instead of raw prompt text. Falls back gracefully to the prompt when the Agents plugin is unavailable.
  • Fix a bug where stopping or archiving a planner agent left the HITL workflow stuck in planning phase, permanently blocking new agents in the thread.
  • Add defensive stale-planner detection in the mention handler to retroactively unblock threads already in the stuck state.

Test plan

  • Trigger an agent via @cursor fix this in a thread with context -- verify the RHS card shows a generated description instead of "fix this"
  • Check a planner agent card -- should show a description instead of "[planner iteration 0]"
  • Verify detail view shows "Description" label with the summary
  • With no Agents plugin installed, verify the card falls back to showing the raw prompt
  • Stop a planner agent via the dashboard, then @cursor in the same thread -- should launch a new agent (not blocked)
  • Archive a running planner agent, then @cursor in the same thread -- should launch a new agent
  • go test ./server/... passes
  • cd webapp && npm test -- --watchAll=false passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Agents now include AI-generated short descriptions shown in the UI and included in real-time creation events; description used in agent cards/details.
    • Thread mentions during planning can queue feedback for the running workflow author.
    • Bot created on activation now includes a profile image.
  • Bug Fixes

    • HITL workflows are transitioned to rejected when agents are cancelled/archived.
    • Stale planning workflows are detected and cleaned up; poller skips recently-cancelled agents.
  • Tests

    • Added/updated tests for workflow lookup, planning feedback, polling, and workflow-phase propagation.

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>
@coderabbitai

coderabbitai Bot commented Feb 14, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Agent data model
server/store/kvstore/kvstore.go, webapp/src/types.ts
Add Description/description fields to AgentRecord, Agent, and AgentCreatedEvent for short AI-generated summaries.
API surface
server/api.go, server/api_test.go
Add Description to AgentResponse and populate it; tests updated to expect workflow lookup calls on cancel/archive.
Handlers & HITL logic
server/handlers.go, server/hitl.go, server/handlers_test.go
Generate and store descriptions during launch/planner/implementer flows; add isPlannerStale() and rejectWorkflowForAgent() and call rejection on cancel/archive and stale-planner cleanup; add tests for mention-in-thread planning feedback queuing/appending.
Polling & publishing
server/poller.go, server/poller_test.go
Poller re-reads agent from KV after status fetch and early-exits for terminal refreshed records; agent_created payload now includes description; tests adjusted for extra GetAgent calls and a canceled-agent skip case.
WebSocket & actions
webapp/src/actions.ts
websocketAgentCreated action payload now includes description with default ''.
UI: Agent card & detail
webapp/src/components/rhs/AgentCard.tsx, webapp/src/components/rhs/AgentDetail.tsx
Prefer description over prompt for display; add isAborted logic to suppress PhaseBadge/PhaseProgress when aborted and workflow not rejected/complete.
State reducer & tests
webapp/src/reducer.ts, webapp/src/reducer.test.ts
Update WORKFLOW_PHASE_CHANGED handling to conditionally update workflows and batch-propagate phase/iteration/workflow_id to related agents; add tests for propagation and no-op preservation.
Plugin activation
server/plugin.go
Pass plugin profile image path when creating bot on activate.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I nibble prompts and spin a brief line,

Five words to tell what this task will shine.
Stale planners hop off, workflows declined,
Tiny descriptions bloom, concise and kind.
Hooray — agents speak, and the burrow's aligned!

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the two main objectives: adding AI-generated agent descriptions and fixing HITL workflow cleanup when agents are stopped or archived.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch agents-dashboard-description

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
webapp/src/reducer.test.ts (1)

360-382: Consider asserting the workflow update alongside the agents ref check.
This makes the intent clear: workflow changes apply even when agent refs stay intact.

Suggested test enhancement
         const state = reducer(prevState, {
             type: WORKFLOW_PHASE_CHANGED,
             data: {
                 workflow_id: 'wf-1',
                 phase: 'plan_review',
                 planner_agent_id: 'nonexistent',
                 implementer_agent_id: '',
                 plan_iteration_count: 1,
                 updated_at: 7000,
             },
         });
 
+        expect(state.workflows['wf-1'].phase).toBe('plan_review');
+        expect(state.workflows['wf-1'].plan_iteration_count).toBe(1);
+
         // agents ref should be preserved (no unnecessary copy).
         expect(state.agents).toBe(prevState.agents);
server/handlers.go (3)

386-386: Consider using the unwrapped prompt for description generation.

At this point, promptText has already been wrapped with system instructions (line 297), so generateDescription receives the full <system-instructions>...</system-instructions><task>...</task> format. This adds unnecessary tokens and could affect the quality of the generated title.

Consider capturing the task prompt before wrapping:

♻️ Proposed fix
 // Step 5: Wrap prompt with system instructions for the Cursor agent.
+taskPromptForDescription := promptText // Save before wrapping for description generation
 promptText = p.wrapPromptWithSystemInstructions(promptText)

Then at line 386:

-agentRecord.Description = p.generateDescription(promptText)
+agentRecord.Description = p.generateDescription(taskPromptForDescription)

823-829: Reduce MaxGeneratedTokens for efficiency.

The description prompt asks for 5-10 words, but MaxGeneratedTokens: 2048 is excessive. A smaller limit (e.g., 64-128) would be more appropriate and efficient for generating a short title.

♻️ Proposed fix
 result, err := p.bridgeClient.AgentCompletion(agentID, bridgeclient.CompletionRequest{
   Posts: []bridgeclient.Post{
     {Role: "system", Message: descriptionPrompt},
     {Role: "user", Message: contextText},
   },
-  MaxGeneratedTokens: 2048,
+  MaxGeneratedTokens: 128,
 })

796-841: Consider extracting shared agent discovery logic.

The agent discovery pattern (GetAgents → find default → fallback to first) is duplicated between generateDescription and enrichPromptViaBridge (lines 752-773). Consider extracting a helper like getDefaultAgentID() (string, error) to reduce duplication.

♻️ Example helper extraction
// getDefaultAgentID discovers the default bridge agent, falling back to the first available.
// Returns empty string if no agents are available.
func (p *Plugin) getDefaultAgentID() string {
    if p.bridgeClient == nil {
        return ""
    }
    agents, err := p.bridgeClient.GetAgents("")
    if err != nil || len(agents) == 0 {
        return ""
    }
    for _, agent := range agents {
        if agent.IsDefault {
            return agent.ID
        }
    }
    return agents[0].ID
}

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
The command is terminated due to an error: unknown linters: 'modernize', run 'golangci-lint help linters' to see the list of supported linters


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 (ApprovedContextEnrichedContextOriginalPrompt) is duplicated here and in launchImplementerFromWorkflow (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.

promptText is 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)

Comment thread server/handlers.go
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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 reducing MaxGeneratedTokens for short outputs.

The prompt requests 5-10 words, but MaxGeneratedTokens: 2048 allows 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,
 	})

Comment thread webapp/src/reducer.test.ts
Comment thread webapp/src/reducer.test.ts
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>
@nickmisasi
nickmisasi merged commit 0ef39ad into master Feb 15, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant