fix: migrate workspace/project IDs to string and widen schema enums
- Change workspace id/projectId from z.number() to z.union([z.string(), z.number()]).transform(String) for backward compat - Add "non_git" to projectKind, "local_checkout" to workspaceKind enums - Session converts numeric IDs to strings at the descriptor boundary - Update tests and daemon-client event types to match
This commit is contained in:
@@ -70,8 +70,8 @@ describe("openProjectDirectly", () => {
|
|||||||
requestId: "request-1",
|
requestId: "request-1",
|
||||||
error: null,
|
error: null,
|
||||||
workspace: {
|
workspace: {
|
||||||
id: 1,
|
id: "1",
|
||||||
projectId: 1,
|
projectId: "1",
|
||||||
projectDisplayName: "project",
|
projectDisplayName: "project",
|
||||||
projectRootPath: WORKSPACE_ID,
|
projectRootPath: WORKSPACE_ID,
|
||||||
workspaceDirectory: WORKSPACE_ID,
|
workspaceDirectory: WORKSPACE_ID,
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export type DaemonEvent =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "workspace_update";
|
type: "workspace_update";
|
||||||
workspaceId: number;
|
workspaceId: string;
|
||||||
payload: Extract<SessionOutboundMessage, { type: "workspace_update" }>["payload"];
|
payload: Extract<SessionOutboundMessage, { type: "workspace_update" }>["payload"];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -317,12 +317,12 @@ type WorkspaceUpdatesSubscriptionState = {
|
|||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
filter?: WorkspaceUpdatesFilter;
|
filter?: WorkspaceUpdatesFilter;
|
||||||
isBootstrapping: boolean;
|
isBootstrapping: boolean;
|
||||||
pendingUpdatesByWorkspaceId: Map<number, WorkspaceUpdatePayload>;
|
pendingUpdatesByWorkspaceId: Map<string, WorkspaceUpdatePayload>;
|
||||||
};
|
};
|
||||||
type FetchWorkspacesCursor = {
|
type FetchWorkspacesCursor = {
|
||||||
sort: FetchWorkspacesRequestSort[];
|
sort: FetchWorkspacesRequestSort[];
|
||||||
values: Record<string, string | number | null>;
|
values: Record<string, string | number | null>;
|
||||||
id: number;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SessionRequestError extends Error {
|
class SessionRequestError extends Error {
|
||||||
@@ -2729,8 +2729,8 @@ export class Session {
|
|||||||
labels,
|
labels,
|
||||||
);
|
);
|
||||||
const resolvedWorkspace =
|
const resolvedWorkspace =
|
||||||
typeof msg.workspaceId === "number"
|
msg.workspaceId
|
||||||
? await this.workspaceRegistry.get(msg.workspaceId)
|
? await this.workspaceRegistry.get(Number(msg.workspaceId))
|
||||||
: (await this.findWorkspaceByDirectory(sessionConfig.cwd)) ??
|
: (await this.findWorkspaceByDirectory(sessionConfig.cwd)) ??
|
||||||
(await this.findOrCreateWorkspaceForDirectory(sessionConfig.cwd));
|
(await this.findOrCreateWorkspaceForDirectory(sessionConfig.cwd));
|
||||||
if (!resolvedWorkspace) {
|
if (!resolvedWorkspace) {
|
||||||
@@ -4093,7 +4093,7 @@ export class Session {
|
|||||||
workspaces: Iterable<WorkspaceDescriptorPayload>,
|
workspaces: Iterable<WorkspaceDescriptorPayload>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
for (const workspace of workspaces) {
|
for (const workspace of workspaces) {
|
||||||
const persistedWorkspace = await this.workspaceRegistry.get(workspace.id);
|
const persistedWorkspace = await this.workspaceRegistry.get(Number(workspace.id));
|
||||||
if (!persistedWorkspace) {
|
if (!persistedWorkspace) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -5181,17 +5181,19 @@ export class Session {
|
|||||||
if (cachedShortstat !== undefined) {
|
if (cachedShortstat !== undefined) {
|
||||||
diffStat = cachedShortstat;
|
diffStat = cachedShortstat;
|
||||||
} else {
|
} else {
|
||||||
warmCheckoutShortstatInBackground(workspace.directory);
|
warmCheckoutShortstatInBackground(workspace.directory, undefined, () => {
|
||||||
|
void this.emitWorkspaceUpdateForCwd(workspace.directory);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: workspace.id,
|
id: String(workspace.id),
|
||||||
projectId: workspace.projectId,
|
projectId: String(workspace.projectId),
|
||||||
projectDisplayName: resolvedProjectRecord?.displayName ?? String(workspace.projectId),
|
projectDisplayName: resolvedProjectRecord?.displayName ?? String(workspace.projectId),
|
||||||
projectRootPath: resolvedProjectRecord?.directory ?? workspace.directory,
|
projectRootPath: resolvedProjectRecord?.directory ?? workspace.directory,
|
||||||
workspaceDirectory: workspace.directory,
|
workspaceDirectory: workspace.directory,
|
||||||
projectKind: resolvedProjectRecord?.kind ?? "directory",
|
projectKind: (resolvedProjectRecord?.kind ?? "directory") === "git" ? "git" : "non_git",
|
||||||
workspaceKind: workspace.kind,
|
workspaceKind: workspace.kind === "checkout" ? "local_checkout" : workspace.kind,
|
||||||
name: workspace.displayName,
|
name: workspace.displayName,
|
||||||
status: "done",
|
status: "done",
|
||||||
activityAt: null,
|
activityAt: null,
|
||||||
@@ -5313,7 +5315,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
return spec.direction === "asc" ? base : -base;
|
return spec.direction === "asc" ? base : -base;
|
||||||
}
|
}
|
||||||
return left.id - right.id;
|
return Number(left.id) - Number(right.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private encodeFetchWorkspacesCursor(
|
private encodeFetchWorkspacesCursor(
|
||||||
@@ -5355,7 +5357,7 @@ export class Session {
|
|||||||
id?: unknown;
|
id?: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Array.isArray(payload.sort) || typeof payload.id !== "number") {
|
if (!Array.isArray(payload.sort) || (typeof payload.id !== "number" && typeof payload.id !== "string")) {
|
||||||
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
throw new SessionRequestError("invalid_cursor", "Invalid fetch_workspaces cursor");
|
||||||
}
|
}
|
||||||
if (!payload.values || typeof payload.values !== "object") {
|
if (!payload.values || typeof payload.values !== "object") {
|
||||||
@@ -5403,7 +5405,7 @@ export class Session {
|
|||||||
return {
|
return {
|
||||||
sort: cursorSort,
|
sort: cursorSort,
|
||||||
values: payload.values as Record<string, string | number | null>,
|
values: payload.values as Record<string, string | number | null>,
|
||||||
id: payload.id,
|
id: String(payload.id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5422,7 +5424,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
return spec.direction === "asc" ? base : -base;
|
return spec.direction === "asc" ? base : -base;
|
||||||
}
|
}
|
||||||
return workspace.id - cursor.id;
|
return Number(workspace.id) - Number(cursor.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private matchesWorkspaceFilter(input: {
|
private matchesWorkspaceFilter(input: {
|
||||||
@@ -5511,7 +5513,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private flushBootstrappedWorkspaceUpdates(options?: {
|
private flushBootstrappedWorkspaceUpdates(options?: {
|
||||||
snapshotLatestActivityByWorkspaceId?: Map<number, number>;
|
snapshotLatestActivityByWorkspaceId?: Map<string, number>;
|
||||||
}): void {
|
}): void {
|
||||||
const subscription = this.workspaceUpdatesSubscription;
|
const subscription = this.workspaceUpdatesSubscription;
|
||||||
if (!subscription || !subscription.isBootstrapping) {
|
if (!subscription || !subscription.isBootstrapping) {
|
||||||
@@ -5687,7 +5689,7 @@ export class Session {
|
|||||||
const persistedWorkspace = await this.findWorkspaceByDirectory(normalizedCwd);
|
const persistedWorkspace = await this.findWorkspaceByDirectory(normalizedCwd);
|
||||||
const all = await this.listWorkspaceDescriptorsSnapshot();
|
const all = await this.listWorkspaceDescriptorsSnapshot();
|
||||||
const descriptorsByWorkspaceId = new Map(all.map((entry) => [entry.id, entry] as const));
|
const descriptorsByWorkspaceId = new Map(all.map((entry) => [entry.id, entry] as const));
|
||||||
const workspaceIdsToEmit = persistedWorkspace ? [persistedWorkspace.id] : [];
|
const workspaceIdsToEmit = persistedWorkspace ? [String(persistedWorkspace.id)] : [];
|
||||||
|
|
||||||
for (const nextWorkspaceId of workspaceIdsToEmit) {
|
for (const nextWorkspaceId of workspaceIdsToEmit) {
|
||||||
const workspace = descriptorsByWorkspaceId.get(nextWorkspaceId);
|
const workspace = descriptorsByWorkspaceId.get(nextWorkspaceId);
|
||||||
@@ -5738,7 +5740,7 @@ export class Session {
|
|||||||
|
|
||||||
for (const workspaceCwd of uniqueWorkspaceCwds) {
|
for (const workspaceCwd of uniqueWorkspaceCwds) {
|
||||||
const persistedWorkspace = await this.findWorkspaceByDirectory(workspaceCwd);
|
const persistedWorkspace = await this.findWorkspaceByDirectory(workspaceCwd);
|
||||||
const workspace = persistedWorkspace ? descriptorsByWorkspaceId.get(persistedWorkspace.id) : null;
|
const workspace = persistedWorkspace ? descriptorsByWorkspaceId.get(String(persistedWorkspace.id)) : null;
|
||||||
const nextWorkspace =
|
const nextWorkspace =
|
||||||
workspace && this.matchesWorkspaceFilter({ workspace, filter: subscription.filter })
|
workspace && this.matchesWorkspaceFilter({ workspace, filter: subscription.filter })
|
||||||
? workspace
|
? workspace
|
||||||
@@ -5749,7 +5751,7 @@ export class Session {
|
|||||||
if (persistedWorkspace) {
|
if (persistedWorkspace) {
|
||||||
this.bufferOrEmitWorkspaceUpdate(subscription, {
|
this.bufferOrEmitWorkspaceUpdate(subscription, {
|
||||||
kind: "remove",
|
kind: "remove",
|
||||||
id: persistedWorkspace.id,
|
id: String(persistedWorkspace.id),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -5844,7 +5846,7 @@ export class Session {
|
|||||||
|
|
||||||
const payload = await this.listFetchWorkspacesEntries(request);
|
const payload = await this.listFetchWorkspacesEntries(request);
|
||||||
await this.primeWorkspaceGitWatchFingerprints(payload.entries);
|
await this.primeWorkspaceGitWatchFingerprints(payload.entries);
|
||||||
const snapshotLatestActivityByWorkspaceId = new Map<number, number>();
|
const snapshotLatestActivityByWorkspaceId = new Map<string, number>();
|
||||||
for (const entry of payload.entries) {
|
for (const entry of payload.entries) {
|
||||||
const parsedLatestActivity = entry.activityAt
|
const parsedLatestActivity = entry.activityAt
|
||||||
? Date.parse(entry.activityAt)
|
? Date.parse(entry.activityAt)
|
||||||
@@ -5973,7 +5975,8 @@ export class Session {
|
|||||||
request: Extract<SessionInboundMessage, { type: "archive_workspace_request" }>,
|
request: Extract<SessionInboundMessage, { type: "archive_workspace_request" }>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const existing = await this.workspaceRegistry.get(request.workspaceId);
|
const numericWorkspaceId = Number(request.workspaceId);
|
||||||
|
const existing = await this.workspaceRegistry.get(numericWorkspaceId);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
throw new Error(`Workspace not found: ${request.workspaceId}`);
|
throw new Error(`Workspace not found: ${request.workspaceId}`);
|
||||||
}
|
}
|
||||||
@@ -5981,7 +5984,7 @@ export class Session {
|
|||||||
throw new Error("Use worktree archive for Paseo worktrees");
|
throw new Error("Use worktree archive for Paseo worktrees");
|
||||||
}
|
}
|
||||||
const archivedAt = new Date().toISOString();
|
const archivedAt = new Date().toISOString();
|
||||||
await this.archiveWorkspaceRecord(request.workspaceId, archivedAt);
|
await this.archiveWorkspaceRecord(numericWorkspaceId, archivedAt);
|
||||||
await this.emitWorkspaceUpdateForCwd(existing.directory);
|
await this.emitWorkspaceUpdateForCwd(existing.directory);
|
||||||
this.emit({
|
this.emit({
|
||||||
type: "archive_workspace_response",
|
type: "archive_workspace_response",
|
||||||
|
|||||||
@@ -658,7 +658,7 @@ export const FetchWorkspacesRequestMessageSchema = z.object({
|
|||||||
filter: z
|
filter: z
|
||||||
.object({
|
.object({
|
||||||
query: z.string().optional(),
|
query: z.string().optional(),
|
||||||
projectId: z.number().int().optional(),
|
projectId: z.union([z.string(), z.number()]).transform(String).optional(),
|
||||||
idPrefix: z.string().optional(),
|
idPrefix: z.string().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -757,7 +757,7 @@ export type GitSetupOptions = z.infer<typeof GitSetupOptionsSchema>;
|
|||||||
export const CreateAgentRequestMessageSchema = z.object({
|
export const CreateAgentRequestMessageSchema = z.object({
|
||||||
type: z.literal("create_agent_request"),
|
type: z.literal("create_agent_request"),
|
||||||
config: AgentSessionConfigSchema,
|
config: AgentSessionConfigSchema,
|
||||||
workspaceId: z.number().int().optional(),
|
workspaceId: z.union([z.string(), z.number()]).transform(String).optional(),
|
||||||
worktreeName: z.string().optional(),
|
worktreeName: z.string().optional(),
|
||||||
initialPrompt: z.string().optional(),
|
initialPrompt: z.string().optional(),
|
||||||
clientMessageId: z.string().optional(),
|
clientMessageId: z.string().optional(),
|
||||||
@@ -1102,7 +1102,7 @@ export const OpenProjectRequestSchema = z.object({
|
|||||||
|
|
||||||
export const ArchiveWorkspaceRequestSchema = z.object({
|
export const ArchiveWorkspaceRequestSchema = z.object({
|
||||||
type: z.literal("archive_workspace_request"),
|
type: z.literal("archive_workspace_request"),
|
||||||
workspaceId: z.number().int(),
|
workspaceId: z.union([z.string(), z.number()]).transform(String),
|
||||||
requestId: z.string(),
|
requestId: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1733,13 +1733,13 @@ export const WorkspaceServicePayloadSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const WorkspaceDescriptorPayloadSchema = z.object({
|
export const WorkspaceDescriptorPayloadSchema = z.object({
|
||||||
id: z.number().int(),
|
id: z.union([z.string(), z.number()]).transform(String),
|
||||||
projectId: z.number().int(),
|
projectId: z.union([z.string(), z.number()]).transform(String),
|
||||||
projectDisplayName: z.string(),
|
projectDisplayName: z.string(),
|
||||||
projectRootPath: z.string(),
|
projectRootPath: z.string(),
|
||||||
workspaceDirectory: z.string(),
|
workspaceDirectory: z.string(),
|
||||||
projectKind: z.enum(["git", "directory"]),
|
projectKind: z.enum(["git", "non_git", "directory"]),
|
||||||
workspaceKind: z.enum(["checkout", "worktree"]),
|
workspaceKind: z.enum(["local_checkout", "checkout", "worktree"]),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
status: WorkspaceStateBucketSchema,
|
status: WorkspaceStateBucketSchema,
|
||||||
activityAt: z.string().nullable(),
|
activityAt: z.string().nullable(),
|
||||||
@@ -1841,7 +1841,7 @@ export const WorkspaceUpdateMessageSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
kind: z.literal("remove"),
|
kind: z.literal("remove"),
|
||||||
id: z.number().int(),
|
id: z.union([z.string(), z.number()]).transform(String),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
@@ -1892,7 +1892,7 @@ export const ArchiveWorkspaceResponseMessageSchema = z.object({
|
|||||||
type: z.literal("archive_workspace_response"),
|
type: z.literal("archive_workspace_response"),
|
||||||
payload: z.object({
|
payload: z.object({
|
||||||
requestId: z.string(),
|
requestId: z.string(),
|
||||||
workspaceId: z.number().int(),
|
workspaceId: z.union([z.string(), z.number()]).transform(String),
|
||||||
archivedAt: z.string().nullable(),
|
archivedAt: z.string().nullable(),
|
||||||
error: z.string().nullable(),
|
error: z.string().nullable(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user