mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-27 19:52:49 +00:00
fix(workspaces): restore clean runtime test paths
This commit is contained in:
@@ -1891,6 +1891,62 @@ test("selected workspace availability fails closed without probing or launching
|
||||
}
|
||||
});
|
||||
|
||||
test("host-placed workspaces require host provider availability before create and resume", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-host-workspace-provider-"));
|
||||
const availabilityScopes: Array<FetchCatalogOptions | undefined> = [];
|
||||
let createCalls = 0;
|
||||
let resumeCalls = 0;
|
||||
class UnavailableHostClient extends TestAgentClient {
|
||||
override async isAvailable(options?: FetchCatalogOptions): Promise<boolean> {
|
||||
availabilityScopes.push(options);
|
||||
return false;
|
||||
}
|
||||
|
||||
override async createSession(config: AgentSessionConfig): Promise<AgentSession> {
|
||||
createCalls += 1;
|
||||
return new TestAgentSession(config);
|
||||
}
|
||||
|
||||
override async resumeSession(
|
||||
handle: AgentPersistenceHandle,
|
||||
overrides?: Partial<AgentSessionConfig>,
|
||||
): Promise<AgentSession> {
|
||||
resumeCalls += 1;
|
||||
return await super.resumeSession(handle, overrides);
|
||||
}
|
||||
}
|
||||
const manager = new AgentManager({
|
||||
clients: { codex: new UnavailableHostClient() },
|
||||
logger,
|
||||
resolveProviderWorkspace: async () => null,
|
||||
});
|
||||
const handle: AgentPersistenceHandle = {
|
||||
provider: "codex",
|
||||
sessionId: "host-workspace-session",
|
||||
metadata: { cwd: workdir },
|
||||
};
|
||||
|
||||
try {
|
||||
await expect(
|
||||
manager.createAgent({ provider: "codex", cwd: workdir }, undefined, {
|
||||
workspaceId: "local-workspace",
|
||||
}),
|
||||
).rejects.toThrow("Provider 'codex' is not available");
|
||||
await expect(
|
||||
manager.resumeAgentFromPersistence(handle, undefined, undefined, {
|
||||
workspaceId: "local-workspace",
|
||||
}),
|
||||
).rejects.toThrow("Provider 'codex' is not available");
|
||||
|
||||
expect(availabilityScopes.length).toBeGreaterThanOrEqual(2);
|
||||
expect(availabilityScopes.every((scope) => scope === undefined)).toBe(true);
|
||||
expect(createCalls).toBe(0);
|
||||
expect(resumeCalls).toBe(0);
|
||||
} finally {
|
||||
rmSync(workdir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("selected workspace without a bound capability never checks host availability", async () => {
|
||||
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-missing-workspace-capability-"));
|
||||
let hostAvailabilityCalls = 0;
|
||||
|
||||
@@ -1141,9 +1141,7 @@ export class AgentManager {
|
||||
options.workspaceId,
|
||||
);
|
||||
this.requireEnabledProvider(storedConfig.provider);
|
||||
const client = options.workspaceId
|
||||
? this.requireClient(storedConfig.provider)
|
||||
: await this.requireAvailableClient({ provider: storedConfig.provider });
|
||||
const client = this.requireClient(storedConfig.provider);
|
||||
const launchContext = await this.buildLaunchContext(
|
||||
resolvedAgentId,
|
||||
client,
|
||||
@@ -1151,7 +1149,7 @@ export class AgentManager {
|
||||
options?.env,
|
||||
options.workspaceId,
|
||||
);
|
||||
await this.requireWorkspaceProviderAvailable(
|
||||
await this.requireProviderAvailableForLaunch(
|
||||
client,
|
||||
storedConfig.provider,
|
||||
storedConfig.cwd,
|
||||
@@ -1232,11 +1230,6 @@ export class AgentManager {
|
||||
);
|
||||
|
||||
const client = this.requireClient(handle.provider);
|
||||
if (!options?.workspaceId && !(await client.isAvailable())) {
|
||||
throw new Error(
|
||||
`Provider '${handle.provider}' is not available. Please ensure the CLI is installed.`,
|
||||
);
|
||||
}
|
||||
const launchContext = await this.buildLaunchContext(
|
||||
resolvedAgentId,
|
||||
client,
|
||||
@@ -1244,7 +1237,7 @@ export class AgentManager {
|
||||
undefined,
|
||||
options?.workspaceId,
|
||||
);
|
||||
await this.requireWorkspaceProviderAvailable(
|
||||
await this.requireProviderAvailableForLaunch(
|
||||
client,
|
||||
storedConfig.provider,
|
||||
storedConfig.cwd,
|
||||
@@ -1307,7 +1300,7 @@ export class AgentManager {
|
||||
undefined,
|
||||
input.workspaceId,
|
||||
);
|
||||
await this.requireWorkspaceProviderAvailable(
|
||||
await this.requireProviderAvailableForLaunch(
|
||||
client,
|
||||
input.provider,
|
||||
storedConfig.cwd,
|
||||
@@ -4687,15 +4680,20 @@ export class AgentManager {
|
||||
return launchContext.paseoTools ? stripInternalPaseoMcpServer(launchConfig) : launchConfig;
|
||||
}
|
||||
|
||||
private async requireWorkspaceProviderAvailable(
|
||||
private async requireProviderAvailableForLaunch(
|
||||
client: AgentClient,
|
||||
provider: AgentProvider,
|
||||
cwd: string,
|
||||
workspaceId: string | undefined,
|
||||
launchContext: AgentLaunchContext,
|
||||
): Promise<void> {
|
||||
if (!workspaceId) return;
|
||||
if (!launchContext.workspace) return;
|
||||
if (!launchContext.workspace) {
|
||||
await this.requireAvailableClient({ provider });
|
||||
return;
|
||||
}
|
||||
if (!workspaceId) {
|
||||
throw new Error(`Workspace-scoped provider '${provider}' is missing a workspace ID.`);
|
||||
}
|
||||
const available = await client.isAvailable({
|
||||
scope: "workspace",
|
||||
cwd,
|
||||
|
||||
@@ -51,6 +51,8 @@ import {
|
||||
import type { CheckDetails, ForgeService } from "../services/forge-service.js";
|
||||
import type { GitHubPullRequestStatusFacts } from "../services/github-facts.js";
|
||||
import { bindWorkspaceGitService } from "./test-utils/workspace-git-service-stub.js";
|
||||
import { createStub } from "./test-utils/class-mocks.js";
|
||||
import type { WorkspaceRuntimeService } from "./workspace-runtime/index.js";
|
||||
|
||||
interface SessionHandlerInternals {
|
||||
interruptAgentIfRunning(agentId: string): Promise<void>;
|
||||
@@ -304,6 +306,7 @@ interface SessionForTestOptions {
|
||||
getProjectSlug?: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
workspaceRegistry?: { get: ReturnType<typeof vi.fn> };
|
||||
workspaceRuntime?: WorkspaceRuntimeService;
|
||||
projectRegistry?: Partial<SessionOptions["projectRegistry"]>;
|
||||
terminalManager?: SessionOptions["terminalManager"];
|
||||
serviceProxy?: SessionOptions["serviceProxy"];
|
||||
@@ -477,6 +480,7 @@ function createSessionForTest(options: SessionForTestOptions = {}): Session {
|
||||
get: vi.fn(),
|
||||
list: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
workspaceRuntime: options.workspaceRuntime,
|
||||
scheduleService: asScheduleService(),
|
||||
checkoutDiffManager: asCheckoutDiffManager(checkoutDiffManager),
|
||||
github: asGitHubService(github),
|
||||
@@ -1832,7 +1836,7 @@ describe("session provider refresh cwd routing", () => {
|
||||
test("get_providers_snapshot_request forwards cwd to the provider authority", async () => {
|
||||
const messages: unknown[] = [];
|
||||
const workspaceCwd = resolvePath("/tmp/session-provider-snapshot");
|
||||
const { manager: providerSnapshotManager, getSnapshot } = createProviderSnapshotManagerStub();
|
||||
const { manager: providerSnapshotManager, readSnapshot } = createProviderSnapshotManagerStub();
|
||||
const session = createSessionForTest({ messages, providerSnapshotManager });
|
||||
|
||||
await session.handleMessage({
|
||||
@@ -1841,7 +1845,7 @@ describe("session provider refresh cwd routing", () => {
|
||||
requestId: "snapshot-workspace",
|
||||
});
|
||||
|
||||
expect(getSnapshot).toHaveBeenCalledWith(workspaceCwd);
|
||||
expect(readSnapshot).toHaveBeenCalledWith({ cwd: workspaceCwd });
|
||||
});
|
||||
|
||||
test("preserves legacy model and mode list requests without cwd as global", async () => {
|
||||
@@ -4328,7 +4332,10 @@ describe("session stash mutation handling", () => {
|
||||
describe("session paseo worktree creation handling", () => {
|
||||
test("forces workspace git refreshes for the source repo and created worktree", async () => {
|
||||
const workspaceGitService = { getSnapshot: vi.fn().mockResolvedValue({}) };
|
||||
const session = createSessionForTest({ workspaceGitService });
|
||||
const session = createSessionForTest({
|
||||
workspaceGitService,
|
||||
workspaceRuntime: createStub<WorkspaceRuntimeService>({}),
|
||||
});
|
||||
paseoWorktreeServiceMocks.createPaseoWorktree.mockResolvedValue({
|
||||
repoRoot: "/tmp/repo",
|
||||
worktree: {
|
||||
|
||||
@@ -148,7 +148,10 @@ export function findByType<T extends SessionOutboundMessage["type"]>(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ProviderSnapshotManagerSpies {
|
||||
getSnapshot: ReturnType<typeof vi.fn<[cwd?: string], ProviderSnapshotEntry[]>>;
|
||||
getSnapshot: ReturnType<
|
||||
typeof vi.fn<[cwd?: string, workspaceId?: string], ProviderSnapshotEntry[]>
|
||||
>;
|
||||
readSnapshot: ReturnType<typeof vi.fn<[unknown], Promise<ProviderSnapshotEntry[]>>>;
|
||||
refreshSnapshotForCwd: ReturnType<typeof vi.fn<[unknown], Promise<void>>>;
|
||||
refreshSettingsSnapshot: ReturnType<typeof vi.fn<[unknown], Promise<void>>>;
|
||||
warmUpSnapshotForCwd: ReturnType<typeof vi.fn<[unknown], Promise<void>>>;
|
||||
@@ -175,7 +178,13 @@ export interface ProviderSnapshotManagerSpies {
|
||||
export function createProviderSnapshotManagerStub(): {
|
||||
manager: ProviderSnapshotManager;
|
||||
} & ProviderSnapshotManagerSpies {
|
||||
const getSnapshot = vi.fn<[cwd?: string], ProviderSnapshotEntry[]>(() => []);
|
||||
const getSnapshot = vi.fn<[cwd?: string, workspaceId?: string], ProviderSnapshotEntry[]>(
|
||||
() => [],
|
||||
);
|
||||
const readSnapshot = vi.fn<[unknown], Promise<ProviderSnapshotEntry[]>>(async (options) => {
|
||||
const { cwd, workspaceId } = options as { cwd?: string | null; workspaceId?: string };
|
||||
return getSnapshot(cwd ?? undefined, workspaceId);
|
||||
});
|
||||
const refreshSnapshotForCwd = vi.fn<[unknown], Promise<void>>(async () => {});
|
||||
const refreshSettingsSnapshot = vi.fn<[unknown], Promise<void>>(async () => {});
|
||||
const warmUpSnapshotForCwd = vi.fn<[unknown], Promise<void>>(async () => {});
|
||||
@@ -218,6 +227,7 @@ export function createProviderSnapshotManagerStub(): {
|
||||
const destroy = vi.fn<[], void>();
|
||||
const stub = {
|
||||
getSnapshot,
|
||||
readSnapshot,
|
||||
refreshSnapshotForCwd,
|
||||
refreshSettingsSnapshot,
|
||||
warmUpSnapshotForCwd,
|
||||
@@ -244,6 +254,7 @@ export function createProviderSnapshotManagerStub(): {
|
||||
return {
|
||||
manager,
|
||||
getSnapshot,
|
||||
readSnapshot,
|
||||
refreshSnapshotForCwd,
|
||||
refreshSettingsSnapshot,
|
||||
warmUpSnapshotForCwd,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
TerminalStreamOpcode,
|
||||
} from "@getpaseo/protocol/terminal-stream-protocol";
|
||||
import { CLIENT_CAPS } from "@getpaseo/protocol/client-capabilities";
|
||||
import type { WorkspaceRuntimeService } from "./workspace-runtime/index.js";
|
||||
|
||||
type SocketListener = (...args: unknown[]) => void;
|
||||
|
||||
@@ -293,6 +294,12 @@ function createServer(options?: {
|
||||
undefined,
|
||||
undefined,
|
||||
createProviderSnapshotManagerStub().manager,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
createStub<WorkspaceRuntimeService>({}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user