2026-06-16 20:16:03 +01:00
|
|
|
import { type } from "arktype"
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Entry kinds supported by the persisted conversation timeline. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const ConversationEntryKind = {
|
|
|
|
|
UserMessage: "user_message",
|
|
|
|
|
AssistantMessage: "assistant_message",
|
|
|
|
|
Attachment: "attachment",
|
|
|
|
|
ToolCall: "tool_call",
|
|
|
|
|
ToolResult: "tool_result",
|
|
|
|
|
ContextSummary: "context_summary",
|
|
|
|
|
SystemNote: "system_note",
|
|
|
|
|
} as const
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Discriminator for the payload shape and handling of a conversation entry. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type ConversationEntryKind =
|
|
|
|
|
(typeof ConversationEntryKind)[keyof typeof ConversationEntryKind]
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Visibility scopes supported by stored conversation entries. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const ConversationEntryVisibility = {
|
|
|
|
|
UserVisible: "user_visible",
|
|
|
|
|
Internal: "internal",
|
|
|
|
|
} as const
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Indicates whether a conversation entry should be exposed to the user. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type ConversationEntryVisibility =
|
|
|
|
|
(typeof ConversationEntryVisibility)[keyof typeof ConversationEntryVisibility]
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Attachment media categories accepted by conversation entries. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const AttachmentType = {
|
|
|
|
|
Image: "image",
|
|
|
|
|
Audio: "audio",
|
|
|
|
|
Video: "video",
|
|
|
|
|
Document: "document",
|
|
|
|
|
Other: "other",
|
|
|
|
|
} as const
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** File or media category associated with an attachment payload. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type AttachmentType = (typeof AttachmentType)[keyof typeof AttachmentType]
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Plain text content part for a message. */
|
|
|
|
|
export const TextMessagePart = type({
|
2026-06-16 20:16:03 +01:00
|
|
|
"+": "reject",
|
|
|
|
|
type: "'text'",
|
|
|
|
|
text: "string",
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Structured JSON content part for a message. */
|
|
|
|
|
export const JsonMessagePart = type({
|
2026-06-16 20:16:03 +01:00
|
|
|
"+": "reject",
|
|
|
|
|
type: "'json'",
|
|
|
|
|
value: "unknown",
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Content part variants supported by user and assistant messages. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const MessagePart = type.or(TextMessagePart, JsonMessagePart)
|
2026-06-18 20:47:36 +01:00
|
|
|
|
|
|
|
|
/** A structured content part inside a user or assistant message payload. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type MessagePart = typeof MessagePart.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** User-authored message entry payload. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const UserMessagePayload = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
role: "'user'",
|
|
|
|
|
parts: MessagePart.array().atLeastLength(1),
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Payload stored for a conversation entry containing a user message. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type UserMessagePayload = typeof UserMessagePayload.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Assistant-authored message entry payload. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const AssistantMessagePayload = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
role: "'assistant'",
|
|
|
|
|
parts: MessagePart.array().atLeastLength(1),
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Payload stored for a conversation entry containing an assistant message. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type AssistantMessagePayload = typeof AssistantMessagePayload.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Attachment entry payload. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const AttachmentPayload = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
role: type.enumerated("user", "assistant"),
|
|
|
|
|
name: "string",
|
|
|
|
|
mimeType: "string",
|
2026-06-18 20:47:36 +01:00
|
|
|
attachmentType: type.enumerated(...Object.values(AttachmentType)),
|
2026-06-16 20:16:03 +01:00
|
|
|
"caption?": "string",
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Payload stored for a conversation entry that references an uploaded file. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type AttachmentPayload = typeof AttachmentPayload.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Durable facts extracted from compacted conversation history. */
|
|
|
|
|
export const ContextSummary = type({
|
2026-06-16 20:16:03 +01:00
|
|
|
"+": "reject",
|
|
|
|
|
"userIntent?": "string",
|
|
|
|
|
durableFacts: type.string.array(),
|
|
|
|
|
preferences: type.string.array(),
|
|
|
|
|
decisions: type.string.array(),
|
|
|
|
|
openTasks: type.string.array(),
|
|
|
|
|
importantDetails: type.string.array(),
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Durable facts and follow-ups retained from compacted conversation history. */
|
|
|
|
|
export type ContextSummary = typeof ContextSummary.infer
|
|
|
|
|
|
|
|
|
|
/** Context-summary conversation entry payload. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const ContextSummaryPayload = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
covers: type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
startSequence: "number.integer >= 1",
|
|
|
|
|
endSequence: "number.integer >= 1",
|
|
|
|
|
}),
|
|
|
|
|
summary: ContextSummary,
|
|
|
|
|
promptVersion: "string",
|
|
|
|
|
"sourceEntryIds?": type.string.array(),
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Payload describing a compaction summary and the sequence range it covers. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type ContextSummaryPayload = typeof ContextSummaryPayload.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Model invocation metadata recorded on generated entries. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const ModelRunMetadata = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
route: "string",
|
|
|
|
|
provider: "string",
|
|
|
|
|
model: "string",
|
|
|
|
|
"contextSummaryEntryId?": "string",
|
|
|
|
|
"rawEntriesStartSequence?": "number.integer >= 1",
|
|
|
|
|
"rawEntriesEndSequence?": "number.integer >= 1",
|
|
|
|
|
"inputTokens?": "number.integer >= 0",
|
|
|
|
|
"outputTokens?": "number.integer >= 0",
|
|
|
|
|
"providerRequestId?": "string",
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Metadata describing the model run that produced a conversation entry. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type ModelRunMetadata = typeof ModelRunMetadata.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Arbitrary metadata stored alongside conversation entries. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const ConversationEntryMetadata = type({
|
|
|
|
|
"modelRun?": ModelRunMetadata,
|
|
|
|
|
"[string]": "unknown",
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Metadata bag attached to a conversation entry. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type ConversationEntryMetadata = typeof ConversationEntryMetadata.infer
|
|
|
|
|
|
2026-07-02 21:31:03 +01:00
|
|
|
export const ToolCallPayload = type({
|
|
|
|
|
toolName: "string",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export type ToolCallPayload = typeof ToolCallPayload.infer
|
|
|
|
|
|
|
|
|
|
export const ToolResultPayload = type({
|
|
|
|
|
toolName: "string",
|
|
|
|
|
ok: "boolean",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export type ToolResultPayload = typeof ToolResultPayload.infer
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Generic object payload used by operational entries. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export const GenericObjectPayload = type("Record<string, unknown>")
|
2026-06-18 20:47:36 +01:00
|
|
|
|
|
|
|
|
/** Fallback payload shape for tool calls, tool results, and system notes. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type GenericObjectPayload = typeof GenericObjectPayload.infer
|
|
|
|
|
|
2026-06-20 16:07:12 +01:00
|
|
|
export const ConversationEntryPayload = type.or(
|
|
|
|
|
UserMessagePayload,
|
|
|
|
|
AssistantMessagePayload,
|
|
|
|
|
AttachmentPayload,
|
|
|
|
|
ContextSummaryPayload,
|
2026-07-02 21:31:03 +01:00
|
|
|
ToolCallPayload,
|
|
|
|
|
ToolResultPayload,
|
2026-06-20 16:07:12 +01:00
|
|
|
GenericObjectPayload,
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-18 20:47:36 +01:00
|
|
|
/** Union of payload shapes that can be stored on a conversation entry. */
|
2026-06-16 20:16:03 +01:00
|
|
|
export type ConversationEntryPayload =
|
|
|
|
|
| UserMessagePayload
|
|
|
|
|
| AssistantMessagePayload
|
|
|
|
|
| AttachmentPayload
|
|
|
|
|
| ContextSummaryPayload
|
2026-07-02 21:31:03 +01:00
|
|
|
| ToolCallPayload
|
|
|
|
|
| ToolResultPayload
|
2026-06-16 20:16:03 +01:00
|
|
|
| GenericObjectPayload
|
2026-07-02 21:31:03 +01:00
|
|
|
|
|
|
|
|
export const Conversation = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
updatedAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export type Conversation = typeof Conversation.infer
|
|
|
|
|
|
|
|
|
|
export const UserMessageConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'user_message'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "null",
|
|
|
|
|
payload: UserMessagePayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const AssistantMessageConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'assistant_message'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "null",
|
|
|
|
|
payload: AssistantMessagePayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const AttachmentConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'attachment'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "string.uuid",
|
|
|
|
|
payload: AttachmentPayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const ToolCallConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'tool_call'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "null",
|
|
|
|
|
payload: ToolCallPayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const ToolResultConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'tool_result'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "null",
|
|
|
|
|
payload: ToolResultPayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const ContextSummaryConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'context_summary'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "null",
|
|
|
|
|
payload: ContextSummaryPayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const SystemNoteConversationEntry = type({
|
|
|
|
|
"+": "reject",
|
|
|
|
|
id: "string.uuid",
|
|
|
|
|
conversationId: "string.uuid",
|
|
|
|
|
sequence: "number.integer >= 1",
|
|
|
|
|
kind: "'system_note'",
|
|
|
|
|
visibility: type.enumerated(...Object.values(ConversationEntryVisibility)),
|
|
|
|
|
fileId: "null",
|
|
|
|
|
payload: GenericObjectPayload,
|
|
|
|
|
metadata: ConversationEntryMetadata,
|
|
|
|
|
createdAt: "string.date.iso",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const ConversationEntry = type.or(
|
|
|
|
|
UserMessageConversationEntry,
|
|
|
|
|
AssistantMessageConversationEntry,
|
|
|
|
|
AttachmentConversationEntry,
|
|
|
|
|
ToolCallConversationEntry,
|
|
|
|
|
ToolResultConversationEntry,
|
|
|
|
|
ContextSummaryConversationEntry,
|
|
|
|
|
SystemNoteConversationEntry,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
export type ConversationEntry = typeof ConversationEntry.infer
|