First Release of Claw3D (#11)

Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Luke The Dev
2026-03-19 23:14:04 -05:00
committed by GitHub
parent 5ea96b2650
commit 4fa4f13558
431 changed files with 105438 additions and 14 deletions
+368
View File
@@ -0,0 +1,368 @@
import { createElement, useEffect } from "react";
import { act, render } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useRuntimeSyncController } from "@/features/agents/operations/useRuntimeSyncController";
import type { AgentState } from "@/features/agents/state/store";
import {
executeAgentReconcileCommands,
runAgentReconcileOperation,
} from "@/features/agents/operations/agentReconcileOperation";
import {
executeHistorySyncCommands,
runHistorySyncOperation,
} from "@/features/agents/operations/historySyncOperation";
import type { GatewayGapInfo } from "@/lib/gateway/GatewayClient";
vi.mock("@/features/agents/operations/historySyncOperation", () => ({
runHistorySyncOperation: vi.fn(async () => []),
executeHistorySyncCommands: vi.fn(),
}));
vi.mock("@/features/agents/operations/agentReconcileOperation", () => ({
runAgentReconcileOperation: vi.fn(async () => []),
executeAgentReconcileCommands: vi.fn(),
}));
const createAgent = (overrides?: Partial<AgentState>): AgentState => ({
agentId: "agent-1",
name: "Agent One",
sessionKey: "agent:agent-1:main",
status: "idle",
sessionCreated: true,
awaitingUserInput: false,
hasUnseenActivity: false,
outputLines: [],
lastResult: null,
lastDiff: null,
runId: null,
runStartedAt: null,
streamText: null,
thinkingTrace: null,
latestOverride: null,
latestOverrideKind: null,
lastAssistantMessageAt: null,
lastActivityAt: null,
latestPreview: null,
lastUserMessage: null,
draft: "",
sessionSettingsSynced: true,
historyLoadedAt: null,
historyFetchLimit: null,
historyFetchedCount: null,
historyMaybeTruncated: false,
toolCallingEnabled: true,
showThinkingTraces: true,
model: "openai/gpt-5",
thinkingLevel: "medium",
avatarSeed: "seed-1",
avatarUrl: null,
...(overrides ?? {}),
});
type RuntimeSyncControllerValue = ReturnType<typeof useRuntimeSyncController>;
type RenderControllerContext = {
getValue: () => RuntimeSyncControllerValue;
rerenderWith: (
overrides: Partial<Parameters<typeof useRuntimeSyncController>[0]>
) => void;
unmount: () => void;
dispatch: ReturnType<typeof vi.fn>;
clearRunTracking: ReturnType<typeof vi.fn>;
call: ReturnType<typeof vi.fn>;
onGap: ReturnType<typeof vi.fn>;
getGapHandler: () => ((info: GatewayGapInfo) => void) | null;
unsubscribeGap: ReturnType<typeof vi.fn>;
};
const renderController = (
overrides?: Partial<Parameters<typeof useRuntimeSyncController>[0]>
): RenderControllerContext => {
const dispatch = vi.fn();
const clearRunTracking = vi.fn();
const call = vi.fn(async (method: string) => {
if (method === "status") {
return { sessions: { recent: [], byAgent: [] } };
}
if (method === "sessions.preview") {
return { ts: 123, previews: [] };
}
return {};
});
let gapHandler: ((info: GatewayGapInfo) => void) | null = null;
const unsubscribeGap = vi.fn();
const onGap = vi.fn((handler: (info: GatewayGapInfo) => void) => {
gapHandler = handler;
return unsubscribeGap;
});
let currentParams: Parameters<typeof useRuntimeSyncController>[0] = {
client: {
call,
onGap,
} as never,
status: "connected",
agents: [createAgent({ status: "running", historyLoadedAt: 1000, runId: "run-1" })],
focusedAgentId: null,
focusedAgentRunning: false,
dispatch,
clearRunTracking,
isDisconnectLikeError: () => false,
defaultHistoryLimit: 200,
maxHistoryLimit: 5000,
...(overrides ?? {}),
};
const valueRef: { current: RuntimeSyncControllerValue | null } = { current: null };
const Probe = ({
params,
onValue,
}: {
params: Parameters<typeof useRuntimeSyncController>[0];
onValue: (value: RuntimeSyncControllerValue) => void;
}) => {
const value = useRuntimeSyncController(params);
useEffect(() => {
onValue(value);
}, [onValue, value]);
return createElement("div", { "data-testid": "probe" }, "ok");
};
const rendered = render(
createElement(Probe, {
params: currentParams,
onValue: (value) => {
valueRef.current = value;
},
})
);
return {
getValue: () => {
if (!valueRef.current) {
throw new Error("runtime sync controller value unavailable");
}
return valueRef.current;
},
rerenderWith: (nextOverrides) => {
currentParams = {
...currentParams,
...nextOverrides,
};
rendered.rerender(
createElement(Probe, {
params: currentParams,
onValue: (value) => {
valueRef.current = value;
},
})
);
},
unmount: () => {
rendered.unmount();
},
dispatch,
clearRunTracking,
call,
onGap,
getGapHandler: () => gapHandler,
unsubscribeGap,
};
};
describe("useRuntimeSyncController", () => {
const mockedRunHistorySyncOperation = vi.mocked(runHistorySyncOperation);
const mockedExecuteHistorySyncCommands = vi.mocked(executeHistorySyncCommands);
const mockedRunAgentReconcileOperation = vi.mocked(runAgentReconcileOperation);
const mockedExecuteAgentReconcileCommands = vi.mocked(executeAgentReconcileCommands);
beforeEach(() => {
vi.useFakeTimers();
mockedRunHistorySyncOperation.mockReset();
mockedRunHistorySyncOperation.mockResolvedValue([]);
mockedExecuteHistorySyncCommands.mockReset();
mockedRunAgentReconcileOperation.mockReset();
mockedRunAgentReconcileOperation.mockResolvedValue([]);
mockedExecuteAgentReconcileCommands.mockReset();
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it("runs reconcile immediately and every 3000ms while connected then cleans up", async () => {
const ctx = renderController({
focusedAgentId: null,
focusedAgentRunning: false,
});
await act(async () => {
await Promise.resolve();
});
expect(mockedRunAgentReconcileOperation).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(2999);
expect(mockedRunAgentReconcileOperation).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(1);
expect(mockedRunAgentReconcileOperation).toHaveBeenCalledTimes(2);
await vi.advanceTimersByTimeAsync(3000);
expect(mockedRunAgentReconcileOperation).toHaveBeenCalledTimes(3);
ctx.unmount();
await vi.advanceTimersByTimeAsync(6000);
expect(mockedRunAgentReconcileOperation).toHaveBeenCalledTimes(3);
});
it("polls focused running history every 4500ms and stops when focus no longer running", async () => {
const ctx = renderController({
agents: [createAgent({ status: "running", historyLoadedAt: 1234 })],
focusedAgentId: "agent-1",
focusedAgentRunning: true,
});
await act(async () => {
await Promise.resolve();
});
expect(mockedRunHistorySyncOperation).toHaveBeenCalledTimes(1);
await vi.advanceTimersByTimeAsync(4500);
expect(mockedRunHistorySyncOperation).toHaveBeenCalledTimes(2);
ctx.rerenderWith({
agents: [createAgent({ status: "idle", historyLoadedAt: 1234 })],
focusedAgentId: "agent-1",
focusedAgentRunning: false,
});
await vi.advanceTimersByTimeAsync(9000);
expect(mockedRunHistorySyncOperation).toHaveBeenCalledTimes(2);
});
it("bootstraps history only for connected sessions missing loaded history", async () => {
renderController({
status: "connected",
focusedAgentId: null,
focusedAgentRunning: false,
agents: [
createAgent({ agentId: "agent-1", sessionCreated: true, historyLoadedAt: null }),
createAgent({ agentId: "agent-2", sessionCreated: true, historyLoadedAt: 1234 }),
createAgent({ agentId: "agent-3", sessionCreated: false, historyLoadedAt: null }),
],
});
await act(async () => {
await Promise.resolve();
});
const bootstrappedAgentIds = mockedRunHistorySyncOperation.mock.calls
.map(([arg]) => (arg as { agentId: string }).agentId)
.filter((agentId) => agentId === "agent-1" || agentId === "agent-2" || agentId === "agent-3");
expect(bootstrappedAgentIds).toContain("agent-1");
expect(bootstrappedAgentIds).not.toContain("agent-2");
expect(bootstrappedAgentIds).not.toContain("agent-3");
});
it("loads summary snapshot when status transitions to connected", async () => {
const ctx = renderController({
status: "disconnected",
focusedAgentId: null,
focusedAgentRunning: false,
agents: [createAgent({ sessionCreated: true, historyLoadedAt: 1234 })],
});
expect(ctx.call).not.toHaveBeenCalledWith("status", {});
ctx.rerenderWith({ status: "connected" });
await act(async () => {
await Promise.resolve();
});
expect(ctx.call).toHaveBeenCalledWith("status", {});
expect(ctx.call).toHaveBeenCalledWith("sessions.preview", {
keys: ["agent:agent-1:main"],
limit: 8,
maxChars: 240,
});
});
it("handles gap recovery by triggering summary refresh and reconcile", async () => {
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const ctx = renderController({
agents: [createAgent({ status: "running", historyLoadedAt: 1234, runId: "run-1" })],
});
await act(async () => {
await Promise.resolve();
});
expect(ctx.onGap).toHaveBeenCalledTimes(1);
const handler = ctx.getGapHandler();
if (!handler) {
throw new Error("expected gap handler to be registered");
}
mockedRunAgentReconcileOperation.mockClear();
ctx.call.mockClear();
await act(async () => {
handler({ expected: 10, received: 11 });
await Promise.resolve();
});
expect(ctx.call).toHaveBeenCalledWith("status", {});
expect(ctx.call).toHaveBeenCalledWith("sessions.preview", {
keys: ["agent:agent-1:main"],
limit: 8,
maxChars: 240,
});
expect(mockedRunAgentReconcileOperation).toHaveBeenCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith("Gateway event gap expected 10, received 11.");
});
it("unsubscribes gap listener on unmount", () => {
const ctx = renderController();
ctx.unmount();
expect(ctx.unsubscribeGap).toHaveBeenCalledTimes(1);
});
it("clears history in-flight tracking when requested", async () => {
const inFlightSeen: boolean[] = [];
mockedRunHistorySyncOperation.mockImplementation(
async ({ agentId, getAgent, inFlightSessionKeys }) => {
const agent = getAgent(agentId);
if (!agent) return [];
const sessionKey = agent.sessionKey;
inFlightSeen.push(inFlightSessionKeys.has(sessionKey));
inFlightSessionKeys.add(sessionKey);
return [];
}
);
const ctx = renderController({
status: "disconnected",
agents: [createAgent({ sessionKey: "agent:agent-1:main", historyLoadedAt: null })],
focusedAgentId: null,
focusedAgentRunning: false,
});
await act(async () => {
await ctx.getValue().loadAgentHistory("agent-1");
});
await act(async () => {
await ctx.getValue().loadAgentHistory("agent-1");
});
act(() => {
ctx.getValue().clearHistoryInFlight("agent:agent-1:main");
});
await act(async () => {
await ctx.getValue().loadAgentHistory("agent-1");
});
expect(inFlightSeen).toEqual([false, true, false]);
});
});