Claude e13dbe5ffa
feat(inbox): list and answer items from all of the user's spaces
The inbox in the main conversation only showed asks, approvals, takeovers and
unread threads of the current space. The main conversation lives in the home
space, so anything a bot needed in a project space never reached it.

- inbox.list now covers every space the user belongs to (except spaces being
  deleted), still only the user's own bots, runs and threads. Items from
  another space carry that space's id and name.
- The web inbox shows the space name next to the bot for those items, answers
  them with that space's header so the existing thread procedures check
  membership and ownership for that space, and opens their thread in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 13:27:39 +00:00

56 lines
1.8 KiB
TypeScript

import * as z from "zod";
import { AskBlockSchema } from "./events.js";
import { Id } from "./ids.js";
/** The thread an item belongs to, in the shape the thread procedures accept. */
export const InboxThreadTargetSchema = z.union([
z.object({ botId: Id }),
z.object({ groupId: Id }),
]);
export type InboxThreadTarget = z.infer<typeof InboxThreadTargetSchema>;
const InboxItemBase = z.object({
/** The bot that needs the user, shown as the sender. */
bot: z.object({ id: Id, name: z.string(), color: z.string() }),
target: InboxThreadTargetSchema,
/**
* Set when the item belongs to another of the user's spaces: call the thread procedures and
* open the thread in that space.
*/
space: z.object({ id: Id, name: z.string() }).optional(),
createdAt: z.string(),
});
const PendingAskItem = InboxItemBase.extend({
runId: Id,
/** The bot message that holds the pending ask; answer it with threads.answer. */
messageId: Id,
ask: AskBlockSchema,
});
/**
* What a bot needs from the user outside the main conversation, in any of the user's spaces: an
* answer, an approval, the computer screen, or a look at an unread thread.
*/
export const InboxItemSchema = z.discriminatedUnion("kind", [
PendingAskItem.extend({ kind: z.literal("ask") }),
PendingAskItem.extend({ kind: z.literal("approval") }),
InboxItemBase.extend({
kind: z.literal("takeover"),
runId: Id,
messageId: Id.nullable(),
/** Why the bot needs the screen. */
text: z.string(),
}),
InboxItemBase.extend({
kind: z.literal("unread"),
messageId: Id.nullable(),
/** Preview of the newest bot message. */
text: z.string(),
}),
]);
export type InboxItem = z.infer<typeof InboxItemSchema>;
export const InboxListOutputSchema = z.object({ items: z.array(InboxItemSchema) });
export type InboxListOutput = z.infer<typeof InboxListOutputSchema>;