Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
692 lines
19 KiB
TypeScript
692 lines
19 KiB
TypeScript
import "@testing-library/jest-dom/vitest";
|
|
import { act, render } from "@testing-library/react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
createLocalAgentGUIAgentTarget,
|
|
createSharedAgentGUIAgentTarget
|
|
} from "../../agentTargets";
|
|
import type { AgentGUINodeData, NodeFrame } from "../../types";
|
|
import { buildAgentComposerDraft } from "./model/agentComposerDraft";
|
|
import {
|
|
groupAgentGUINodeViewModelFixture,
|
|
type AgentGUINodeViewModelFixtureOverrides,
|
|
type FlatAgentGUINodeViewModelFixture
|
|
} from "./model/AgentGUINodeViewModel.fixture";
|
|
import type {
|
|
AgentComposerDraft,
|
|
AgentGUINodeViewModel
|
|
} from "./model/agentGuiNodeTypes";
|
|
import {
|
|
createAgentStatusController,
|
|
type AgentStatusController,
|
|
type AgentStatusQuery,
|
|
type AgentStatusControllerSnapshot,
|
|
type AgentStatusStreamObserver
|
|
} from "./controller/AgentStatusController";
|
|
import { AgentGUINode } from "./AgentGUINode";
|
|
|
|
const { agentGuiNodeViewSpy, agentProbeInfoPopoverSpy } = vi.hoisted(() => ({
|
|
agentGuiNodeViewSpy: vi.fn(),
|
|
agentProbeInfoPopoverSpy: vi.fn()
|
|
}));
|
|
|
|
let mockViewModel: AgentGUINodeViewModel;
|
|
|
|
vi.mock("../../i18n/index", () => ({
|
|
useTranslation: () => ({ t: (key: string) => key })
|
|
}));
|
|
|
|
vi.mock("./controller/useAgentGUINodeController", () => ({
|
|
useAgentGUINodeController: () => ({
|
|
viewModel: mockViewModel,
|
|
actions: {
|
|
createConversation: vi.fn(),
|
|
selectConversation: vi.fn(),
|
|
submitPrompt: vi.fn(),
|
|
submitGuidancePrompt: vi.fn(),
|
|
showPromptImagesUnsupported: vi.fn(),
|
|
submitApprovalOption: vi.fn(),
|
|
submitInteractivePrompt: vi.fn(),
|
|
interruptCurrentTurn: vi.fn(),
|
|
updateDraftContent: vi.fn(),
|
|
updateComposerSettings: vi.fn(),
|
|
sendQueuedPromptNext: vi.fn(),
|
|
removeQueuedPrompt: vi.fn(),
|
|
editQueuedPrompt: vi.fn(),
|
|
removeProject: vi.fn(),
|
|
toggleProjectPinned: vi.fn(),
|
|
confirmDeleteProjectConversations: vi.fn(),
|
|
requestDeleteConversation: vi.fn(),
|
|
retryActivation: vi.fn(),
|
|
continueInNewConversation: vi.fn(),
|
|
cancelDeleteConversation: vi.fn(),
|
|
confirmDeleteConversation: vi.fn()
|
|
}
|
|
})
|
|
}));
|
|
|
|
vi.mock("./AgentGUINodeView", () => ({
|
|
AgentGUINodeView: (props: unknown) => {
|
|
agentGuiNodeViewSpy(props);
|
|
return <div data-testid="agent-gui-view" />;
|
|
}
|
|
}));
|
|
|
|
vi.mock("../shared/WorkspaceNodeWindow", () => ({
|
|
WorkspaceNodeWindow: ({
|
|
children,
|
|
width,
|
|
height,
|
|
titleAccessory
|
|
}: {
|
|
children: (frame: {
|
|
size: { width: number; height: number };
|
|
}) => React.ReactNode;
|
|
width: number;
|
|
height: number;
|
|
titleAccessory?: React.ReactNode;
|
|
}) => (
|
|
<div>
|
|
{titleAccessory}
|
|
{children({ size: { width, height } })}
|
|
</div>
|
|
)
|
|
}));
|
|
|
|
vi.mock("../shared/CanvasNodeGhostIconButton", () => ({
|
|
CanvasNodeGhostIconButton: ({ children }: { children: React.ReactNode }) => (
|
|
<button>{children}</button>
|
|
)
|
|
}));
|
|
|
|
vi.mock("../shared/canvasNodeChromeIcons", () => ({
|
|
CanvasNodePanelLinedIcon: () => <span />
|
|
}));
|
|
|
|
vi.mock("../workspaceDesktop/view/AgentProbeInfoPopover", () => ({
|
|
AgentProbeInfoPopover: (props: unknown) => {
|
|
agentProbeInfoPopoverSpy(props);
|
|
return <div />;
|
|
}
|
|
}));
|
|
|
|
describe("AgentGUINode status controller integration", () => {
|
|
afterEach(() => {
|
|
agentGuiNodeViewSpy.mockReset();
|
|
agentProbeInfoPopoverSpy.mockReset();
|
|
});
|
|
|
|
it("routes info, config, and slash status through exact-scope requests", () => {
|
|
mockViewModel = createViewModel();
|
|
const queries: AgentStatusQuery[] = [];
|
|
const unsubscribes: ReturnType<typeof vi.fn>[] = [];
|
|
const controller = createAgentStatusController({
|
|
source: {
|
|
open: (query) => {
|
|
queries.push(query);
|
|
const unsubscribe = vi.fn();
|
|
unsubscribes.push(unsubscribe);
|
|
return unsubscribe;
|
|
}
|
|
}
|
|
});
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps({
|
|
runtimeRequests: { agentStatusController: controller }
|
|
})}
|
|
/>
|
|
);
|
|
|
|
const infoProps = agentProbeInfoPopoverSpy.mock.calls.at(-1)?.[0] as {
|
|
onOpen?: () => void;
|
|
};
|
|
act(() => infoProps.onOpen?.());
|
|
let viewProps = latestViewProps();
|
|
act(() => viewProps.onAgentConfigMenuOpen?.());
|
|
viewProps = latestViewProps();
|
|
act(() => viewProps.onSlashStatusOpen?.());
|
|
viewProps = latestViewProps();
|
|
act(() => viewProps.onSlashStatusClose?.());
|
|
|
|
expect(queries).toEqual([
|
|
statusQuery("agent-info"),
|
|
statusQuery("agent-config"),
|
|
statusQuery("slash-status")
|
|
]);
|
|
expect(unsubscribes).toHaveLength(3);
|
|
expect(
|
|
unsubscribes.every((unsubscribe) => unsubscribe.mock.calls.length === 1)
|
|
).toBe(true);
|
|
});
|
|
|
|
it("gives Host account chrome the same exact target on config open", () => {
|
|
const target = {
|
|
...createLocalAgentGUIAgentTarget("tutti-agent"),
|
|
ownership: "self" as const
|
|
};
|
|
mockViewModel = createViewModel({
|
|
selectedAgentTarget: target,
|
|
agentTargets: [target],
|
|
conversationFilter: {
|
|
kind: "agentTarget",
|
|
agentTargetId: target.agentTargetId ?? target.targetId
|
|
}
|
|
});
|
|
const onAgentConfigMenuOpen = vi.fn();
|
|
const renderAgentConfigAccount = vi.fn(() => (
|
|
<div data-testid="host-account">Host account</div>
|
|
));
|
|
const props = createProps();
|
|
render(
|
|
<AgentGUINode
|
|
{...props}
|
|
hostActions={{
|
|
...props.hostActions,
|
|
onAgentConfigMenuOpen
|
|
}}
|
|
renderSlots={{
|
|
agentConfigAccount: renderAgentConfigAccount
|
|
}}
|
|
/>
|
|
);
|
|
|
|
expect(renderAgentConfigAccount).toHaveBeenCalledWith({
|
|
agentTargetId: "local:tutti-agent",
|
|
provider: "tutti-agent",
|
|
label: target.label,
|
|
ownership: "self",
|
|
presentation: "menu"
|
|
});
|
|
expect(latestViewProps().agentConfigAccountContent).toBeTruthy();
|
|
|
|
act(() => latestViewProps().onAgentConfigMenuOpen?.());
|
|
|
|
expect(onAgentConfigMenuOpen).toHaveBeenCalledOnce();
|
|
expect(onAgentConfigMenuOpen).toHaveBeenCalledWith({
|
|
agentTargetId: "local:tutti-agent",
|
|
provider: "tutti-agent",
|
|
label: target.label,
|
|
ownership: "self",
|
|
presentation: "menu"
|
|
});
|
|
});
|
|
|
|
it("renders Host system actions in the all-provider config menu", () => {
|
|
mockViewModel = createViewModel({
|
|
conversationFilter: { kind: "all" }
|
|
});
|
|
const renderAgentConfigSystemActions = vi.fn(() => (
|
|
<div data-testid="host-system-actions">Host system actions</div>
|
|
));
|
|
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps()}
|
|
renderSlots={{
|
|
agentConfigSystemActions: renderAgentConfigSystemActions
|
|
}}
|
|
/>
|
|
);
|
|
|
|
expect(renderAgentConfigSystemActions).toHaveBeenCalledOnce();
|
|
expect(renderAgentConfigSystemActions).toHaveBeenCalledWith({
|
|
presentation: "menu"
|
|
});
|
|
expect(latestViewProps().agentConfigSystemActionsContent).toBeTruthy();
|
|
});
|
|
|
|
it("does not project status from the previous target after a target switch", () => {
|
|
mockViewModel = createViewModel();
|
|
let observer: AgentStatusStreamObserver | null = null;
|
|
const controller = createAgentStatusController({
|
|
source: {
|
|
open: (_query, nextObserver) => {
|
|
observer = nextObserver;
|
|
return vi.fn();
|
|
}
|
|
}
|
|
});
|
|
const props = createProps({
|
|
runtimeRequests: { agentStatusController: controller }
|
|
});
|
|
const { rerender } = render(<AgentGUINode {...props} />);
|
|
let viewProps = latestViewProps();
|
|
act(() => viewProps.onSlashStatusOpen?.());
|
|
act(() => {
|
|
observer?.onFrame({
|
|
kind: "refreshed",
|
|
value: {
|
|
contextState: "unavailable",
|
|
limitsState: "available",
|
|
quotas: [{ quotaType: "weekly", percentRemaining: 80 }]
|
|
}
|
|
});
|
|
});
|
|
expect(latestViewProps().slashStatusOverride?.limits).toHaveLength(1);
|
|
|
|
mockViewModel = createViewModel({
|
|
selectedAgentTarget: createLocalAgentGUIAgentTarget("claude-code"),
|
|
agentTargets: [createLocalAgentGUIAgentTarget("claude-code")]
|
|
});
|
|
rerender(
|
|
<AgentGUINode
|
|
{...props}
|
|
state={createState({ provider: "claude-code" })}
|
|
/>
|
|
);
|
|
viewProps = latestViewProps();
|
|
expect(viewProps.slashStatusOverride?.limits).toEqual([]);
|
|
});
|
|
|
|
it("projects structured usage errors as localized config messages", () => {
|
|
mockViewModel = createViewModel({
|
|
conversationFilter: {
|
|
kind: "agentTarget",
|
|
agentTargetId: "local:codex"
|
|
}
|
|
});
|
|
let observer: AgentStatusStreamObserver | null = null;
|
|
const controller = createAgentStatusController({
|
|
source: {
|
|
open: (_query, nextObserver) => {
|
|
observer = nextObserver;
|
|
return vi.fn();
|
|
}
|
|
}
|
|
});
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps({
|
|
runtimeRequests: { agentStatusController: controller }
|
|
})}
|
|
/>
|
|
);
|
|
|
|
act(() => latestViewProps().onAgentConfigMenuOpen?.());
|
|
act(() => {
|
|
observer?.onFrame({
|
|
kind: "refreshed",
|
|
value: {
|
|
contextState: "unavailable",
|
|
limitsErrorCode: "subscription_required",
|
|
limitsState: "error",
|
|
quotas: []
|
|
}
|
|
});
|
|
});
|
|
|
|
expect(latestViewProps().slashStatusUsageErrorMessage).toBe(
|
|
"agentHost.agentGui.slashStatusUsageSubscriptionRequired"
|
|
);
|
|
});
|
|
|
|
it("disables provider-account usage refresh for a Model Plan target", () => {
|
|
const target = {
|
|
...createLocalAgentGUIAgentTarget("codex"),
|
|
agentTargetId: "workspace-agent:kimi-code",
|
|
targetId: "workspace-agent:kimi-code",
|
|
providerAccountUsageApplicable: false
|
|
};
|
|
mockViewModel = createViewModel({
|
|
selectedAgentTarget: target,
|
|
agentTargets: [target],
|
|
conversationFilter: {
|
|
kind: "agentTarget",
|
|
agentTargetId: "workspace-agent:kimi-code"
|
|
}
|
|
});
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps({
|
|
runtimeRequests: {
|
|
agentStatusController: createAgentStatusController({
|
|
source: { open: vi.fn(() => vi.fn()) }
|
|
})
|
|
}
|
|
})}
|
|
/>
|
|
);
|
|
|
|
expect(latestViewProps().onAgentUsageRefresh).toBeUndefined();
|
|
expect(latestViewProps().onSlashStatusRefresh).toBeUndefined();
|
|
});
|
|
|
|
it("projects the host billing label into the rail config menu", () => {
|
|
mockViewModel = createViewModel({
|
|
conversationFilter: {
|
|
kind: "agentTarget",
|
|
agentTargetId: "local:codex"
|
|
}
|
|
});
|
|
let observer: AgentStatusStreamObserver | null = null;
|
|
const controller = createAgentStatusController({
|
|
source: {
|
|
open: (_query, nextObserver) => {
|
|
observer = nextObserver;
|
|
return vi.fn();
|
|
}
|
|
}
|
|
});
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps({
|
|
runtimeRequests: { agentStatusController: controller }
|
|
})}
|
|
/>
|
|
);
|
|
|
|
act(() => latestViewProps().onAgentConfigMenuOpen?.());
|
|
act(() => {
|
|
observer?.onFrame({
|
|
kind: "refreshed",
|
|
value: {
|
|
accountLabel: "API Usage Billing",
|
|
contextState: "unavailable",
|
|
limitsState: "available",
|
|
quotas: []
|
|
}
|
|
});
|
|
});
|
|
|
|
expect(latestViewProps().providerAuthAccountLabels?.codex).toBe(
|
|
"API Usage Billing"
|
|
);
|
|
});
|
|
|
|
it("switches status controllers synchronously when their query keys match", () => {
|
|
mockViewModel = createViewModel();
|
|
const props = createProps({
|
|
runtimeRequests: {
|
|
agentStatusController: statusControllerWithUsedTokens(10)
|
|
}
|
|
});
|
|
const { rerender } = render(<AgentGUINode {...props} />);
|
|
|
|
expect(
|
|
latestViewProps().slashStatusOverride?.contextWindow?.usedTokens
|
|
).toBe(10);
|
|
|
|
rerender(
|
|
<AgentGUINode
|
|
{...props}
|
|
runtimeRequests={{
|
|
agentStatusController: statusControllerWithUsedTokens(20)
|
|
}}
|
|
/>
|
|
);
|
|
|
|
expect(
|
|
latestViewProps().slashStatusOverride?.contextWindow?.usedTokens
|
|
).toBe(20);
|
|
});
|
|
|
|
it("uses the active conversation binding before raw session hydration", () => {
|
|
const target = createSharedAgentGUIAgentTarget({
|
|
provider: "codex",
|
|
sharedAgentId: "shared-1",
|
|
label: "Shared Codex"
|
|
});
|
|
mockViewModel = createViewModel({
|
|
activeConversationId: "binding-1",
|
|
activeConversation: {
|
|
id: "binding-1",
|
|
agentTargetId: target.agentTargetId ?? target.targetId,
|
|
provider: "codex",
|
|
title: "Shared session",
|
|
status: "completed",
|
|
cwd: "/workspace",
|
|
updatedAtUnixMs: 1
|
|
},
|
|
conversations: [],
|
|
selectedAgentTarget: target,
|
|
agentTargets: [target],
|
|
sessionChrome: {
|
|
auth: null,
|
|
approval: null,
|
|
recovery: null,
|
|
rawState: null
|
|
}
|
|
});
|
|
const open = vi.fn(() => vi.fn());
|
|
const controller = createAgentStatusController({ source: { open } });
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps({
|
|
runtimeRequests: { agentStatusController: controller }
|
|
})}
|
|
/>
|
|
);
|
|
|
|
act(() => latestViewProps().onSlashStatusOpen?.());
|
|
|
|
expect(open).toHaveBeenCalledWith(
|
|
{
|
|
scopeKey: "shared-agent:shared-1",
|
|
agentSessionId: "binding-1",
|
|
reason: "slash-status",
|
|
forceRefresh: false
|
|
},
|
|
expect.any(Object)
|
|
);
|
|
});
|
|
|
|
it("closes only the request owned by the surface that is closing", () => {
|
|
mockViewModel = createViewModel();
|
|
const unsubscribes: ReturnType<typeof vi.fn>[] = [];
|
|
const controller = createAgentStatusController({
|
|
source: {
|
|
open: () => {
|
|
const unsubscribe = vi.fn();
|
|
unsubscribes.push(unsubscribe);
|
|
return unsubscribe;
|
|
}
|
|
}
|
|
});
|
|
render(
|
|
<AgentGUINode
|
|
{...createProps({
|
|
runtimeRequests: { agentStatusController: controller }
|
|
})}
|
|
/>
|
|
);
|
|
|
|
const infoProps = agentProbeInfoPopoverSpy.mock.calls.at(-1)?.[0] as {
|
|
onOpen?: () => void;
|
|
onClose?: () => void;
|
|
};
|
|
act(() => infoProps.onOpen?.());
|
|
act(() => latestViewProps().onSlashStatusOpen?.());
|
|
expect(unsubscribes[0]).toHaveBeenCalledOnce();
|
|
|
|
act(() => infoProps.onClose?.());
|
|
expect(unsubscribes[1]).not.toHaveBeenCalled();
|
|
|
|
act(() => latestViewProps().onSlashStatusClose?.());
|
|
expect(unsubscribes[1]).toHaveBeenCalledOnce();
|
|
|
|
act(() => latestViewProps().onAgentConfigMenuOpen?.());
|
|
act(() => latestViewProps().onAgentConfigMenuClose?.());
|
|
expect(unsubscribes[2]).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
interface CapturedViewProps {
|
|
agentConfigAccountContent?: React.ReactNode;
|
|
agentConfigSystemActionsContent?: React.ReactNode;
|
|
onAgentConfigMenuOpen?: () => void;
|
|
onAgentConfigMenuClose?: () => void;
|
|
onAgentUsageRefresh?: () => void;
|
|
providerAuthAccountLabels?: Partial<Record<string, string>>;
|
|
onSlashStatusOpen?: () => void;
|
|
onSlashStatusClose?: () => void;
|
|
onSlashStatusRefresh?: () => void;
|
|
slashStatusUsageErrorMessage?: string | null;
|
|
slashStatusOverride?: {
|
|
contextWindow?: { usedTokens?: number | null } | null;
|
|
limits?: readonly unknown[];
|
|
} | null;
|
|
}
|
|
|
|
function latestViewProps(): CapturedViewProps {
|
|
return agentGuiNodeViewSpy.mock.calls.at(-1)?.[0] as CapturedViewProps;
|
|
}
|
|
|
|
function statusQuery(reason: AgentStatusQuery["reason"]): AgentStatusQuery {
|
|
return {
|
|
scopeKey: "local:codex",
|
|
agentSessionId: null,
|
|
reason,
|
|
forceRefresh: false
|
|
};
|
|
}
|
|
|
|
function statusControllerWithUsedTokens(
|
|
usedTokens: number
|
|
): AgentStatusController {
|
|
const snapshot: AgentStatusControllerSnapshot = {
|
|
query: statusQuery("slash-status"),
|
|
value: {
|
|
agentSessionId: null,
|
|
contextState: "available",
|
|
contextWindow: { usedTokens, totalTokens: 100 },
|
|
limitsState: "available",
|
|
quotas: []
|
|
},
|
|
phase: "ready",
|
|
isRefreshing: false,
|
|
errorCode: null
|
|
};
|
|
return {
|
|
close: vi.fn(),
|
|
getSnapshot: () => snapshot,
|
|
invalidate: vi.fn(),
|
|
open: vi.fn(),
|
|
subscribe: () => () => {}
|
|
};
|
|
}
|
|
|
|
function createProps(
|
|
overrides: Partial<Parameters<typeof AgentGUINode>[0]> = {}
|
|
): Parameters<typeof AgentGUINode>[0] {
|
|
return {
|
|
identity: {
|
|
nodeId: "agent-gui-1",
|
|
workspaceId: "room-1",
|
|
currentUserId: "user-1",
|
|
title: "Codex"
|
|
},
|
|
workspace: {
|
|
path: "/workspace",
|
|
agentSettings: { avoidGroupingEdits: false }
|
|
},
|
|
frame: {
|
|
position: { x: 80, y: 56 },
|
|
width: 880,
|
|
height: 520,
|
|
desktopSize: { width: 1280, height: 720 },
|
|
isActive: true
|
|
},
|
|
state: createState(),
|
|
runtimeRequests: {},
|
|
hostCapabilities: {},
|
|
hostActions: {
|
|
onClose: vi.fn(),
|
|
onResize: vi.fn<(frame: NodeFrame) => void>(),
|
|
onUpdateNode: vi.fn()
|
|
},
|
|
renderSlots: {},
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
function createState(
|
|
overrides: Partial<AgentGUINodeData> = {}
|
|
): AgentGUINodeData {
|
|
return {
|
|
provider: "codex",
|
|
lastActiveAgentSessionId: null,
|
|
conversationRailWidthPx: null,
|
|
conversationRailCollapsed: false,
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
function createViewModel(
|
|
overrides: AgentGUINodeViewModelFixtureOverrides = {}
|
|
): AgentGUINodeViewModel {
|
|
const draftContent: AgentComposerDraft = buildAgentComposerDraft({
|
|
prompt: ""
|
|
});
|
|
return groupAgentGUINodeViewModelFixture({
|
|
workspaceId: "room-1",
|
|
data: createState(),
|
|
activeConversationId: null,
|
|
activeConversation: null,
|
|
conversations: [],
|
|
userProjects: [],
|
|
conversation: null,
|
|
conversationDetail: null,
|
|
selectedAgentTarget: createLocalAgentGUIAgentTarget("codex"),
|
|
agentTargets: [createLocalAgentGUIAgentTarget("codex")],
|
|
agentTargetsLoading: false,
|
|
conversationFilter: { kind: "all" },
|
|
draftPrompt: "",
|
|
draftContent,
|
|
sessionChrome: {
|
|
auth: null,
|
|
approval: null,
|
|
recovery: null,
|
|
rawState: null
|
|
},
|
|
pendingInteractivePrompt: null,
|
|
queuedPrompts: [],
|
|
queueStatus: "active",
|
|
gate: {
|
|
conversationBusy: false,
|
|
isAwaitingTurnStart: false,
|
|
runtime: {
|
|
status: "ready",
|
|
reason: null,
|
|
sessionRuntimeReason: null
|
|
},
|
|
editor: { status: "editable", reason: null },
|
|
submission: { status: "ready", reason: null }
|
|
},
|
|
isSubmitting: false,
|
|
isInterrupting: false,
|
|
hasPendingSubmitStopTarget: false,
|
|
promptImagesSupported: true,
|
|
availability: "ready",
|
|
listError: null,
|
|
isCreatingConversation: false,
|
|
isLoadingConversations: false,
|
|
isLoadingMessages: false,
|
|
detailError: null,
|
|
deletingConversationId: null,
|
|
deletingConversationTitle: null,
|
|
composerSettings: {
|
|
sessionSettings: null,
|
|
draftSettings: {
|
|
model: null,
|
|
reasoningEffort: null,
|
|
planMode: false,
|
|
permissionModeId: "full-access"
|
|
},
|
|
defaultModel: null,
|
|
defaultReasoningEffort: null,
|
|
supportsModel: false,
|
|
supportsReasoningEffort: true,
|
|
supportsPlanMode: true,
|
|
isSettingsLoading: false,
|
|
modelUnavailable: false,
|
|
reasoningUnavailable: false,
|
|
availableModels: [],
|
|
availableReasoningEfforts: []
|
|
},
|
|
availableCommands: [],
|
|
availableSkills: [],
|
|
...overrides
|
|
} as FlatAgentGUINodeViewModelFixture);
|
|
}
|