mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-30 21:23:04 +00:00
Order ordinary sidebar workspaces by Created or Last prompt, preserve project and pin ordering, and keep later active or unacknowledged work visible beyond the first seven rows. Preserve reveal state through live updates and verify sorting, visibility, compatibility, and rendering behavior.
129 lines
4.5 KiB
TypeScript
129 lines
4.5 KiB
TypeScript
import { writeFile, rm, rename } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { copyPluginExample } from "./helpers/plugin-fixture";
|
|
import { connectNewWorkspaceDaemonClient } from "./helpers/new-workspace";
|
|
import { test as base } from "./fixtures";
|
|
import {
|
|
createCreationScenario,
|
|
createPromptRetryScenario,
|
|
retryNextAgentCreation,
|
|
} from "./helpers/creation";
|
|
import { installDaemonWebSocketGate } from "./helpers/daemon-websocket-gate";
|
|
import { delayBrowserAgentCreatedStatus } from "./helpers/new-workspace";
|
|
|
|
export const test = base.extend<{
|
|
startup: { release(): Promise<void>; fail(): Promise<void> };
|
|
promptRejection: void;
|
|
creation: Awaited<ReturnType<typeof createCreationScenario>>;
|
|
promptRetry: ReturnType<typeof createPromptRetryScenario>;
|
|
agentRetries: Awaited<ReturnType<typeof retryNextAgentCreation>>;
|
|
delayedCreation: Awaited<ReturnType<typeof delayBrowserAgentCreatedStatus>>;
|
|
}>({
|
|
startup: async ({ page }, provide) => {
|
|
void page;
|
|
const client = await connectNewWorkspaceDaemonClient();
|
|
const plugin = await copyPluginExample("lifecycle-logger");
|
|
const gate = path.join(plugin.directory, "startup.txt");
|
|
const failure = path.join(plugin.directory, "startup-failure.txt");
|
|
const previous = await client.getDaemonConfig();
|
|
try {
|
|
await writeFile(gate, "hold");
|
|
await writeFile(failure, "fail");
|
|
await writeFile(
|
|
path.join(plugin.directory, "index.server.ts"),
|
|
`
|
|
import { readFile, rm } from "node:fs/promises";
|
|
export default function contribute(server) {
|
|
return server.before("agent.session_open", async ({ request }) => {
|
|
// Metadata helpers have no workspace and must not consume the one-shot failure.
|
|
if (request.reason !== "create" || !request.workspaceId) return request;
|
|
const gate = ${JSON.stringify(gate)};
|
|
let command = await readFile(gate, "utf8").catch(() => "release");
|
|
while (command === "hold") {
|
|
await new Promise(resolve => setTimeout(resolve, 20));
|
|
command = await readFile(gate, "utf8").catch(() => "release");
|
|
}
|
|
if (command === "fail") {
|
|
await rm(gate, { force: true });
|
|
throw new Error("Creation startup failed for test");
|
|
}
|
|
return request;
|
|
});
|
|
}
|
|
`,
|
|
);
|
|
await client.patchDaemonConfig({ pluginsEnabled: true });
|
|
await client.installDirectoryPlugin(plugin.directory);
|
|
await provide({
|
|
release: () => rm(gate, { force: true }),
|
|
// Replacing the command must not expose a truncated, empty file to the hook.
|
|
fail: () => rename(failure, gate),
|
|
});
|
|
} finally {
|
|
await rm(gate, { force: true });
|
|
await client.removePlugin("lifecycle-logger");
|
|
await client.patchDaemonConfig({ pluginsEnabled: previous.config.pluginsEnabled });
|
|
await plugin.cleanup();
|
|
await client.close();
|
|
}
|
|
},
|
|
promptRejection: async ({ page }, provide) => {
|
|
void page;
|
|
const client = await connectNewWorkspaceDaemonClient();
|
|
const plugin = await copyPluginExample("lifecycle-logger");
|
|
const previous = await client.getDaemonConfig();
|
|
try {
|
|
await writeFile(
|
|
path.join(plugin.directory, "index.server.ts"),
|
|
`
|
|
export default function contribute(server) {
|
|
return server.before("agent.create", ({ request }) => ({
|
|
...request,
|
|
config: {
|
|
...request.config,
|
|
model: "e2e-fast-stream",
|
|
featureValues: { ...request.config.featureValues, mockPromptRejections: 1 },
|
|
},
|
|
}));
|
|
}
|
|
`,
|
|
);
|
|
await client.patchDaemonConfig({ pluginsEnabled: true });
|
|
await client.installDirectoryPlugin(plugin.directory);
|
|
await provide();
|
|
} finally {
|
|
await client.removePlugin("lifecycle-logger");
|
|
await client.patchDaemonConfig({ pluginsEnabled: previous.config.pluginsEnabled });
|
|
await plugin.cleanup();
|
|
await client.close();
|
|
}
|
|
},
|
|
creation: async ({ page }, provide) => {
|
|
const creation = await createCreationScenario(page);
|
|
try {
|
|
await provide(creation);
|
|
} finally {
|
|
await creation.cleanup();
|
|
}
|
|
},
|
|
promptRetry: async ({ page }, provide) => {
|
|
const gate = await installDaemonWebSocketGate(page);
|
|
try {
|
|
await provide(createPromptRetryScenario(page, gate));
|
|
} finally {
|
|
gate.restore();
|
|
}
|
|
},
|
|
agentRetries: async ({ page }, provide) => {
|
|
await provide(await retryNextAgentCreation(page));
|
|
},
|
|
delayedCreation: async ({ page }, provide) => {
|
|
const delayed = await delayBrowserAgentCreatedStatus(page);
|
|
try {
|
|
await provide(delayed);
|
|
} finally {
|
|
delayed.release();
|
|
}
|
|
},
|
|
});
|