diff --git a/bun.lock b/bun.lock index 0f31646..a33c138 100644 --- a/bun.lock +++ b/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", diff --git a/packages/freya-agent-protocol/package.json b/packages/freya-agent-protocol/package.json index 23573ab..4d1a68e 100644 --- a/packages/freya-agent-protocol/package.json +++ b/packages/freya-agent-protocol/package.json @@ -6,5 +6,8 @@ "types": "src/index.ts", "scripts": { "test": "bun test ./src" + }, + "dependencies": { + "@freya/core": "workspace:*" } } diff --git a/packages/freya-agent-protocol/src/index.test.ts b/packages/freya-agent-protocol/src/index.test.ts index 1913a2f..09b3f81 100644 --- a/packages/freya-agent-protocol/src/index.test.ts +++ b/packages/freya-agent-protocol/src/index.test.ts @@ -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) }) }) diff --git a/packages/freya-agent-protocol/src/index.ts b/packages/freya-agent-protocol/src/index.ts index 0c1004a..eafc603 100644 --- a/packages/freya-agent-protocol/src/index.ts +++ b/packages/freya-agent-protocol/src/index.ts @@ -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 + sendMessage(message: string): Promise + notify(event: UserEvent): void ping(): "pong" } diff --git a/packages/freya-core/src/conversation.ts b/packages/freya-core/src/conversation.ts index d504f4c..5b51ddc 100644 --- a/packages/freya-core/src/conversation.ts +++ b/packages/freya-core/src/conversation.ts @@ -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") @@ -158,6 +171,8 @@ export type ConversationEntryPayload = | AssistantMessagePayload | AttachmentPayload | ContextSummaryPayload + | ToolCallPayload + | ToolResultPayload | GenericObjectPayload export const Conversation = type({ diff --git a/packages/freya-core/src/index.ts b/packages/freya-core/src/index.ts index 6fa1ad1..a4180a4 100644 --- a/packages/freya-core/src/index.ts +++ b/packages/freya-core/src/index.ts @@ -29,7 +29,9 @@ export { SystemNoteConversationEntry, TextMessagePart, ToolCallConversationEntry, + ToolCallPayload, ToolResultConversationEntry, + ToolResultPayload, UserMessagePayload, UserMessageConversationEntry, } from "./conversation"