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: "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", " ", {
|
||||||
|
|||||||
@@ -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,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"];
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user