feat(agents): Tier 2 — AGENTS.md + per-session picker

Six builtin defaults (Code Reviewer, Debugger, Refactorer, Architect,
Security Auditor, Prompt Builder) with no model field so session.model
wins. Project root AGENTS.md parsed on demand with mtime cache; when
present, only its agents are shown. sessions.agent_id resolves per turn
into effective system prompt, temperature, and a tool whitelist applied
in inference. AgentPicker mounts in the ChatInput toolbar; SettingsDrawer
agent surface deferred to Batch 7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 20:06:51 +00:00
parent 934f739ca1
commit 92bd3b1cdf
16 changed files with 984 additions and 35 deletions

View File

@@ -15,6 +15,7 @@ import { AttachmentChip } from '@/components/AttachmentChip';
import { AttachmentPreviewModal } from '@/components/AttachmentPreviewModal';
import { FileMentionPopover } from '@/components/FileMentionPopover';
import { DropOverlay } from '@/components/DropOverlay';
import { AgentPicker } from '@/components/AgentPicker';
import { api } from '@/api/client';
import { sessionEvents } from '@/hooks/sessionEvents';
import { useViewport } from '@/hooks/useViewport';
@@ -24,11 +25,15 @@ const MAX_ATTACHMENTS = 10;
interface Props {
disabled?: boolean;
projectId: string;
// Batch 9: optional so callers that pre-date the agent picker still compile.
// When omitted, the toolbar row is hidden entirely.
agentId?: string | null;
onAgentChange?: (agentId: string | null) => void | Promise<void>;
onSend: (content: string) => void | Promise<void>;
onForceSend?: (content: string) => void | Promise<void>;
}
export function ChatInput({ disabled, projectId, onSend, onForceSend }: Props) {
export function ChatInput({ disabled, projectId, agentId, onAgentChange, onSend, onForceSend }: Props) {
const { isMobile } = useViewport();
const [value, setValue] = useState('');
const [busy, setBusy] = useState(false);
@@ -420,6 +425,18 @@ export function ChatInput({ disabled, projectId, onSend, onForceSend }: Props) {
))}
</div>
)}
{/* Batch 9 toolbar — agent picker. Sits above the input row so it
doesn't compete with the send button for vertical alignment.
When Batch 7 lands, ModelPicker and the + button join this row. */}
{onAgentChange && (
<div className="px-4 pt-2 flex items-center gap-1.5">
<AgentPicker
projectId={projectId}
value={agentId ?? null}
onChange={onAgentChange}
/>
</div>
)}
<div className="px-4 py-3 flex items-end gap-2">
<Textarea
ref={textareaRef}