mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-18 20:03:33 +00:00
WIP: Major cleanup - move Attachment to consumers, simplify agent API
- Removed Attachment from agent package (now in web-ui/coding-agent) - Agent.prompt now takes (text, images?: ImageContent[]) - Removed transports from web-ui (duplicate of agent package) - Updated coding-agent to use local message types - Updated mom package for new agent API Remaining: Fix AgentInterface.ts to compose UserMessageWithAttachments
This commit is contained in:
parent
f86dea2e4f
commit
6ddc7418da
57 changed files with 167 additions and 1061 deletions
|
|
@ -6,9 +6,9 @@ import type { MessageEditor } from "./MessageEditor.js";
|
|||
import "./MessageEditor.js";
|
||||
import "./MessageList.js";
|
||||
import "./Messages.js"; // Import for side effects to register the custom elements
|
||||
import type { Agent, AgentEvent } from "../agent/agent.js";
|
||||
import { getAppStorage } from "../storage/app-storage.js";
|
||||
import "./StreamingMessageContainer.js";
|
||||
import type { Agent, AgentEvent } from "@mariozechner/pi-agent-core";
|
||||
import type { Attachment } from "../utils/attachment-utils.js";
|
||||
import { formatUsage } from "../utils/format.js";
|
||||
import { i18n } from "../utils/i18n.js";
|
||||
|
|
@ -130,16 +130,13 @@ export class AgentInterface extends LitElement {
|
|||
}
|
||||
if (!this.session) return;
|
||||
this._unsubscribeSession = this.session.subscribe(async (ev: AgentEvent) => {
|
||||
if (ev.type === "state-update") {
|
||||
if (ev.type === "message_update") {
|
||||
if (this._streamingContainer) {
|
||||
this._streamingContainer.isStreaming = ev.state.isStreaming;
|
||||
this._streamingContainer.setMessage(ev.state.streamMessage, !ev.state.isStreaming);
|
||||
const isStreaming = this.session?.state.isStreaming || false;
|
||||
this._streamingContainer.isStreaming = isStreaming;
|
||||
this._streamingContainer.setMessage(ev.message, !isStreaming);
|
||||
}
|
||||
this.requestUpdate();
|
||||
} else if (ev.type === "error-no-model") {
|
||||
// TODO show some UI feedback
|
||||
} else if (ev.type === "error-no-api-key") {
|
||||
// Handled by onApiKeyRequired callback
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
||||
import type {
|
||||
AgentTool,
|
||||
AssistantMessage as AssistantMessageType,
|
||||
ToolResultMessage as ToolResultMessageType,
|
||||
} from "@mariozechner/pi-ai";
|
||||
import { html, LitElement, type TemplateResult } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
import type { AppMessage } from "./Messages.js";
|
||||
import { renderMessage } from "./message-renderer-registry.js";
|
||||
|
||||
export class MessageList extends LitElement {
|
||||
@property({ type: Array }) messages: AppMessage[] = [];
|
||||
@property({ type: Array }) messages: AgentMessage[] = [];
|
||||
@property({ type: Array }) tools: AgentTool[] = [];
|
||||
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
||||
@property({ type: Boolean }) isStreaming: boolean = false;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type {
|
||||
AgentTool,
|
||||
AssistantMessage as AssistantMessageType,
|
||||
ImageContent,
|
||||
TextContent,
|
||||
ToolCall,
|
||||
ToolResultMessage as ToolResultMessageType,
|
||||
UserMessage as UserMessageType,
|
||||
|
|
@ -12,8 +13,14 @@ import type { Attachment } from "../utils/attachment-utils.js";
|
|||
import { formatUsage } from "../utils/format.js";
|
||||
import { i18n } from "../utils/i18n.js";
|
||||
import "./ThinkingBlock.js";
|
||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||
|
||||
export type UserMessageWithAttachments = UserMessageType & { attachments?: Attachment[] };
|
||||
export type UserMessageWithAttachments = {
|
||||
role: "user-with-attachments";
|
||||
content: string | (TextContent | ImageContent)[];
|
||||
timestamp: number;
|
||||
attachments?: Attachment[];
|
||||
};
|
||||
|
||||
// Artifact message type for session persistence
|
||||
export interface ArtifactMessage {
|
||||
|
|
@ -25,26 +32,16 @@ export interface ArtifactMessage {
|
|||
timestamp: string;
|
||||
}
|
||||
|
||||
// Base message union
|
||||
type BaseMessage = AssistantMessageType | UserMessageWithAttachments | ToolResultMessageType | ArtifactMessage;
|
||||
|
||||
// Extensible interface - apps can extend via declaration merging
|
||||
// Example:
|
||||
// declare module "@mariozechner/pi-web-ui" {
|
||||
// interface CustomMessages {
|
||||
// "system-notification": SystemNotificationMessage;
|
||||
// }
|
||||
// }
|
||||
export interface CustomMessages {
|
||||
// Empty by default - apps extend via declaration merging
|
||||
declare module "@mariozechner/pi-agent-core" {
|
||||
interface CustomAgentMessages {
|
||||
"user-with-attachment": UserMessageWithAttachments;
|
||||
artifact: ArtifactMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// AppMessage is union of base messages + custom messages
|
||||
export type AppMessage = BaseMessage | CustomMessages[keyof CustomMessages];
|
||||
|
||||
@customElement("user-message")
|
||||
export class UserMessage extends LitElement {
|
||||
@property({ type: Object }) message!: UserMessageWithAttachments;
|
||||
@property({ type: Object }) message!: UserMessageWithAttachments | UserMessageType;
|
||||
|
||||
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
||||
return this;
|
||||
|
|
@ -66,7 +63,9 @@ export class UserMessage extends LitElement {
|
|||
<div class="user-message-container py-2 px-4 rounded-xl">
|
||||
<markdown-block .content=${content}></markdown-block>
|
||||
${
|
||||
this.message.attachments && this.message.attachments.length > 0
|
||||
this.message.role === "user-with-attachments" &&
|
||||
this.message.attachments &&
|
||||
this.message.attachments.length > 0
|
||||
? html`
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
${this.message.attachments.map(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { AgentTool, Message, ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import type { AgentMessage, AgentTool } from "@mariozechner/pi-agent-core";
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import { html, LitElement } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
|
|
@ -9,8 +10,8 @@ export class StreamingMessageContainer extends LitElement {
|
|||
@property({ type: Object }) toolResultsById?: Map<string, ToolResultMessage>;
|
||||
@property({ attribute: false }) onCostClick?: () => void;
|
||||
|
||||
@state() private _message: Message | null = null;
|
||||
private _pendingMessage: Message | null = null;
|
||||
@state() private _message: AgentMessage | null = null;
|
||||
private _pendingMessage: AgentMessage | null = null;
|
||||
private _updateScheduled = false;
|
||||
private _immediateUpdate = false;
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ export class StreamingMessageContainer extends LitElement {
|
|||
}
|
||||
|
||||
// Public method to update the message with batching for performance
|
||||
public setMessage(message: Message | null, immediate = false) {
|
||||
public setMessage(message: AgentMessage | null, immediate = false) {
|
||||
// Store the latest message
|
||||
this._pendingMessage = message;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { TemplateResult } from "lit";
|
||||
import type { AppMessage } from "./Messages.js";
|
||||
|
||||
// Extract role type from AppMessage union
|
||||
export type MessageRole = AppMessage["role"];
|
||||
export type MessageRole = AgentMessage["role"];
|
||||
|
||||
// Generic message renderer typed to specific message type
|
||||
export interface MessageRenderer<TMessage extends AppMessage = AppMessage> {
|
||||
export interface MessageRenderer<TMessage extends AgentMessage = AgentMessage> {
|
||||
render(message: TMessage): TemplateResult;
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ const messageRenderers = new Map<MessageRole, MessageRenderer<any>>();
|
|||
|
||||
export function registerMessageRenderer<TRole extends MessageRole>(
|
||||
role: TRole,
|
||||
renderer: MessageRenderer<Extract<AppMessage, { role: TRole }>>,
|
||||
renderer: MessageRenderer<Extract<AgentMessage, { role: TRole }>>,
|
||||
): void {
|
||||
messageRenderers.set(role, renderer);
|
||||
}
|
||||
|
|
@ -23,6 +23,6 @@ export function getMessageRenderer(role: MessageRole): MessageRenderer | undefin
|
|||
return messageRenderers.get(role);
|
||||
}
|
||||
|
||||
export function renderMessage(message: AppMessage): TemplateResult | undefined {
|
||||
export function renderMessage(message: AgentMessage): TemplateResult | undefined {
|
||||
return messageRenderers.get(message.role)?.render(message);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue