mirror of
https://github.com/kennethnym/freya
synced 2026-07-04 15:11:15 +01:00
Compare commits
6 Commits
feat/upgra
...
feat/agent
| Author | SHA1 | Date | |
|---|---|---|---|
|
2e3ec94f96
|
|||
|
350e1f5fcb
|
|||
| 9aaefda216 | |||
| 952f8e4fb0 | |||
| 2e6cae4d02 | |||
| 8cf38d609b |
19
.nvim.lua
Normal file
19
.nvim.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
local ok, conform = pcall(require, "conform")
|
||||
if not ok then
|
||||
vim.notify("conform.nvim not loaded", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
conform.setup({
|
||||
formatters_by_ft = {
|
||||
javascript = { "oxfmt" },
|
||||
javascriptreact = { "oxfmt" },
|
||||
typescript = { "oxfmt" },
|
||||
typescriptreact = { "oxfmt" },
|
||||
json = { "oxfmt" },
|
||||
jsonc = { "oxfmt" },
|
||||
},
|
||||
})
|
||||
|
||||
vim.lsp.enable("tsgo")
|
||||
vim.lsp.enable("oxlint")
|
||||
20
.zed/settings.json
Normal file
20
.zed/settings.json
Normal file
@@ -0,0 +1,20 @@
|
||||
// Folder-specific settings
|
||||
//
|
||||
// For a full list of overridable settings, and general information on folder-specific settings,
|
||||
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
|
||||
{
|
||||
"languages": {
|
||||
"TypeScript": {
|
||||
"formatter": { "language_server": { "name": "oxfmt" } }
|
||||
},
|
||||
"TSX": {
|
||||
"formatter": { "language_server": { "name": "oxfmt" } }
|
||||
},
|
||||
"JavaScript": {
|
||||
"formatter": { "language_server": { "name": "oxfmt" } }
|
||||
},
|
||||
"JSX": {
|
||||
"formatter": { "language_server": { "name": "oxfmt" } }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
import type { Context, Hono } from "hono"
|
||||
|
||||
import { ConversationEntryVisibility } from "@freya/core"
|
||||
import {
|
||||
AssistantMessagePayload,
|
||||
AttachmentPayload,
|
||||
ConversationEntryKind,
|
||||
ConversationEntryVisibility,
|
||||
ContextSummaryPayload,
|
||||
GenericObjectPayload,
|
||||
UserMessagePayload,
|
||||
type Conversation,
|
||||
type ConversationEntry,
|
||||
} from "@freya/core"
|
||||
import { type } from "arktype"
|
||||
import { createMiddleware } from "hono/factory"
|
||||
|
||||
import type { AuthSessionMiddleware } from "../auth/session-middleware.ts"
|
||||
import type { Database } from "../db/index.ts"
|
||||
import type { ConversationRow } from "./storage.ts"
|
||||
import type { ConversationEntryRow, ConversationRow } from "./storage.ts"
|
||||
|
||||
import { ConversationNotFoundError } from "./errors.ts"
|
||||
import { conversations } from "./storage.ts"
|
||||
@@ -18,19 +28,20 @@ type Env = {
|
||||
}
|
||||
}
|
||||
|
||||
/** Serialized conversation summary returned by the list endpoint. */
|
||||
interface ConversationSummaryResponse {
|
||||
id: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
/** Dependencies required to register conversation HTTP handlers. */
|
||||
interface ConversationsHttpHandlersDeps {
|
||||
db: Database
|
||||
authSessionMiddleware: AuthSessionMiddleware
|
||||
}
|
||||
|
||||
interface ListConversationsResponse {
|
||||
conversations: Conversation[]
|
||||
}
|
||||
|
||||
interface ListConversationEntriesResponse {
|
||||
entries: ConversationEntry[]
|
||||
}
|
||||
|
||||
const ConversationIdParam = type("string.uuid")
|
||||
|
||||
export function registerConversationsHttpHandlers(
|
||||
@@ -49,12 +60,13 @@ export function registerConversationsHttpHandlers(
|
||||
async function handleListConversations(c: Context<Env>) {
|
||||
const user = c.get("user")!
|
||||
const db = c.get("db")
|
||||
|
||||
return c.json({
|
||||
const response: ListConversationsResponse = {
|
||||
conversations: (await conversations(db, user.id).listConversations()).map(
|
||||
serializeConversation,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
return c.json(response)
|
||||
}
|
||||
|
||||
async function handleListEntries(c: Context<Env>) {
|
||||
@@ -73,20 +85,11 @@ async function handleListEntries(c: Context<Env>) {
|
||||
const entries = await conversations(db, user.id).listEntries(parsedConversationId, {
|
||||
visibility: ConversationEntryVisibility.UserVisible,
|
||||
})
|
||||
const response: ListConversationEntriesResponse = {
|
||||
entries: entries.map(serializeConversationEntry),
|
||||
}
|
||||
|
||||
return c.json({
|
||||
entries: entries.map((row) => ({
|
||||
id: row.id,
|
||||
conversationId: row.conversationId,
|
||||
sequence: row.sequence,
|
||||
kind: row.kind,
|
||||
visibility: row.visibility,
|
||||
fileId: row.fileId,
|
||||
payload: row.payload,
|
||||
metadata: row.metadata,
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
})),
|
||||
})
|
||||
return c.json(response)
|
||||
} catch (err) {
|
||||
if (err instanceof ConversationNotFoundError) {
|
||||
return c.json({ error: "Conversation not found" }, 404)
|
||||
@@ -95,10 +98,89 @@ async function handleListEntries(c: Context<Env>) {
|
||||
}
|
||||
}
|
||||
|
||||
function serializeConversation(row: ConversationRow): ConversationSummaryResponse {
|
||||
function serializeConversation(row: ConversationRow): Conversation {
|
||||
return {
|
||||
id: row.id,
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
updatedAt: row.updatedAt.toISOString(),
|
||||
}
|
||||
}
|
||||
|
||||
function serializeConversationEntry(row: ConversationEntryRow): ConversationEntry {
|
||||
const base = {
|
||||
id: row.id,
|
||||
conversationId: row.conversationId,
|
||||
sequence: row.sequence,
|
||||
visibility: row.visibility,
|
||||
metadata: row.metadata,
|
||||
createdAt: row.createdAt.toISOString(),
|
||||
}
|
||||
|
||||
switch (row.kind) {
|
||||
case ConversationEntryKind.UserMessage:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: nullFileId(row),
|
||||
payload: UserMessagePayload.assert(row.payload),
|
||||
}
|
||||
case ConversationEntryKind.AssistantMessage:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: nullFileId(row),
|
||||
payload: AssistantMessagePayload.assert(row.payload),
|
||||
}
|
||||
case ConversationEntryKind.Attachment:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: requireFileId(row),
|
||||
payload: AttachmentPayload.assert(row.payload),
|
||||
}
|
||||
case ConversationEntryKind.ToolCall:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: nullFileId(row),
|
||||
payload: GenericObjectPayload.assert(row.payload),
|
||||
}
|
||||
case ConversationEntryKind.ToolResult:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: nullFileId(row),
|
||||
payload: GenericObjectPayload.assert(row.payload),
|
||||
}
|
||||
case ConversationEntryKind.ContextSummary:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: nullFileId(row),
|
||||
payload: ContextSummaryPayload.assert(row.payload),
|
||||
}
|
||||
case ConversationEntryKind.SystemNote:
|
||||
return {
|
||||
...base,
|
||||
kind: row.kind,
|
||||
fileId: nullFileId(row),
|
||||
payload: GenericObjectPayload.assert(row.payload),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function requireFileId(row: ConversationEntryRow): string {
|
||||
if (!row.fileId) {
|
||||
throw new Error(`Conversation attachment entry "${row.id}" is missing a file id`)
|
||||
}
|
||||
|
||||
return row.fileId
|
||||
}
|
||||
|
||||
function nullFileId(row: ConversationEntryRow): null {
|
||||
if (row.fileId !== null) {
|
||||
throw new Error(`Conversation entry "${row.id}" unexpectedly references a file`)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
3
bun.lock
3
bun.lock
@@ -158,6 +158,9 @@
|
||||
"packages/freya-agent-protocol": {
|
||||
"name": "@freya/agent-protocol",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@freya/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/freya-components": {
|
||||
"name": "@freya/components",
|
||||
|
||||
18
flake.nix
18
flake.nix
@@ -53,9 +53,10 @@
|
||||
# so source-only edits do not force Bun to reinstall.
|
||||
dependencySource = lib.fileset.toSource {
|
||||
root = ./.;
|
||||
fileset = lib.fileset.fileFilter (
|
||||
file: file.name == "bun.lock" || file.name == "package.json" || file.name == "bunfig.toml"
|
||||
) ./.;
|
||||
fileset = lib.fileset.fileFilter
|
||||
(
|
||||
file: file.name == "bun.lock" || file.name == "package.json" || file.name == "bunfig.toml"
|
||||
) ./.;
|
||||
};
|
||||
|
||||
# Checks run against a clean source tree, even when using `path:.`.
|
||||
@@ -92,10 +93,12 @@
|
||||
lib.mapAttrs mkBunScript scripts;
|
||||
mkBunApps =
|
||||
commands:
|
||||
lib.mapAttrs (name: command: {
|
||||
type = "app";
|
||||
program = "${command}/bin/${name}";
|
||||
}) commands;
|
||||
lib.mapAttrs
|
||||
(name: command: {
|
||||
type = "app";
|
||||
program = "${command}/bin/${name}";
|
||||
})
|
||||
commands;
|
||||
mkBunNodeModules =
|
||||
system: pkgs:
|
||||
pkgs.stdenvNoCC.mkDerivation {
|
||||
@@ -255,6 +258,7 @@
|
||||
pkg-config
|
||||
postgresql
|
||||
python3
|
||||
typescript-go
|
||||
watchman
|
||||
];
|
||||
linuxPackages = with pkgs; [
|
||||
|
||||
@@ -6,5 +6,8 @@
|
||||
"types": "src/index.ts",
|
||||
"scripts": {
|
||||
"test": "bun test ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@freya/core": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,40 @@
|
||||
import { ConversationEntryKind, ConversationEntryVisibility } from "@freya/core"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import type { AgentEvent, AgentServerApi } from "./index"
|
||||
import { AgentEventKind, type AgentEvent, type AgentServerApi } from "./index"
|
||||
|
||||
describe("agent protocol", () => {
|
||||
test("defines server methods and agent events", () => {
|
||||
const server: AgentServerApi = {
|
||||
async sendMessage(message) {
|
||||
return { message, conversationId: "conversation-1" }
|
||||
return {
|
||||
id: "entry-1",
|
||||
conversationId: "conversation-1",
|
||||
sequence: 1,
|
||||
kind: ConversationEntryKind.UserMessage,
|
||||
visibility: ConversationEntryVisibility.UserVisible,
|
||||
fileId: null,
|
||||
payload: {
|
||||
role: "user",
|
||||
parts: [{ type: "text", text: message }],
|
||||
},
|
||||
metadata: {},
|
||||
createdAt: "2026-07-03T00:00:00.000Z",
|
||||
}
|
||||
},
|
||||
notify() {
|
||||
// no-op for protocol shape test
|
||||
},
|
||||
ping() {
|
||||
return "pong"
|
||||
},
|
||||
}
|
||||
const event: AgentEvent = { type: "message_finished" }
|
||||
const event: AgentEvent = {
|
||||
kind: AgentEventKind.ResponseFinished,
|
||||
conversationId: "conversation-1",
|
||||
}
|
||||
|
||||
expect(server.ping()).toBe("pong")
|
||||
expect(event.type).toBe("message_finished")
|
||||
expect(event.kind).toBe(AgentEventKind.ResponseFinished)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,18 +1,46 @@
|
||||
export interface SendMessageResult {
|
||||
message: string
|
||||
import type { ConversationEntry } from "@freya/core"
|
||||
|
||||
export const AgentEventKind = {
|
||||
ConversationStarted: "conversation_started",
|
||||
ConversationEntryCreated: "conversation_entry_created",
|
||||
ResponseFinished: "response_finished",
|
||||
ResponseFailed: "response_failed",
|
||||
} as const
|
||||
|
||||
export type AgentEventKind = (typeof AgentEventKind)[keyof typeof AgentEventKind]
|
||||
|
||||
export interface AgentConversationStartedEvent {
|
||||
kind: typeof AgentEventKind.ConversationStarted
|
||||
conversationId: string
|
||||
}
|
||||
|
||||
export interface AgentConversationEntryCreatedEvent {
|
||||
kind: typeof AgentEventKind.ConversationEntryCreated
|
||||
entry: ConversationEntry
|
||||
}
|
||||
|
||||
export interface AgentResponseFinishedEvent {
|
||||
kind: typeof AgentEventKind.ResponseFinished
|
||||
conversationId: string
|
||||
}
|
||||
|
||||
export interface AgentResponseFailedEvent {
|
||||
kind: typeof AgentEventKind.ResponseFailed
|
||||
conversationId: string
|
||||
error: string
|
||||
}
|
||||
|
||||
export type AgentEvent =
|
||||
| { type: "conversation_started"; conversationId: string }
|
||||
| { type: "message_created"; text: string }
|
||||
| { type: "tool_started"; toolName: string }
|
||||
| { type: "tool_finished"; toolName: string; ok: boolean }
|
||||
| { type: "message_finished" }
|
||||
| { type: "message_failed"; error: string }
|
||||
| AgentConversationStartedEvent
|
||||
| AgentConversationEntryCreatedEvent
|
||||
| AgentResponseFinishedEvent
|
||||
| AgentResponseFailedEvent
|
||||
|
||||
export type UserEvent = { type: "typing" }
|
||||
|
||||
export interface AgentServerApi {
|
||||
sendMessage(message: string): Promise<SendMessageResult>
|
||||
sendMessage(message: string): Promise<ConversationEntry>
|
||||
notify(event: UserEvent): void
|
||||
ping(): "pong"
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ import {
|
||||
AttachmentType,
|
||||
AttachmentPayload,
|
||||
ContextSummaryPayload,
|
||||
Conversation,
|
||||
ConversationEntry,
|
||||
ConversationEntryKind,
|
||||
ConversationEntryMetadata,
|
||||
ConversationEntryVisibility,
|
||||
GenericObjectPayload,
|
||||
UserMessagePayload,
|
||||
} from "./conversation"
|
||||
@@ -143,4 +147,99 @@ describe("conversation entry schemas", () => {
|
||||
}),
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
test("parses conversation summaries", () => {
|
||||
const conversation = Conversation.assert({
|
||||
id: "11111111-1111-4111-8111-111111111111",
|
||||
createdAt: "2026-06-17T09:30:00.000Z",
|
||||
updatedAt: "2026-06-17T09:35:00.000Z",
|
||||
})
|
||||
|
||||
expect(conversation.id).toBe("11111111-1111-4111-8111-111111111111")
|
||||
})
|
||||
|
||||
test("parses kind-specific conversation entries", () => {
|
||||
const userMessageEntry = ConversationEntry.assert({
|
||||
id: "22222222-2222-4222-8222-222222222222",
|
||||
conversationId: "11111111-1111-4111-8111-111111111111",
|
||||
sequence: 1,
|
||||
kind: ConversationEntryKind.UserMessage,
|
||||
visibility: ConversationEntryVisibility.UserVisible,
|
||||
fileId: null,
|
||||
payload: {
|
||||
role: "user",
|
||||
parts: [{ type: "text", text: "hello" }],
|
||||
},
|
||||
metadata: {},
|
||||
createdAt: "2026-06-17T09:30:00.000Z",
|
||||
})
|
||||
const attachmentEntry = ConversationEntry.assert({
|
||||
id: "33333333-3333-4333-8333-333333333333",
|
||||
conversationId: "11111111-1111-4111-8111-111111111111",
|
||||
sequence: 2,
|
||||
kind: ConversationEntryKind.Attachment,
|
||||
visibility: ConversationEntryVisibility.UserVisible,
|
||||
fileId: "44444444-4444-4444-8444-444444444444",
|
||||
payload: {
|
||||
role: "user",
|
||||
name: "photo.png",
|
||||
mimeType: "image/png",
|
||||
attachmentType: AttachmentType.Image,
|
||||
},
|
||||
metadata: {},
|
||||
createdAt: "2026-06-17T09:31:00.000Z",
|
||||
})
|
||||
|
||||
expect(userMessageEntry.kind).toBe(ConversationEntryKind.UserMessage)
|
||||
expect(attachmentEntry.kind).toBe(ConversationEntryKind.Attachment)
|
||||
})
|
||||
|
||||
test("rejects conversation entries whose payload does not match the kind", () => {
|
||||
expect(() =>
|
||||
ConversationEntry.assert({
|
||||
id: "22222222-2222-4222-8222-222222222222",
|
||||
conversationId: "11111111-1111-4111-8111-111111111111",
|
||||
sequence: 1,
|
||||
kind: ConversationEntryKind.UserMessage,
|
||||
visibility: ConversationEntryVisibility.UserVisible,
|
||||
fileId: null,
|
||||
payload: {
|
||||
role: "assistant",
|
||||
parts: [{ type: "text", text: "hello" }],
|
||||
},
|
||||
metadata: {},
|
||||
createdAt: "2026-06-17T09:30:00.000Z",
|
||||
}),
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
test("rejects serialized conversations with extra fields", () => {
|
||||
expect(() =>
|
||||
Conversation.assert({
|
||||
id: "11111111-1111-4111-8111-111111111111",
|
||||
createdAt: "2026-06-17T09:30:00.000Z",
|
||||
updatedAt: "2026-06-17T09:35:00.000Z",
|
||||
title: "not yet part of the schema",
|
||||
}),
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
test("rejects file ids on non-attachment entries", () => {
|
||||
expect(() =>
|
||||
ConversationEntry.assert({
|
||||
id: "22222222-2222-4222-8222-222222222222",
|
||||
conversationId: "11111111-1111-4111-8111-111111111111",
|
||||
sequence: 1,
|
||||
kind: ConversationEntryKind.UserMessage,
|
||||
visibility: ConversationEntryVisibility.UserVisible,
|
||||
fileId: "44444444-4444-4444-8444-444444444444",
|
||||
payload: {
|
||||
role: "user",
|
||||
parts: [{ type: "text", text: "hello" }],
|
||||
},
|
||||
metadata: {},
|
||||
createdAt: "2026-06-17T09:30:00.000Z",
|
||||
}),
|
||||
).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -146,6 +146,19 @@ export const ConversationEntryMetadata = type({
|
||||
/** Metadata bag attached to a conversation entry. */
|
||||
export type ConversationEntryMetadata = typeof ConversationEntryMetadata.infer
|
||||
|
||||
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
|
||||
|
||||
/** Generic object payload used by operational entries. */
|
||||
export const GenericObjectPayload = type("Record<string, unknown>")
|
||||
|
||||
@@ -158,4 +171,118 @@ export type ConversationEntryPayload =
|
||||
| AssistantMessagePayload
|
||||
| AttachmentPayload
|
||||
| ContextSummaryPayload
|
||||
| ToolCallPayload
|
||||
| ToolResultPayload
|
||||
| GenericObjectPayload
|
||||
|
||||
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: GenericObjectPayload,
|
||||
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: GenericObjectPayload,
|
||||
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
|
||||
|
||||
@@ -10,10 +10,15 @@ export { UnknownActionError } from "./action"
|
||||
export type { ConversationEntryPayload } from "./conversation"
|
||||
export {
|
||||
AssistantMessagePayload,
|
||||
AssistantMessageConversationEntry,
|
||||
AttachmentPayload,
|
||||
AttachmentConversationEntry,
|
||||
AttachmentType,
|
||||
ContextSummary,
|
||||
ContextSummaryConversationEntry,
|
||||
ContextSummaryPayload,
|
||||
Conversation,
|
||||
ConversationEntry,
|
||||
ConversationEntryKind,
|
||||
ConversationEntryMetadata,
|
||||
ConversationEntryVisibility,
|
||||
@@ -21,8 +26,14 @@ export {
|
||||
JsonMessagePart,
|
||||
MessagePart,
|
||||
ModelRunMetadata,
|
||||
SystemNoteConversationEntry,
|
||||
TextMessagePart,
|
||||
ToolCallConversationEntry,
|
||||
ToolCallPayload,
|
||||
ToolResultConversationEntry,
|
||||
ToolResultPayload,
|
||||
UserMessagePayload,
|
||||
UserMessageConversationEntry,
|
||||
} from "./conversation"
|
||||
|
||||
// Feed
|
||||
|
||||
Reference in New Issue
Block a user