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
@@ -141,11 +141,8 @@ describe("useChat attachments", () => {
{ type: "text", text: "" }, { type: "text", text: "" },
{ {
type: "image", type: "image",
source: { data: "abc123",
type: "base64", mimeType: "image/png",
mediaType: "image/png",
data: "abc123",
},
}, },
]); ]);
expect(mockAcpSendMessage).toHaveBeenCalledWith("session-1", " ", { expect(mockAcpSendMessage).toHaveBeenCalledWith("session-1", " ", {
+3 -6
View File
@@ -94,7 +94,7 @@ function markMessageStopped(sessionId: string, messageId: string) {
completionStatus: "stopped", completionStatus: "stopped",
}, },
content: message.content.map((block) => content: message.content.map((block) =>
block.type === "toolRequest" && block.status === "executing" block.type === "toolRequest" && block.status === "in_progress"
? { ...block, status: "stopped" } ? { ...block, status: "stopped" }
: block, : block,
), ),
@@ -215,11 +215,8 @@ export function useChat(
for (const img of images) { for (const img of images) {
userMessage.content.push({ userMessage.content.push({
type: "image", type: "image",
source: { data: img.base64,
type: "base64", mimeType: img.mimeType,
mediaType: img.mimeType,
data: img.base64,
},
}); });
} }
} }
@@ -74,13 +74,13 @@ describe("getToolItemStatus", () => {
it("treats response.isError as error", () => { it("treats response.isError as error", () => {
expect(getToolItemStatus(pair("a", { name: "x" }, { isError: true }))).toBe( expect(getToolItemStatus(pair("a", { name: "x" }, { isError: true }))).toBe(
"error", "failed",
); );
}); });
it("uses request status when no response yet", () => { it("uses request status when no response yet", () => {
expect(getToolItemStatus(pair("a", { status: "executing" }))).toBe( expect(getToolItemStatus(pair("a", { status: "in_progress" }))).toBe(
"executing", "in_progress",
); );
}); });
}); });
@@ -89,17 +89,17 @@ describe("getChainAggregateStatus", () => {
it("prefers error over pending and executing", () => { it("prefers error over pending and executing", () => {
expect( expect(
getChainAggregateStatus([ getChainAggregateStatus([
pair("a", { status: "executing" }), pair("a", { status: "in_progress" }),
pair("b", { name: "x" }, { isError: true }), pair("b", { name: "x" }, { isError: true }),
pair("c", { status: "pending" }), pair("c", { status: "pending" }),
]), ]),
).toBe("error"); ).toBe("failed");
}); });
it("prefers stopped over executing/pending when there is no error", () => { it("prefers stopped over executing/pending when there is no error", () => {
expect( expect(
getChainAggregateStatus([ getChainAggregateStatus([
pair("a", { status: "executing" }), pair("a", { status: "in_progress" }),
pair("b", { status: "stopped" }), pair("b", { status: "stopped" }),
]), ]),
).toBe("stopped"); ).toBe("stopped");
@@ -109,9 +109,9 @@ describe("getChainAggregateStatus", () => {
expect( expect(
getChainAggregateStatus([ getChainAggregateStatus([
pair("a", { name: "x" }, {}), pair("a", { name: "x" }, {}),
pair("b", { status: "executing" }), pair("b", { status: "in_progress" }),
]), ]),
).toBe("executing"); ).toBe("in_progress");
}); });
it("returns pending when only pending is present", () => { it("returns pending when only pending is present", () => {
@@ -23,7 +23,7 @@ export function getToolItemName(item: ToolChainItem): string {
export function getToolItemStatus(item: ToolChainItem): ToolCallStatus { export function getToolItemStatus(item: ToolChainItem): ToolCallStatus {
if (item.response) { if (item.response) {
return item.response.isError ? "error" : "completed"; return item.response.isError ? "failed" : "completed";
} }
return item.request?.status ?? "completed"; return item.request?.status ?? "completed";
} }
@@ -35,10 +35,10 @@ export function getToolItemStatus(item: ToolChainItem): ToolCallStatus {
export function getChainAggregateStatus( export function getChainAggregateStatus(
items: ToolChainItem[], items: ToolChainItem[],
): ToolCallStatus { ): ToolCallStatus {
if (items.some((i) => getToolItemStatus(i) === "error")) return "error"; if (items.some((i) => getToolItemStatus(i) === "failed")) return "failed";
if (items.some((i) => getToolItemStatus(i) === "stopped")) return "stopped"; if (items.some((i) => getToolItemStatus(i) === "stopped")) return "stopped";
if (items.some((i) => getToolItemStatus(i) === "executing")) if (items.some((i) => getToolItemStatus(i) === "in_progress"))
return "executing"; return "in_progress";
if (items.some((i) => getToolItemStatus(i) === "pending")) return "pending"; if (items.some((i) => getToolItemStatus(i) === "pending")) return "pending";
return "completed"; return "completed";
} }
@@ -3,8 +3,8 @@ import type { ToolPart } from "@/shared/ui/ai-elements/tool";
export const toolStatusMap: Record<ToolCallStatus, ToolPart["state"]> = { export const toolStatusMap: Record<ToolCallStatus, ToolPart["state"]> = {
pending: "input-streaming", pending: "input-streaming",
executing: "input-available", in_progress: "input-available",
completed: "output-available", completed: "output-available",
error: "output-error", failed: "output-error",
stopped: "output-denied", stopped: "output-denied",
}; };
@@ -219,10 +219,7 @@ function renderContentBlock(
} }
case "image": { case "image": {
const ic = content as ImageContent; const ic = content as ImageContent;
const src = const src = ic.uri ?? `data:${ic.mimeType};base64,${ic.data}`;
ic.source.type === "base64"
? `data:${ic.source.mediaType};base64,${ic.source.data}`
: ic.source.url;
return ( return (
<ClickableImage <ClickableImage
key={`image-${index}`} key={`image-${index}`}
@@ -46,7 +46,7 @@ function useElapsedTime(status: ToolCallStatus, startedAt?: number) {
const [elapsed, setElapsed] = useState(0); const [elapsed, setElapsed] = useState(0);
useEffect(() => { useEffect(() => {
if (status === "executing") { if (status === "in_progress") {
const origin = startedAt ?? Date.now(); const origin = startedAt ?? Date.now();
setElapsed(Math.floor((Date.now() - origin) / 1000)); setElapsed(Math.floor((Date.now() - origin) / 1000));
const interval = setInterval(() => { const interval = setInterval(() => {
@@ -282,7 +282,7 @@ export function ToolCallAdapter({
[args, name], [args, name],
); );
const elapsedSeconds = const elapsedSeconds =
status === "executing" && elapsed >= 3 ? elapsed : undefined; status === "in_progress" && elapsed >= 3 ? elapsed : undefined;
const { resolveMarkdownHref, openResolvedPath } = useArtifactPolicyContext(); const { resolveMarkdownHref, openResolvedPath } = useArtifactPolicyContext();
@@ -17,25 +17,25 @@ import type { ToolCallStatus } from "@/shared/types/messages";
export type { ToolChainItem }; export type { ToolChainItem };
const STEP_BULLET_ICON: Record< const STEP_BULLET_ICON: Record<
Exclude<ToolCallStatus, "error" | "stopped">, Exclude<ToolCallStatus, "failed" | "stopped">,
LucideIcon LucideIcon
> = { > = {
pending: CircleIcon, pending: CircleIcon,
executing: ClockIcon, in_progress: ClockIcon,
completed: Check, completed: Check,
}; };
const STEP_BULLET_CLASS: Record< const STEP_BULLET_CLASS: Record<
Exclude<ToolCallStatus, "error" | "stopped">, Exclude<ToolCallStatus, "failed" | "stopped">,
string string
> = { > = {
pending: "text-muted-foreground/70", pending: "text-muted-foreground/70",
executing: "text-muted-foreground animate-pulse", in_progress: "text-muted-foreground animate-pulse",
completed: "text-muted-foreground", completed: "text-muted-foreground",
}; };
function ChainStepBullet({ status }: { status: ToolCallStatus }) { function ChainStepBullet({ status }: { status: ToolCallStatus }) {
if (status === "error") { if (status === "failed") {
return ( return (
<span aria-hidden className="size-1.5 shrink-0 rounded-full bg-red-600" /> <span aria-hidden className="size-1.5 shrink-0 rounded-full bg-red-600" />
); );
@@ -172,7 +172,7 @@ export function ToolChainCards({ toolItems }: { toolItems: ToolChainItem[] }) {
const aggregateStatus = getChainAggregateStatus(toolItems); const aggregateStatus = getChainAggregateStatus(toolItems);
const summary = summarizeToolChainSteps(primaryItems); const summary = summarizeToolChainSteps(primaryItems);
const isActiveChain = const isActiveChain =
aggregateStatus === "executing" || aggregateStatus === "pending"; aggregateStatus === "in_progress" || aggregateStatus === "pending";
// Chains that mount as already-complete (history replay) start collapsed; // Chains that mount as already-complete (history replay) start collapsed;
// live chains mount mid-execution, stay open while running, and auto-collapse // live chains mount mid-execution, stay open while running, and auto-collapse
// once they finish so the chat keeps moving forward. // once they finish so the chat keeps moving forward.
@@ -349,7 +349,7 @@ describe("MessageBubble", () => {
id: "tool-1", id: "tool-1",
name: "readFile", name: "readFile",
arguments: { path: "/tmp/demo.txt" }, arguments: { path: "/tmp/demo.txt" },
status: "executing", status: "in_progress",
}, },
{ {
type: "toolResponse", type: "toolResponse",
@@ -374,7 +374,7 @@ describe("MessageBubble", () => {
id: "tool-1", id: "tool-1",
name: "readFile", name: "readFile",
arguments: {}, arguments: {},
status: "executing", status: "in_progress",
}, },
{ {
type: "toolResponse", type: "toolResponse",
@@ -410,7 +410,7 @@ describe("MessageBubble", () => {
id: "tool-1", id: "tool-1",
name: "readFile", name: "readFile",
arguments: {}, arguments: {},
status: "executing", status: "in_progress",
}, },
{ {
type: "toolResponse", type: "toolResponse",
@@ -79,7 +79,7 @@ describe("ToolChainCards", () => {
toolItems={[ toolItems={[
pair("Shell · npm test", { completed: true }), pair("Shell · npm test", { completed: true }),
pair("Shell · npm build", { pair("Shell · npm build", {
status: "executing", status: "in_progress",
completed: false, completed: false,
}), }),
]} ]}
@@ -97,7 +97,7 @@ describe("ToolChainCards", () => {
toolItems={[ toolItems={[
pair("Edit · src/a.ts"), pair("Edit · src/a.ts"),
pair("Edit · src/b.ts", { pair("Edit · src/b.ts", {
status: "executing", status: "in_progress",
completed: false, completed: false,
}), }),
]} ]}
@@ -129,7 +129,7 @@ describe("ToolChainCards", () => {
it("auto-collapses a live chain once every step has completed", () => { it("auto-collapses a live chain once every step has completed", () => {
const a = pair("Edit · src/a.ts"); const a = pair("Edit · src/a.ts");
const bRequest = pair("Edit · src/b.ts", { const bRequest = pair("Edit · src/b.ts", {
status: "executing", status: "in_progress",
completed: false, completed: false,
}); });
const { rerender } = render(<ToolChainCards toolItems={[a, bRequest]} />); const { rerender } = render(<ToolChainCards toolItems={[a, bRequest]} />);
@@ -171,7 +171,7 @@ describe("ToolChainCards", () => {
/>, />,
); );
const wrapper = container.querySelector('[data-role="tool-chain-card"]'); const wrapper = container.querySelector('[data-role="tool-chain-card"]');
expect(wrapper?.getAttribute("data-status")).toBe("error"); expect(wrapper?.getAttribute("data-status")).toBe("failed");
}); });
it("renders a step rail row for each child inside a chain", () => { it("renders a step rail row for each child inside a chain", () => {
@@ -181,7 +181,7 @@ describe("ToolChainCards", () => {
pair("Edit · src/a.ts"), pair("Edit · src/a.ts"),
pair("Edit · src/b.ts"), pair("Edit · src/b.ts"),
pair("Edit · src/c.ts", { pair("Edit · src/c.ts", {
status: "executing", status: "in_progress",
completed: false, completed: false,
}), }),
]} ]}
@@ -317,7 +317,7 @@ describe("ToolChainCards", () => {
it("does not surface the chain summary while the chain is still active", () => { it("does not surface the chain summary while the chain is still active", () => {
const a = pair("Edit · src/a.ts"); const a = pair("Edit · src/a.ts");
const b = pair("Edit · src/b.ts", { const b = pair("Edit · src/b.ts", {
status: "executing", status: "in_progress",
completed: false, completed: false,
}); });
if (a.request) { if (a.request) {
@@ -14,10 +14,7 @@ export async function getProviderConfig(
): Promise<ProviderFieldValue[]> { ): Promise<ProviderFieldValue[]> {
const client = await getClient(); const client = await getClient();
const response = await client.goose.GooseProvidersConfigRead({ providerId }); const response = await client.goose.GooseProvidersConfigRead({ providerId });
return response.fields.map((field) => ({ return response.fields;
...field,
value: field.value ?? null,
}));
} }
export async function saveProviderConfig( export async function saveProviderConfig(
@@ -239,7 +239,7 @@ describe("acpNotificationHandler", () => {
type: "toolRequest", type: "toolRequest",
id: "tool-b", id: "tool-b",
name: "grep", name: "grep",
status: "executing", status: "in_progress",
}); });
expect(message.content[2]).toMatchObject({ expect(message.content[2]).toMatchObject({
type: "toolResponse", type: "toolResponse",
@@ -65,7 +65,7 @@ describe("ACP tool call status handling", () => {
expect(assistant?.content[0]).toMatchObject({ expect(assistant?.content[0]).toMatchObject({
type: "toolRequest", type: "toolRequest",
id: "tool-1", id: "tool-1",
status: "error", status: "failed",
}); });
expect(assistant?.content[1]).toMatchObject({ expect(assistant?.content[1]).toMatchObject({
type: "toolResponse", type: "toolResponse",
@@ -114,7 +114,7 @@ describe("ACP tool call status handling", () => {
expect(message.content[0]).toMatchObject({ expect(message.content[0]).toMatchObject({
type: "toolRequest", type: "toolRequest",
id: "tool-1", id: "tool-1",
status: "error", status: "failed",
}); });
expect(message.content[1]).toMatchObject({ expect(message.content[1]).toMatchObject({
type: "toolResponse", type: "toolResponse",
@@ -7,7 +7,6 @@ import { useChatSessionStore } from "@/features/chat/stores/chatSessionStore";
import { getBufferedMessage } from "@/features/chat/hooks/replayBuffer"; import { getBufferedMessage } from "@/features/chat/hooks/replayBuffer";
import type { import type {
ToolCallLocation, ToolCallLocation,
ToolCallStatus,
ToolKind, ToolKind,
ToolRequestContent, ToolRequestContent,
ToolResponseContent, ToolResponseContent,
@@ -51,9 +50,6 @@ interface LivePerf {
} }
const livePerf = new Map<string, 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> { function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value); return typeof value === "object" && value !== null && !Array.isArray(value);
} }
@@ -90,7 +86,7 @@ function locationsFromUpdate(
function toolCallUpdatePatch( function toolCallUpdatePatch(
update: SessionUpdate, update: SessionUpdate,
): Partial<ToolRequestContent> { ): Pick<Partial<ToolRequestContent>, "toolKind" | "locations"> {
const toolKind = toolKindFromUpdate(update); const toolKind = toolKindFromUpdate(update);
const locations = locationsFromUpdate(update); const locations = locationsFromUpdate(update);
@@ -226,7 +222,7 @@ function handleReplay(sessionId: string, update: SessionUpdate): void {
name: update.title, name: update.title,
...identity, ...identity,
arguments: rawInputToArguments(update.rawInput), arguments: rawInputToArguments(update.rawInput),
status: "executing", status: "in_progress",
...toolCallUpdatePatch(update), ...toolCallUpdatePatch(update),
startedAt: created ?? Date.now(), startedAt: created ?? Date.now(),
...(chainSummary ? { chainSummary } : {}), ...(chainSummary ? { chainSummary } : {}),
@@ -276,7 +272,6 @@ function handleReplay(sessionId: string, update: SessionUpdate): void {
} }
} }
if (update.status === "completed" || update.status === "failed") { if (update.status === "completed" || update.status === "failed") {
const toolCallStatus = toolCallStatusFromUpdate(update.status);
const tc = msg.content.find( const tc = msg.content.find(
(c) => c.type === "toolRequest" && c.id === update.toolCallId, (c) => c.type === "toolRequest" && c.id === update.toolCallId,
); );
@@ -287,7 +282,7 @@ function handleReplay(sessionId: string, update: SessionUpdate): void {
...tc, ...tc,
...identity, ...identity,
...toolCallUpdatePatch(update), ...toolCallUpdatePatch(update),
status: toolCallStatus, status: update.status,
} as ToolRequestContent; } as ToolRequestContent;
} }
} }
@@ -356,7 +351,7 @@ function handleLive(sessionId: string, update: SessionUpdate): void {
name: update.title, name: update.title,
...identity, ...identity,
arguments: rawInputToArguments(update.rawInput), arguments: rawInputToArguments(update.rawInput),
status: "executing", status: "in_progress",
...toolCallUpdatePatch(update), ...toolCallUpdatePatch(update),
startedAt: Date.now(), startedAt: Date.now(),
...(chainSummary ? { chainSummary } : {}), ...(chainSummary ? { chainSummary } : {}),
@@ -403,7 +398,7 @@ function handleLive(sessionId: string, update: SessionUpdate): void {
} }
if (update.status === "completed" || update.status === "failed") { if (update.status === "completed" || update.status === "failed") {
const toolCallStatus = toolCallStatusFromUpdate(update.status); const { status: resolvedStatus } = update;
const ownerMessage = store.messagesBySession[sessionId]?.find( const ownerMessage = store.messagesBySession[sessionId]?.find(
(m) => m.id === messageId, (m) => m.id === messageId,
); );
@@ -425,7 +420,7 @@ function handleLive(sessionId: string, update: SessionUpdate): void {
...block, ...block,
...identity, ...identity,
...toolCallUpdatePatch(update), ...toolCallUpdatePatch(update),
status: toolCallStatus, status: resolvedStatus,
} }
: block, : block,
), ),
+2 -2
View File
@@ -59,7 +59,7 @@ export async function listDictationLocalModels(): Promise<
> { > {
const client = await getClient(); const client = await getClient();
const response = await client.goose.GooseDictationModelsList({}); const response = await client.goose.GooseDictationModelsList({});
return response.models as unknown as WhisperModelStatus[]; return response.models;
} }
export async function downloadDictationLocalModel( export async function downloadDictationLocalModel(
@@ -76,7 +76,7 @@ export async function getDictationLocalModelDownloadProgress(
const response = await client.goose.GooseDictationModelsDownloadProgress({ const response = await client.goose.GooseDictationModelsDownloadProgress({
modelId, modelId,
}); });
return (response.progress ?? null) as DictationDownloadProgress | null; return response.progress ?? null;
} }
export async function cancelDictationLocalModelDownload( export async function cancelDictationLocalModelDownload(
@@ -26,7 +26,9 @@ import type {
const textBlock: TextContent = { type: "text", text: "hello" }; const textBlock: TextContent = { type: "text", text: "hello" };
const imageBlock: ImageContent = { const imageBlock: ImageContent = {
type: "image", type: "image",
source: { type: "url", url: "https://img.png" }, data: "",
mimeType: "image/png",
uri: "https://img.png",
}; };
const toolRequestBlock: ToolRequestContent = { const toolRequestBlock: ToolRequestContent = {
type: "toolRequest", type: "toolRequest",
+1 -19
View File
@@ -3,24 +3,6 @@
// a narrow union. // a narrow union.
export type ProviderType = string; 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/ // Avatar type — either a remote URL or a local file in ~/.goose/avatars/
export type Avatar = export type Avatar =
| { type: "url"; value: string } | { type: "url"; value: string }
@@ -86,4 +68,4 @@ export interface CreateAgentRequest {
acpEndpoint?: string; 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 // Chat state machine
export type ChatState = export type ChatState =
| "idle" | "idle"
@@ -67,61 +64,3 @@ export interface Session {
messageCount: number; messageCount: number;
userSetName?: boolean; 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 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 = export type MicrophonePermissionStatus =
| "not_determined" | "not_determined"
| "authorized" | "authorized"
| "denied" | "denied"
| "restricted" | "restricted"
| "unsupported"; | "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, GooseReadResourceResult,
GooseToolMetadata, GooseToolMetadata,
} from "@aaif/goose-sdk"; } 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"; export type ChatAttachmentKind = "image" | "file" | "directory";
@@ -35,55 +77,11 @@ export type ChatAttachmentDraft =
| ChatFileAttachmentDraft | ChatFileAttachmentDraft
| ChatDirectoryAttachmentDraft; | ChatDirectoryAttachmentDraft;
// Message roles // ── Renderer-only content block types ─────────────────────────────────
export type MessageRole = "user" | "assistant" | "system"; //
// These types have no ACP equivalent. They are synthesized by the
/** ACP audience restriction — which roles may see a content block. */ // notification handler from _meta payloads, tool call reductions, or
export type Audience = ("user" | "assistant")[]; // local UI events.
/** 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;
}
export type MessageCompletionStatus = export type MessageCompletionStatus =
| "inProgress" | "inProgress"
@@ -92,9 +90,7 @@ export type MessageCompletionStatus =
| "stopped"; | "stopped";
export interface ToolChainSummary { export interface ToolChainSummary {
/** Lowercase phrase covering the chain's tool calls (e.g. "applied dark mode polish"). */
summary: string; summary: string;
/** Number of tool calls the summary covers. */
count: number; count: number;
} }
@@ -107,15 +103,9 @@ export interface ToolRequestContent {
arguments: Record<string, unknown>; arguments: Record<string, unknown>;
status: ToolCallStatus; status: ToolCallStatus;
toolKind?: ToolKind; toolKind?: ToolKind;
locations?: ToolCallLocation[]; locations?: AcpToolCallLocation[];
/** Epoch ms when the tool call started executing (set on event receipt). */
startedAt?: number; startedAt?: number;
annotations?: ContentAnnotations; annotations?: Annotations;
/**
* 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.
*/
chainSummary?: ToolChainSummary; chainSummary?: ToolChainSummary;
} }
@@ -126,7 +116,7 @@ export interface ToolResponseContent {
result: string; result: string;
structuredContent?: unknown; structuredContent?: unknown;
isError: boolean; isError: boolean;
annotations?: ContentAnnotations; annotations?: Annotations;
} }
export interface McpAppPayload { export interface McpAppPayload {
@@ -155,18 +145,18 @@ export interface McpAppContent {
export interface ThinkingContent { export interface ThinkingContent {
type: "thinking"; type: "thinking";
text: string; text: string;
annotations?: ContentAnnotations; annotations?: Annotations;
} }
export interface RedactedThinkingContent { export interface RedactedThinkingContent {
type: "redactedThinking"; type: "redactedThinking";
annotations?: ContentAnnotations; annotations?: Annotations;
} }
export interface ReasoningContent { export interface ReasoningContent {
type: "reasoning"; type: "reasoning";
text: string; text: string;
annotations?: ContentAnnotations; annotations?: Annotations;
} }
export interface ActionRequiredContent { export interface ActionRequiredContent {
@@ -177,16 +167,18 @@ export interface ActionRequiredContent {
toolName?: string; toolName?: string;
arguments?: Record<string, unknown>; arguments?: Record<string, unknown>;
schema?: Record<string, unknown>; schema?: Record<string, unknown>;
annotations?: ContentAnnotations; annotations?: Annotations;
} }
export interface SystemNotificationContent { export interface SystemNotificationContent {
type: "systemNotification"; type: "systemNotification";
notificationType: "compaction" | "info" | "warning" | "error"; notificationType: "compaction" | "info" | "warning" | "error";
text: string; text: string;
annotations?: ContentAnnotations; annotations?: Annotations;
} }
// ── Message ───────────────────────────────────────────────────────────
export type MessageContent = export type MessageContent =
| TextContent | TextContent
| ImageContent | ImageContent
@@ -217,11 +209,9 @@ export interface MessageMetadata {
agentVisible?: boolean; agentVisible?: boolean;
attachments?: MessageAttachment[]; attachments?: MessageAttachment[];
chips?: MessageChip[]; chips?: MessageChip[];
/** Persona that generated this assistant message (set on send). */
personaId?: string; personaId?: string;
personaName?: string; personaName?: string;
providerId?: string; providerId?: string;
/** Which persona this user message is addressed to. */
targetPersonaId?: string; targetPersonaId?: string;
targetPersonaName?: string; targetPersonaName?: string;
completionStatus?: MessageCompletionStatus; completionStatus?: MessageCompletionStatus;
@@ -235,7 +225,8 @@ export interface Message {
metadata?: MessageMetadata; metadata?: MessageMetadata;
} }
// Type guards for content blocks // ── Type guards ───────────────────────────────────────────────────────
export function isTextContent(c: MessageContent): c is TextContent { export function isTextContent(c: MessageContent): c is TextContent {
return c.type === "text"; return c.type === "text";
} }
@@ -265,7 +256,8 @@ export function isSystemNotification(
return c.type === "systemNotification"; return c.type === "systemNotification";
} }
// Helpers // ── Helpers ───────────────────────────────────────────────────────────
export function getTextContent(message: Message): string { export function getTextContent(message: Message): string {
return message.content return message.content
.filter(isTextContent) .filter(isTextContent)
+1 -7
View File
@@ -11,13 +11,7 @@ export type ProviderSetupMethod = ProviderSetupMethodDto;
export type ProviderGroup = ProviderSetupGroupDto; export type ProviderGroup = ProviderSetupGroupDto;
export type ProviderField = ProviderSetupFieldDto; export type ProviderField = ProviderSetupFieldDto;
export interface ProviderFieldValue { export type { ProviderConfigFieldValueDto as ProviderFieldValue } from "@aaif/goose-sdk";
key: string;
value: string | null;
isSet: boolean;
isSecret: boolean;
required: boolean;
}
export type ProviderCatalogEntry = Omit< export type ProviderCatalogEntry = Omit<
ProviderSetupCatalogEntryDto, ProviderSetupCatalogEntryDto,