mirror of
https://github.com/aaif-goose/goose.git
synced 2026-07-03 14:10:03 +02:00
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:
@@ -141,11 +141,8 @@ describe("useChat attachments", () => {
|
||||
{ type: "text", text: "" },
|
||||
{
|
||||
type: "image",
|
||||
source: {
|
||||
type: "base64",
|
||||
mediaType: "image/png",
|
||||
data: "abc123",
|
||||
},
|
||||
data: "abc123",
|
||||
mimeType: "image/png",
|
||||
},
|
||||
]);
|
||||
expect(mockAcpSendMessage).toHaveBeenCalledWith("session-1", " ", {
|
||||
|
||||
@@ -94,7 +94,7 @@ function markMessageStopped(sessionId: string, messageId: string) {
|
||||
completionStatus: "stopped",
|
||||
},
|
||||
content: message.content.map((block) =>
|
||||
block.type === "toolRequest" && block.status === "executing"
|
||||
block.type === "toolRequest" && block.status === "in_progress"
|
||||
? { ...block, status: "stopped" }
|
||||
: block,
|
||||
),
|
||||
@@ -215,11 +215,8 @@ export function useChat(
|
||||
for (const img of images) {
|
||||
userMessage.content.push({
|
||||
type: "image",
|
||||
source: {
|
||||
type: "base64",
|
||||
mediaType: img.mimeType,
|
||||
data: img.base64,
|
||||
},
|
||||
data: img.base64,
|
||||
mimeType: img.mimeType,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,13 +74,13 @@ describe("getToolItemStatus", () => {
|
||||
|
||||
it("treats response.isError as error", () => {
|
||||
expect(getToolItemStatus(pair("a", { name: "x" }, { isError: true }))).toBe(
|
||||
"error",
|
||||
"failed",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses request status when no response yet", () => {
|
||||
expect(getToolItemStatus(pair("a", { status: "executing" }))).toBe(
|
||||
"executing",
|
||||
expect(getToolItemStatus(pair("a", { status: "in_progress" }))).toBe(
|
||||
"in_progress",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -89,17 +89,17 @@ describe("getChainAggregateStatus", () => {
|
||||
it("prefers error over pending and executing", () => {
|
||||
expect(
|
||||
getChainAggregateStatus([
|
||||
pair("a", { status: "executing" }),
|
||||
pair("a", { status: "in_progress" }),
|
||||
pair("b", { name: "x" }, { isError: true }),
|
||||
pair("c", { status: "pending" }),
|
||||
]),
|
||||
).toBe("error");
|
||||
).toBe("failed");
|
||||
});
|
||||
|
||||
it("prefers stopped over executing/pending when there is no error", () => {
|
||||
expect(
|
||||
getChainAggregateStatus([
|
||||
pair("a", { status: "executing" }),
|
||||
pair("a", { status: "in_progress" }),
|
||||
pair("b", { status: "stopped" }),
|
||||
]),
|
||||
).toBe("stopped");
|
||||
@@ -109,9 +109,9 @@ describe("getChainAggregateStatus", () => {
|
||||
expect(
|
||||
getChainAggregateStatus([
|
||||
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", () => {
|
||||
|
||||
@@ -23,7 +23,7 @@ export function getToolItemName(item: ToolChainItem): string {
|
||||
|
||||
export function getToolItemStatus(item: ToolChainItem): ToolCallStatus {
|
||||
if (item.response) {
|
||||
return item.response.isError ? "error" : "completed";
|
||||
return item.response.isError ? "failed" : "completed";
|
||||
}
|
||||
return item.request?.status ?? "completed";
|
||||
}
|
||||
@@ -35,10 +35,10 @@ export function getToolItemStatus(item: ToolChainItem): ToolCallStatus {
|
||||
export function getChainAggregateStatus(
|
||||
items: ToolChainItem[],
|
||||
): 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) === "executing"))
|
||||
return "executing";
|
||||
if (items.some((i) => getToolItemStatus(i) === "in_progress"))
|
||||
return "in_progress";
|
||||
if (items.some((i) => getToolItemStatus(i) === "pending")) return "pending";
|
||||
return "completed";
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { ToolPart } from "@/shared/ui/ai-elements/tool";
|
||||
|
||||
export const toolStatusMap: Record<ToolCallStatus, ToolPart["state"]> = {
|
||||
pending: "input-streaming",
|
||||
executing: "input-available",
|
||||
in_progress: "input-available",
|
||||
completed: "output-available",
|
||||
error: "output-error",
|
||||
failed: "output-error",
|
||||
stopped: "output-denied",
|
||||
};
|
||||
|
||||
@@ -219,10 +219,7 @@ function renderContentBlock(
|
||||
}
|
||||
case "image": {
|
||||
const ic = content as ImageContent;
|
||||
const src =
|
||||
ic.source.type === "base64"
|
||||
? `data:${ic.source.mediaType};base64,${ic.source.data}`
|
||||
: ic.source.url;
|
||||
const src = ic.uri ?? `data:${ic.mimeType};base64,${ic.data}`;
|
||||
return (
|
||||
<ClickableImage
|
||||
key={`image-${index}`}
|
||||
|
||||
@@ -46,7 +46,7 @@ function useElapsedTime(status: ToolCallStatus, startedAt?: number) {
|
||||
const [elapsed, setElapsed] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "executing") {
|
||||
if (status === "in_progress") {
|
||||
const origin = startedAt ?? Date.now();
|
||||
setElapsed(Math.floor((Date.now() - origin) / 1000));
|
||||
const interval = setInterval(() => {
|
||||
@@ -282,7 +282,7 @@ export function ToolCallAdapter({
|
||||
[args, name],
|
||||
);
|
||||
const elapsedSeconds =
|
||||
status === "executing" && elapsed >= 3 ? elapsed : undefined;
|
||||
status === "in_progress" && elapsed >= 3 ? elapsed : undefined;
|
||||
|
||||
const { resolveMarkdownHref, openResolvedPath } = useArtifactPolicyContext();
|
||||
|
||||
|
||||
@@ -17,25 +17,25 @@ import type { ToolCallStatus } from "@/shared/types/messages";
|
||||
export type { ToolChainItem };
|
||||
|
||||
const STEP_BULLET_ICON: Record<
|
||||
Exclude<ToolCallStatus, "error" | "stopped">,
|
||||
Exclude<ToolCallStatus, "failed" | "stopped">,
|
||||
LucideIcon
|
||||
> = {
|
||||
pending: CircleIcon,
|
||||
executing: ClockIcon,
|
||||
in_progress: ClockIcon,
|
||||
completed: Check,
|
||||
};
|
||||
|
||||
const STEP_BULLET_CLASS: Record<
|
||||
Exclude<ToolCallStatus, "error" | "stopped">,
|
||||
Exclude<ToolCallStatus, "failed" | "stopped">,
|
||||
string
|
||||
> = {
|
||||
pending: "text-muted-foreground/70",
|
||||
executing: "text-muted-foreground animate-pulse",
|
||||
in_progress: "text-muted-foreground animate-pulse",
|
||||
completed: "text-muted-foreground",
|
||||
};
|
||||
|
||||
function ChainStepBullet({ status }: { status: ToolCallStatus }) {
|
||||
if (status === "error") {
|
||||
if (status === "failed") {
|
||||
return (
|
||||
<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 summary = summarizeToolChainSteps(primaryItems);
|
||||
const isActiveChain =
|
||||
aggregateStatus === "executing" || aggregateStatus === "pending";
|
||||
aggregateStatus === "in_progress" || aggregateStatus === "pending";
|
||||
// Chains that mount as already-complete (history replay) start collapsed;
|
||||
// live chains mount mid-execution, stay open while running, and auto-collapse
|
||||
// once they finish so the chat keeps moving forward.
|
||||
|
||||
@@ -349,7 +349,7 @@ describe("MessageBubble", () => {
|
||||
id: "tool-1",
|
||||
name: "readFile",
|
||||
arguments: { path: "/tmp/demo.txt" },
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
},
|
||||
{
|
||||
type: "toolResponse",
|
||||
@@ -374,7 +374,7 @@ describe("MessageBubble", () => {
|
||||
id: "tool-1",
|
||||
name: "readFile",
|
||||
arguments: {},
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
},
|
||||
{
|
||||
type: "toolResponse",
|
||||
@@ -410,7 +410,7 @@ describe("MessageBubble", () => {
|
||||
id: "tool-1",
|
||||
name: "readFile",
|
||||
arguments: {},
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
},
|
||||
{
|
||||
type: "toolResponse",
|
||||
|
||||
@@ -79,7 +79,7 @@ describe("ToolChainCards", () => {
|
||||
toolItems={[
|
||||
pair("Shell · npm test", { completed: true }),
|
||||
pair("Shell · npm build", {
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
completed: false,
|
||||
}),
|
||||
]}
|
||||
@@ -97,7 +97,7 @@ describe("ToolChainCards", () => {
|
||||
toolItems={[
|
||||
pair("Edit · src/a.ts"),
|
||||
pair("Edit · src/b.ts", {
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
completed: false,
|
||||
}),
|
||||
]}
|
||||
@@ -129,7 +129,7 @@ describe("ToolChainCards", () => {
|
||||
it("auto-collapses a live chain once every step has completed", () => {
|
||||
const a = pair("Edit · src/a.ts");
|
||||
const bRequest = pair("Edit · src/b.ts", {
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
completed: false,
|
||||
});
|
||||
const { rerender } = render(<ToolChainCards toolItems={[a, bRequest]} />);
|
||||
@@ -171,7 +171,7 @@ describe("ToolChainCards", () => {
|
||||
/>,
|
||||
);
|
||||
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", () => {
|
||||
@@ -181,7 +181,7 @@ describe("ToolChainCards", () => {
|
||||
pair("Edit · src/a.ts"),
|
||||
pair("Edit · src/b.ts"),
|
||||
pair("Edit · src/c.ts", {
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
completed: false,
|
||||
}),
|
||||
]}
|
||||
@@ -317,7 +317,7 @@ describe("ToolChainCards", () => {
|
||||
it("does not surface the chain summary while the chain is still active", () => {
|
||||
const a = pair("Edit · src/a.ts");
|
||||
const b = pair("Edit · src/b.ts", {
|
||||
status: "executing",
|
||||
status: "in_progress",
|
||||
completed: false,
|
||||
});
|
||||
if (a.request) {
|
||||
|
||||
@@ -14,10 +14,7 @@ export async function getProviderConfig(
|
||||
): Promise<ProviderFieldValue[]> {
|
||||
const client = await getClient();
|
||||
const response = await client.goose.GooseProvidersConfigRead({ providerId });
|
||||
return response.fields.map((field) => ({
|
||||
...field,
|
||||
value: field.value ?? null,
|
||||
}));
|
||||
return response.fields;
|
||||
}
|
||||
|
||||
export async function saveProviderConfig(
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user