cynarAI 6fd136e944
feat: suggestions the user accepts or dismisses with one tap (#142)
Bots can offer an exact tool call as a card. Nothing runs until the user
taps Accept; the call then runs as its own approved run of the bot it was
meant for, and the card shows the outcome. Open suggestions also wait in
the inbox and expire after a week.


Claude-Session: https://claude.ai/code/session_01UmSCnoCgXqBp2fU3vwz4o1

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-05 02:19:31 +02:00

63 lines
2.1 KiB
TypeScript

import * as z from "zod";
import { CardBlockSchema } from "./cards.js";
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, a suggestion to accept, 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("suggestion"),
/** The bot message holding the card; accept or dismiss it with threads.act. */
messageId: Id,
card: CardBlockSchema,
}),
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>;