Clean up types (#9057)

Signed-off-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-05-07 09:38:37 -04:00
committed by GitHub
parent 64f795f82c
commit bced4ea5ec
21 changed files with 123 additions and 262 deletions
@@ -239,7 +239,7 @@ describe("acpNotificationHandler", () => {
type: "toolRequest",
id: "tool-b",
name: "grep",
status: "executing",
status: "in_progress",
});
expect(message.content[2]).toMatchObject({
type: "toolResponse",
@@ -65,7 +65,7 @@ describe("ACP tool call status handling", () => {
expect(assistant?.content[0]).toMatchObject({
type: "toolRequest",
id: "tool-1",
status: "error",
status: "failed",
});
expect(assistant?.content[1]).toMatchObject({
type: "toolResponse",
@@ -114,7 +114,7 @@ describe("ACP tool call status handling", () => {
expect(message.content[0]).toMatchObject({
type: "toolRequest",
id: "tool-1",
status: "error",
status: "failed",
});
expect(message.content[1]).toMatchObject({
type: "toolResponse",
@@ -7,7 +7,6 @@ import { useChatSessionStore } from "@/features/chat/stores/chatSessionStore";
import { getBufferedMessage } from "@/features/chat/hooks/replayBuffer";
import type {
ToolCallLocation,
ToolCallStatus,
ToolKind,
ToolRequestContent,
ToolResponseContent,
@@ -51,9 +50,6 @@ interface LivePerf {
}
const livePerf = new Map<string, LivePerf>();
const toolCallStatusFromUpdate = (status: string): ToolCallStatus =>
status === "failed" ? "error" : "completed";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
@@ -90,7 +86,7 @@ function locationsFromUpdate(
function toolCallUpdatePatch(
update: SessionUpdate,
): Partial<ToolRequestContent> {
): Pick<Partial<ToolRequestContent>, "toolKind" | "locations"> {
const toolKind = toolKindFromUpdate(update);
const locations = locationsFromUpdate(update);
@@ -226,7 +222,7 @@ function handleReplay(sessionId: string, update: SessionUpdate): void {
name: update.title,
...identity,
arguments: rawInputToArguments(update.rawInput),
status: "executing",
status: "in_progress",
...toolCallUpdatePatch(update),
startedAt: created ?? Date.now(),
...(chainSummary ? { chainSummary } : {}),
@@ -276,7 +272,6 @@ function handleReplay(sessionId: string, update: SessionUpdate): void {
}
}
if (update.status === "completed" || update.status === "failed") {
const toolCallStatus = toolCallStatusFromUpdate(update.status);
const tc = msg.content.find(
(c) => c.type === "toolRequest" && c.id === update.toolCallId,
);
@@ -287,7 +282,7 @@ function handleReplay(sessionId: string, update: SessionUpdate): void {
...tc,
...identity,
...toolCallUpdatePatch(update),
status: toolCallStatus,
status: update.status,
} as ToolRequestContent;
}
}
@@ -356,7 +351,7 @@ function handleLive(sessionId: string, update: SessionUpdate): void {
name: update.title,
...identity,
arguments: rawInputToArguments(update.rawInput),
status: "executing",
status: "in_progress",
...toolCallUpdatePatch(update),
startedAt: Date.now(),
...(chainSummary ? { chainSummary } : {}),
@@ -403,7 +398,7 @@ function handleLive(sessionId: string, update: SessionUpdate): void {
}
if (update.status === "completed" || update.status === "failed") {
const toolCallStatus = toolCallStatusFromUpdate(update.status);
const { status: resolvedStatus } = update;
const ownerMessage = store.messagesBySession[sessionId]?.find(
(m) => m.id === messageId,
);
@@ -425,7 +420,7 @@ function handleLive(sessionId: string, update: SessionUpdate): void {
...block,
...identity,
...toolCallUpdatePatch(update),
status: toolCallStatus,
status: resolvedStatus,
}
: block,
),
+2 -2
View File
@@ -59,7 +59,7 @@ export async function listDictationLocalModels(): Promise<
> {
const client = await getClient();
const response = await client.goose.GooseDictationModelsList({});
return response.models as unknown as WhisperModelStatus[];
return response.models;
}
export async function downloadDictationLocalModel(
@@ -76,7 +76,7 @@ export async function getDictationLocalModelDownloadProgress(
const response = await client.goose.GooseDictationModelsDownloadProgress({
modelId,
});
return (response.progress ?? null) as DictationDownloadProgress | null;
return response.progress ?? null;
}
export async function cancelDictationLocalModelDownload(
@@ -26,7 +26,9 @@ import type {
const textBlock: TextContent = { type: "text", text: "hello" };
const imageBlock: ImageContent = {
type: "image",
source: { type: "url", url: "https://img.png" },
data: "",
mimeType: "image/png",
uri: "https://img.png",
};
const toolRequestBlock: ToolRequestContent = {
type: "toolRequest",
+1 -19
View File
@@ -3,24 +3,6 @@
// a narrow union.
export type ProviderType = string;
export interface ProviderConfig {
type: ProviderType;
name: string;
description?: string;
models: ModelInfo[];
requiresApiKey: boolean;
apiKeyEnvVar?: string;
}
export interface ModelInfo {
id: string;
name: string;
contextWindow: number;
supportsTools: boolean;
supportsVision: boolean;
supportsThinking: boolean;
}
// Avatar type — either a remote URL or a local file in ~/.goose/avatars/
export type Avatar =
| { type: "url"; value: string }
@@ -86,4 +68,4 @@ export interface CreateAgentRequest {
acpEndpoint?: string;
}
// Session, TokenState, ChatState, and MessageEventType are defined in ./chat.ts
// Session, TokenState, and ChatState are defined in ./chat.ts
-61
View File
@@ -1,6 +1,3 @@
import type { Message } from "./messages";
import type { Agent } from "./agents";
// Chat state machine
export type ChatState =
| "idle"
@@ -67,61 +64,3 @@ export interface Session {
messageCount: number;
userSetName?: boolean;
}
// SSE event types (from goosed server)
export type MessageEventType =
| "message"
| "error"
| "finish"
| "modelChange"
| "notification"
| "updateConversation"
| "ping";
export interface MessageEvent {
type: "message";
message: Message;
tokenState: TokenState;
}
export interface ErrorEvent {
type: "error";
error: string;
}
export interface FinishEvent {
type: "finish";
reason: string;
tokenState: TokenState;
}
export interface ModelChangeEvent {
type: "modelChange";
model: string;
mode: string;
}
export type StreamEvent =
| MessageEvent
| ErrorEvent
| FinishEvent
| ModelChangeEvent;
// Chat request
export interface ChatRequest {
userMessage: Message;
sessionId: string;
recipeName?: string;
overrideConversation?: Message[];
}
// Active chat context
export interface ChatContext {
sessionId: string;
agent: Agent;
messages: Message[];
chatState: SessionChatRuntime["chatState"];
tokenState: SessionChatRuntime["tokenState"];
streamingMessageId: SessionChatRuntime["streamingMessageId"];
error: SessionChatRuntime["error"];
}
+8 -39
View File
@@ -1,47 +1,16 @@
export type {
DictationModelOption,
DictationProviderStatusEntry as DictationProviderStatus,
DictationTranscribeResponse,
DictationLocalModelStatus as WhisperModelStatus,
DictationDownloadProgress,
} from "@aaif/goose-sdk";
export type DictationProvider = "openai" | "groq" | "elevenlabs" | "local";
export interface DictationModelOption {
id: string;
label: string;
description: string;
}
export interface DictationProviderStatus {
configured: boolean;
host?: string | null;
description: string;
usesProviderConfig: boolean;
settingsPath?: string | null;
configKey?: string | null;
modelConfigKey?: string | null;
defaultModel?: string | null;
selectedModel?: string | null;
availableModels: DictationModelOption[];
}
export interface DictationTranscribeResponse {
text: string;
}
export type MicrophonePermissionStatus =
| "not_determined"
| "authorized"
| "denied"
| "restricted"
| "unsupported";
export interface WhisperModelStatus {
id: string;
sizeMb: number;
description: string;
downloaded: boolean;
downloadInProgress: boolean;
}
export interface DictationDownloadProgress {
bytesDownloaded: number;
totalBytes: number;
progressPercent: number;
status: string;
error?: string | null;
}
+61 -69
View File
@@ -2,6 +2,48 @@ import type {
GooseReadResourceResult,
GooseToolMetadata,
} from "@aaif/goose-sdk";
import type {
Annotations,
ImageContent as AcpImageContent,
Role,
TextContent as AcpTextContent,
ToolCallLocation as AcpToolCallLocation,
ToolCallStatus as AcpToolCallStatus,
ToolKind,
} from "@agentclientprotocol/sdk";
// ── Wire types (re-exported from ACP SDK) ─────────────────────────────
//
// These are the exact types that come off the ACP WebSocket. We re-export
// them so feature code imports everything from this module. Aliases exist
// only for readability — no reshaping, no field-dropping.
export type { Annotations, Role, ToolKind };
export type ToolCallLocation = AcpToolCallLocation;
/** ACP TextContent with discriminator. */
export type TextContent = AcpTextContent & { type: "text" };
/** ACP ImageContent with discriminator. */
export type ImageContent = AcpImageContent & { type: "image" };
/**
* Tool call execution status.
*
* The four ACP wire values plus `"stopped"`, a renderer-only extension
* for user-cancelled tool calls.
*/
export type ToolCallStatus = AcpToolCallStatus | "stopped";
// ── Message role ──────────────────────────────────────────────────────
/**
* ACP defines `Role = "user" | "assistant"`. The renderer adds `"system"`
* for locally-synthesized notification messages.
*/
export type MessageRole = Role | "system";
// ── Composer attachment drafts ────────────────────────────────────────
export type ChatAttachmentKind = "image" | "file" | "directory";
@@ -35,55 +77,11 @@ export type ChatAttachmentDraft =
| ChatFileAttachmentDraft
| ChatDirectoryAttachmentDraft;
// Message roles
export type MessageRole = "user" | "assistant" | "system";
/** ACP audience restriction — which roles may see a content block. */
export type Audience = ("user" | "assistant")[];
/** ACP content-block annotations (mirrors the SDK's Annotations shape). */
export interface ContentAnnotations {
audience?: Audience;
}
// Content block types
export interface TextContent {
type: "text";
text: string;
annotations?: ContentAnnotations;
}
export interface ImageContent {
type: "image";
source:
| { type: "base64"; mediaType: string; data: string }
| { type: "url"; url: string };
annotations?: ContentAnnotations;
}
export type ToolCallStatus =
| "pending"
| "executing"
| "completed"
| "error"
| "stopped";
export type ToolKind =
| "read"
| "edit"
| "delete"
| "move"
| "search"
| "execute"
| "think"
| "fetch"
| "switch_mode"
| "other";
export interface ToolCallLocation {
path: string;
line?: number | null;
}
// ── Renderer-only content block types ─────────────────────────────────
//
// These types have no ACP equivalent. They are synthesized by the
// notification handler from _meta payloads, tool call reductions, or
// local UI events.
export type MessageCompletionStatus =
| "inProgress"
@@ -92,9 +90,7 @@ export type MessageCompletionStatus =
| "stopped";
export interface ToolChainSummary {
/** Lowercase phrase covering the chain's tool calls (e.g. "applied dark mode polish"). */
summary: string;
/** Number of tool calls the summary covers. */
count: number;
}
@@ -107,15 +103,9 @@ export interface ToolRequestContent {
arguments: Record<string, unknown>;
status: ToolCallStatus;
toolKind?: ToolKind;
locations?: ToolCallLocation[];
/** Epoch ms when the tool call started executing (set on event receipt). */
locations?: AcpToolCallLocation[];
startedAt?: number;
annotations?: ContentAnnotations;
/**
* Server-generated summary of a multi-tool chain that starts at this tool
* call. Only set on the FIRST tool call of a chain (>= 2 tools); the rest of
* the chain has this field undefined.
*/
annotations?: Annotations;
chainSummary?: ToolChainSummary;
}
@@ -126,7 +116,7 @@ export interface ToolResponseContent {
result: string;
structuredContent?: unknown;
isError: boolean;
annotations?: ContentAnnotations;
annotations?: Annotations;
}
export interface McpAppPayload {
@@ -155,18 +145,18 @@ export interface McpAppContent {
export interface ThinkingContent {
type: "thinking";
text: string;
annotations?: ContentAnnotations;
annotations?: Annotations;
}
export interface RedactedThinkingContent {
type: "redactedThinking";
annotations?: ContentAnnotations;
annotations?: Annotations;
}
export interface ReasoningContent {
type: "reasoning";
text: string;
annotations?: ContentAnnotations;
annotations?: Annotations;
}
export interface ActionRequiredContent {
@@ -177,16 +167,18 @@ export interface ActionRequiredContent {
toolName?: string;
arguments?: Record<string, unknown>;
schema?: Record<string, unknown>;
annotations?: ContentAnnotations;
annotations?: Annotations;
}
export interface SystemNotificationContent {
type: "systemNotification";
notificationType: "compaction" | "info" | "warning" | "error";
text: string;
annotations?: ContentAnnotations;
annotations?: Annotations;
}
// ── Message ───────────────────────────────────────────────────────────
export type MessageContent =
| TextContent
| ImageContent
@@ -217,11 +209,9 @@ export interface MessageMetadata {
agentVisible?: boolean;
attachments?: MessageAttachment[];
chips?: MessageChip[];
/** Persona that generated this assistant message (set on send). */
personaId?: string;
personaName?: string;
providerId?: string;
/** Which persona this user message is addressed to. */
targetPersonaId?: string;
targetPersonaName?: string;
completionStatus?: MessageCompletionStatus;
@@ -235,7 +225,8 @@ export interface Message {
metadata?: MessageMetadata;
}
// Type guards for content blocks
// ── Type guards ───────────────────────────────────────────────────────
export function isTextContent(c: MessageContent): c is TextContent {
return c.type === "text";
}
@@ -265,7 +256,8 @@ export function isSystemNotification(
return c.type === "systemNotification";
}
// Helpers
// ── Helpers ───────────────────────────────────────────────────────────
export function getTextContent(message: Message): string {
return message.content
.filter(isTextContent)
+1 -7
View File
@@ -11,13 +11,7 @@ export type ProviderSetupMethod = ProviderSetupMethodDto;
export type ProviderGroup = ProviderSetupGroupDto;
export type ProviderField = ProviderSetupFieldDto;
export interface ProviderFieldValue {
key: string;
value: string | null;
isSet: boolean;
isSecret: boolean;
required: boolean;
}
export type { ProviderConfigFieldValueDto as ProviderFieldValue } from "@aaif/goose-sdk";
export type ProviderCatalogEntry = Omit<
ProviderSetupCatalogEntryDto,