rowboat/apps/x/packages/shared/src/message.ts
Ramnique Singh b3066a0b7a add cmd+k palette with chat mode that captures editor cursor context
Cmd+K (Ctrl+K on Win/Linux) now opens a unified palette with two
modes: Chat (default) and Search (existing behavior). Tab cycles
between them. In Chat mode, if the user triggered the shortcut from
the markdown editor, the palette auto-attaches a removable chip
showing the note path and precise cursor line. Enter sends the
prompt to the right-sidebar copilot — opening the sidebar if closed
and starting a fresh chat tab — with the chip carried as a
FileMention whose lineNumber is forwarded to the agent as
"... at <path> (line N)" so the agent can use workspace-readFile
with offset to fetch the right slice on demand.

Line numbers are computed against the same getMarkdownWithBlankLines
serializer used to write notes to disk, so the reference is
byte-identical to what the agent reads back.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 17:00:37 +05:30

89 lines
No EOL
2.5 KiB
TypeScript

import { z } from "zod";
export const ProviderOptions = z.record(z.string(), z.record(z.string(), z.json()));
export const TextPart = z.object({
type: z.literal("text"),
text: z.string(),
providerOptions: ProviderOptions.optional(),
});
export const ReasoningPart = z.object({
type: z.literal("reasoning"),
text: z.string(),
providerOptions: ProviderOptions.optional(),
});
export const ToolCallPart = z.object({
type: z.literal("tool-call"),
toolCallId: z.string(),
toolName: z.string(),
arguments: z.any(),
providerOptions: ProviderOptions.optional(),
});
export const AssistantContentPart = z.union([
TextPart,
ReasoningPart,
ToolCallPart,
]);
// A piece of user-typed text within a content array
export const UserTextPart = z.object({
type: z.literal("text"),
text: z.string(),
});
// An attachment within a content array
export const UserAttachmentPart = z.object({
type: z.literal("attachment"),
path: z.string(), // absolute file path
filename: z.string(), // display name ("photo.png")
mimeType: z.string(), // MIME type ("image/png", "text/plain")
size: z.number().optional(), // bytes
lineNumber: z.number().int().min(1).optional(), // 1-indexed line in source file (for editor-context references)
});
// Any single part of a user message (text or attachment)
export const UserContentPart = z.union([UserTextPart, UserAttachmentPart]);
// Named type for user message content — used everywhere instead of repeating the union
export const UserMessageContent = z.union([z.string(), z.array(UserContentPart)]);
export const UserMessage = z.object({
role: z.literal("user"),
content: UserMessageContent,
providerOptions: ProviderOptions.optional(),
});
export const AssistantMessage = z.object({
role: z.literal("assistant"),
content: z.union([
z.string(),
z.array(AssistantContentPart),
]),
providerOptions: ProviderOptions.optional(),
});
export const SystemMessage = z.object({
role: z.literal("system"),
content: z.string(),
providerOptions: ProviderOptions.optional(),
});
export const ToolMessage = z.object({
role: z.literal("tool"),
content: z.string(),
toolCallId: z.string(),
toolName: z.string(),
providerOptions: ProviderOptions.optional(),
});
export const Message = z.discriminatedUnion("role", [
AssistantMessage,
SystemMessage,
ToolMessage,
UserMessage,
]);
export const MessageList = z.array(Message);