fix: Switch auto-spawn from discuss to refine agent, surface in UI
The auto-spawned agent on initiative creation was using discuss mode (Q&A) when it should use refine mode (expand content). Now: - Description seeds root page as tiptap content (split on double newlines) - Spawns refine agent with the populated page in inputContext - getActiveRefineAgent broadened to also surface discuss agents (for CLI-spawned discuss agents) - RefineAgentPanel shows mode-appropriate label for discuss vs refine
This commit is contained in:
@@ -167,10 +167,12 @@ export function agentProcedures(publicProcedure: ProcedureBuilder) {
|
||||
.query(async ({ ctx, input }): Promise<AgentInfo | null> => {
|
||||
const agentManager = requireAgentManager(ctx);
|
||||
const allAgents = await agentManager.list();
|
||||
// Surface discuss and refine agents — both work on initiative content
|
||||
const CONTENT_MODES = ['discuss', 'refine'];
|
||||
const candidates = allAgents
|
||||
.filter(
|
||||
(a) =>
|
||||
a.mode === 'refine' &&
|
||||
CONTENT_MODES.includes(a.mode) &&
|
||||
a.initiativeId === input.initiativeId &&
|
||||
['running', 'waiting_for_input', 'idle', 'crashed'].includes(a.status) &&
|
||||
!a.userDismissedAt,
|
||||
|
||||
@@ -35,7 +35,7 @@ export function deriveInitiativeActivity(
|
||||
}
|
||||
|
||||
// Check for active architect agents BEFORE zero-phases check
|
||||
// so auto-spawned discuss/plan/refine agents surface activity
|
||||
// so architect agents (discuss/plan/detail/refine) surface activity
|
||||
const activeAgent = activeArchitectAgents?.find(
|
||||
a => a.initiativeId === initiative.id
|
||||
&& (a.status === 'running' || a.status === 'waiting_for_input'),
|
||||
|
||||
@@ -7,7 +7,8 @@ import { z } from 'zod';
|
||||
import type { ProcedureBuilder } from '../trpc.js';
|
||||
import { requireAgentManager, requireInitiativeRepository, requireProjectRepository, requireTaskRepository } from './_helpers.js';
|
||||
import { deriveInitiativeActivity } from './initiative-activity.js';
|
||||
import { buildDiscussPrompt } from '../../agent/prompts/index.js';
|
||||
import { buildRefinePrompt } from '../../agent/prompts/index.js';
|
||||
import type { PageForSerialization } from '../../agent/content-serializer.js';
|
||||
|
||||
export function initiativeProcedures(publicProcedure: ProcedureBuilder) {
|
||||
return {
|
||||
@@ -47,38 +48,57 @@ export function initiativeProcedures(publicProcedure: ProcedureBuilder) {
|
||||
await projectRepo.setInitiativeProjects(initiative.id, input.projectIds);
|
||||
}
|
||||
|
||||
// Create root page — seed with description as tiptap content if provided
|
||||
const descriptionText = input.description?.trim();
|
||||
let rootPage: { id: string; parentPageId: string | null; title: string; content: string | null; sortOrder: number } | null = null;
|
||||
if (ctx.pageRepository) {
|
||||
await ctx.pageRepository.create({
|
||||
const tiptapContent = descriptionText
|
||||
? JSON.stringify({
|
||||
type: 'doc',
|
||||
content: descriptionText.split(/\n{2,}/).map(para => ({
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: para.trim() }],
|
||||
})).filter(p => p.content[0].text),
|
||||
})
|
||||
: null;
|
||||
|
||||
rootPage = await ctx.pageRepository.create({
|
||||
initiativeId: initiative.id,
|
||||
parentPageId: null,
|
||||
title: input.name,
|
||||
content: null,
|
||||
content: tiptapContent,
|
||||
sortOrder: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-spawn discuss agent when description is provided
|
||||
if (input.description?.trim() && ctx.agentManager && ctx.taskRepository) {
|
||||
// Auto-spawn refine agent when description is provided
|
||||
if (descriptionText && rootPage && ctx.agentManager && ctx.taskRepository) {
|
||||
try {
|
||||
const taskRepo = requireTaskRepository(ctx);
|
||||
const agentManager = requireAgentManager(ctx);
|
||||
|
||||
const task = await taskRepo.create({
|
||||
initiativeId: initiative.id,
|
||||
name: `Discuss: ${initiative.name}`,
|
||||
description: input.description.trim(),
|
||||
category: 'discuss',
|
||||
name: `Refine: ${initiative.name}`,
|
||||
description: descriptionText,
|
||||
category: 'refine',
|
||||
status: 'in_progress',
|
||||
});
|
||||
|
||||
const prompt = buildDiscussPrompt();
|
||||
const pages: PageForSerialization[] = [{
|
||||
id: rootPage.id,
|
||||
parentPageId: null,
|
||||
title: rootPage.title,
|
||||
content: rootPage.content,
|
||||
sortOrder: 0,
|
||||
}];
|
||||
|
||||
agentManager.spawn({
|
||||
taskId: task.id,
|
||||
prompt,
|
||||
mode: 'discuss',
|
||||
prompt: buildRefinePrompt(),
|
||||
mode: 'refine',
|
||||
initiativeId: initiative.id,
|
||||
inputContext: { initiative, task },
|
||||
inputContext: { initiative, pages },
|
||||
});
|
||||
} catch {
|
||||
// Fire-and-forget — don't fail initiative creation if agent spawn fails
|
||||
|
||||
@@ -123,7 +123,7 @@ export function CreateInitiativeDialog({
|
||||
<Label htmlFor="initiative-description">
|
||||
Description{" "}
|
||||
<span className="text-muted-foreground font-normal">
|
||||
(optional — spawns a discuss agent)
|
||||
(optional — seeds root page and spawns refine agent)
|
||||
</span>
|
||||
</Label>
|
||||
<Textarea
|
||||
|
||||
@@ -66,11 +66,12 @@ export function RefineAgentPanel({ initiativeId }: RefineAgentPanelProps) {
|
||||
|
||||
// Running
|
||||
if (state === "running") {
|
||||
const runningLabel = agent?.mode === 'discuss' ? 'Architect is discussing...' : 'Architect is refining...';
|
||||
return (
|
||||
<div className="mb-3 flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2">
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin text-primary" />
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Architect is refining...
|
||||
{runningLabel}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -141,7 +141,7 @@ Configured in `src/lib/trpc.ts`. Uses `@trpc/react-query` with TanStack Query fo
|
||||
|
||||
### Creating an Initiative
|
||||
1. Dashboard → "New Initiative" → enter name, optional description, select projects
|
||||
2. `createInitiative` mutation → auto-creates root page; if description provided, auto-spawns discuss agent
|
||||
2. `createInitiative` mutation → auto-creates root page (seeded with description as tiptap content); if description provided, auto-spawns refine agent
|
||||
3. Navigate to initiative detail page on success
|
||||
|
||||
### Managing Content (Pages)
|
||||
|
||||
@@ -85,7 +85,7 @@ Each procedure uses `require*Repository(ctx)` helpers that throw `TRPCError(INTE
|
||||
### Initiatives
|
||||
| Procedure | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| createInitiative | mutation | Create with optional branch/projectIds/description, auto-creates root page; if description provided, auto-spawns discuss agent |
|
||||
| createInitiative | mutation | Create with optional branch/projectIds/description, auto-creates root page (seeded with description); if description provided, auto-spawns refine agent |
|
||||
| listInitiatives | query | Filter by status; returns `activity` (state, activePhase, phase counts) computed from phases |
|
||||
| getInitiative | query | With projects array |
|
||||
| updateInitiative | mutation | Name, status |
|
||||
|
||||
Reference in New Issue
Block a user