mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-01 22:23:07 +00:00
feat(workspaces): choose runtime when creating workspaces
Expose the runtime catalog and carry an explicit runtime ID through workspace creation while preserving omitted-field compatibility for older clients. The runtime POC made useGitActions require WorkspaceGitBoundary, but Command Center mounted that consumer without the boundary and crashed. Bind the active workspace at that consumer. Keep provisional Worktree reservations unpublished until final placement so observers never bind the source checkout as the new workspace. Gate legacy cwd Git compatibility by hostVisiblePath instead of runtime kind because Local and Worktree remain host-visible runtimes.
This commit is contained in:
@@ -2714,6 +2714,65 @@ test("sends first-agent prompt context with workspace.create.request", async ()
|
||||
});
|
||||
});
|
||||
|
||||
test("lists workspace runtimes and sends an explicit creation runtime", async () => {
|
||||
const mock = createMockTransport();
|
||||
const client = new DaemonClient({
|
||||
url: "ws://test",
|
||||
clientId: "clsk_runtime_test",
|
||||
logger: createMockLogger(),
|
||||
reconnect: { enabled: false },
|
||||
transportFactory: () => mock.transport,
|
||||
});
|
||||
clients.push(client);
|
||||
const connectPromise = client.connect();
|
||||
mock.triggerOpen();
|
||||
await connectPromise;
|
||||
|
||||
const listPromise = client.listWorkspaceRuntimes("req-runtime-list");
|
||||
expect(parseSentFrame(mock.sent.at(-1)!)).toEqual({
|
||||
type: "workspace.runtime.list.request",
|
||||
requestId: "req-runtime-list",
|
||||
});
|
||||
mock.triggerMessage(
|
||||
wrapSessionMessage({
|
||||
type: "workspace.runtime.list.response",
|
||||
payload: {
|
||||
requestId: "req-runtime-list",
|
||||
runtimes: [{ runtimeId: "fixture", builtin: false, requiresGitProject: true }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
await expect(listPromise).resolves.toMatchObject({
|
||||
runtimes: [{ runtimeId: "fixture", builtin: false, requiresGitProject: true }],
|
||||
});
|
||||
|
||||
const createPromise = client.createWorkspace(
|
||||
{
|
||||
source: { kind: "directory", path: "/tmp/project" },
|
||||
runtimeId: "fixture",
|
||||
},
|
||||
"req-runtime-create",
|
||||
);
|
||||
expect(parseSentFrame(mock.sent.at(-1)!)).toEqual({
|
||||
type: "workspace.create.request",
|
||||
requestId: "req-runtime-create",
|
||||
runtimeId: "fixture",
|
||||
source: { kind: "directory", path: "/tmp/project" },
|
||||
});
|
||||
mock.triggerMessage(
|
||||
wrapSessionMessage({
|
||||
type: "workspace.create.response",
|
||||
payload: {
|
||||
requestId: "req-runtime-create",
|
||||
workspace: null,
|
||||
setupTerminalId: null,
|
||||
error: "fixture sentinel",
|
||||
},
|
||||
}),
|
||||
);
|
||||
await expect(createPromise).resolves.toMatchObject({ error: "fixture sentinel" });
|
||||
});
|
||||
|
||||
test("sends project.remove.request", async () => {
|
||||
const logger = createMockLogger();
|
||||
const mock = createMockTransport();
|
||||
|
||||
@@ -101,6 +101,7 @@ import type {
|
||||
PaseoConfigRevision,
|
||||
WorkspaceCreateRequest,
|
||||
WorkspaceRecoveryState,
|
||||
WorkspaceRuntimeListPayload,
|
||||
} from "@getpaseo/protocol/messages";
|
||||
import type {
|
||||
AgentPermissionRequest,
|
||||
@@ -2195,6 +2196,14 @@ export class DaemonClient {
|
||||
});
|
||||
}
|
||||
|
||||
async listWorkspaceRuntimes(requestId?: string): Promise<WorkspaceRuntimeListPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
message: { type: "workspace.runtime.list.request" },
|
||||
responseType: "workspace.runtime.list.response",
|
||||
});
|
||||
}
|
||||
|
||||
async openProject(cwd: string, requestId?: string): Promise<OpenProjectPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
@@ -4380,6 +4389,7 @@ export class DaemonClient {
|
||||
async createWorkspace(
|
||||
input: {
|
||||
source: WorkspaceCreateRequest["source"];
|
||||
runtimeId?: string;
|
||||
title?: string;
|
||||
firstAgentContext?: WorkspaceCreateRequest["firstAgentContext"];
|
||||
},
|
||||
@@ -4390,6 +4400,7 @@ export class DaemonClient {
|
||||
message: {
|
||||
type: "workspace.create.request",
|
||||
source: input.source,
|
||||
...(input.runtimeId !== undefined ? { runtimeId: input.runtimeId } : {}),
|
||||
...(input.title !== undefined ? { title: input.title } : {}),
|
||||
...(input.firstAgentContext !== undefined
|
||||
? { firstAgentContext: input.firstAgentContext }
|
||||
|
||||
@@ -18,6 +18,7 @@ import type {
|
||||
SessionOutboundMessage,
|
||||
WorkspaceDescriptorPayload,
|
||||
WorkspaceCreateRequest,
|
||||
WorkspaceRuntimeListPayload,
|
||||
} from "@getpaseo/protocol/messages";
|
||||
import { DaemonClient } from "./daemon-client.js";
|
||||
import type {
|
||||
@@ -150,6 +151,7 @@ export interface PaseoWorkspaceActions {
|
||||
requestId?: string,
|
||||
): Promise<PaseoWorkspaceHandle>;
|
||||
create(options: PaseoWorkspaceCreateOptions): Promise<PaseoWorkspaceHandle>;
|
||||
listRuntimes(requestId?: string): Promise<WorkspaceRuntimeListPayload>;
|
||||
archive(
|
||||
workspace: string | PaseoWorkspaceHandle,
|
||||
requestId?: string,
|
||||
@@ -410,6 +412,7 @@ export function createPaseoClient(config: PaseoClientConfig): PaseoClient {
|
||||
}
|
||||
return createWorkspaceHandle(result.workspace);
|
||||
},
|
||||
listRuntimes: (requestId) => daemonClient.listWorkspaceRuntimes(requestId),
|
||||
archive: (workspace, requestId) =>
|
||||
daemonClient.archiveWorkspace(resolveWorkspaceId(workspace), requestId),
|
||||
subscribe: (handler) =>
|
||||
|
||||
Reference in New Issue
Block a user