The thread-panel composer was pinned to `channel="direct"` with no
`directParticipantIds` and no `replyTo`. Clicking reply in a human
review thread sent a message with no recipient and the send button
stayed disabled — a silent dead-end for the entire Co-Founder
review UX.
The fix exposes the thread's participant list to the renderer:
- `listThreadParticipantsSchema` and the matching IPC channel
(`agent-company:list-thread-participants`) return the rows from
`conversation_participants` for a given thread.
- `threadIntegration.onTaskCreated` / `onApprovalCreated` already
enrol the owning agent and the virtual user on thread creation,
so the list is populated without any backfill.
- `ThreadPanel` loads participants alongside events and forwards
the agent ids to `MessageComposer.directParticipantIds`. The
composer's existing `directRecipientId` memo picks the first
non-board agent, so replies now target the correct agent — or
surface the "no recipient" notice when the thread has none.
The Board/user member is deliberately excluded from the list the
composer consumes: the composer wants an agent to route to. All
other wiring (wait-state precision, threadId) stays untouched.
24 lines
613 B
TypeScript
24 lines
613 B
TypeScript
import { z } from "zod";
|
|
|
|
export const threadGetterSchema = z.object({
|
|
companyId: z.string().uuid(),
|
|
threadId: z.string().uuid(),
|
|
});
|
|
|
|
export const listThreadEventsSchema = z.object({
|
|
companyId: z.string().uuid(),
|
|
threadId: z.string().uuid(),
|
|
limit: z.number().int().min(1).max(100).default(50),
|
|
offset: z.number().int().min(0).default(0),
|
|
});
|
|
|
|
export const listThreadWaitStatesSchema = z.object({
|
|
companyId: z.string().uuid(),
|
|
resolvedOnly: z.boolean().default(false),
|
|
});
|
|
|
|
export const listThreadParticipantsSchema = z.object({
|
|
companyId: z.string().uuid(),
|
|
threadId: z.string().uuid(),
|
|
});
|