Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/browser/components/ChatInput/useCreationWorkspace.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,16 @@ const setupWindow = ({ listBranches, sendMessage }: SetupWindowOptions = {}) =>
if (!args.workspaceId) {
return Promise.resolve({
success: true,
workspaceId: TEST_WORKSPACE_ID,
metadata: TEST_METADATA,
data: {
workspaceId: TEST_WORKSPACE_ID,
metadata: TEST_METADATA,
},
} satisfies WorkspaceSendMessageResult);
}

const existingWorkspaceResult: WorkspaceSendMessageResult = {
success: true,
data: undefined,
data: {},
};
return Promise.resolve(existingWorkspaceResult);
});
Expand Down Expand Up @@ -357,8 +359,10 @@ describe("useCreationWorkspace", () => {
(_args: WorkspaceSendMessageArgs): Promise<WorkspaceSendMessageResult> =>
Promise.resolve({
success: true as const,
workspaceId: TEST_WORKSPACE_ID,
metadata: TEST_METADATA,
data: {
workspaceId: TEST_WORKSPACE_ID,
metadata: TEST_METADATA,
},
})
);
const { workspaceApi } = setupWindow({
Expand Down
9 changes: 5 additions & 4 deletions src/browser/components/ChatInput/useCreationWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,17 @@ export function useCreationWorkspace({
return false;
}

// Check if this is a workspace creation result (has metadata field)
if ("metadata" in result && result.metadata) {
syncCreationPreferences(projectPath, result.metadata.id);
// Check if this is a workspace creation result (has metadata in data)
const { metadata } = result.data;
if (metadata) {
syncCreationPreferences(projectPath, metadata.id);
if (projectPath) {
const pendingInputKey = getInputKey(getPendingScopeId(projectPath));
updatePersistedState(pendingInputKey, "");
}
// Settings are already persisted via useDraftWorkspaceSettings
// Notify parent to switch workspace (clears input via parent unmount)
onWorkspaceCreated(result.metadata);
onWorkspaceCreated(metadata);
setIsSending(false);
return true;
} else {
Expand Down
11 changes: 5 additions & 6 deletions src/common/orpc/schemas/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,13 @@ export const workspace = {
trunkBranch: z.string().optional(),
}).optional(),
}),
output: z.union([
ResultSchema(z.void(), SendMessageErrorSchema),
output: ResultSchema(
z.object({
success: z.literal(true),
workspaceId: z.string(),
metadata: FrontendWorkspaceMetadataSchema,
workspaceId: z.string().optional(),
metadata: FrontendWorkspaceMetadataSchema.optional(),
}),
]),
SendMessageErrorSchema
),
},
resumeStream: {
input: z.object({
Expand Down
21 changes: 8 additions & 13 deletions src/node/orpc/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,35 +199,30 @@ export const router = (authToken?: string) => {
const result = await context.workspaceService.sendMessage(
input.workspaceId,
input.message,
input.options // Cast to avoid Zod vs Interface mismatch
input.options
);

// Type mismatch handling: WorkspaceService returns Result<any>.
// Our schema output is ResultSchema(void, SendMessageError) OR {success:true, ...}
// We need to ensure strict type alignment.
if (!result.success) {
const error =
typeof result.error === "string"
? { type: "unknown" as const, raw: result.error }
: result.error;
return { success: false, error };
}
// If success, it returns different shapes depending on lazy creation.
// If lazy creation happened, it returns {success: true, workspaceId, metadata}
// If regular message, it returns {success: true, data: ...} -> wait, result.data is undefined for normal message?
// SendMessage in AgentSession returns Result<void> mostly.
// But createForFirstMessage returns object.

// Check result shape
// Check if this is a workspace creation result
if ("workspaceId" in result) {
return {
success: true,
workspaceId: result.workspaceId,
metadata: result.metadata,
data: {
workspaceId: result.workspaceId,
metadata: result.metadata,
},
};
}

return { success: true, data: undefined };
// Regular message send (no workspace creation)
return { success: true, data: {} };
}),
resumeStream: t
.input(schemas.workspace.resumeStream.input)
Expand Down
1 change: 1 addition & 0 deletions src/node/services/workspaceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ export class WorkspaceService extends EventEmitter {

const allMetadata = await this.config.getAllWorkspaceMetadata();
const completeMetadata = allMetadata.find((m) => m.id === workspaceId);

if (!completeMetadata) {
return { success: false, error: "Failed to retrieve workspace metadata" };
}
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,12 @@ export async function sendMessage(
return { success: false, error: { type: "unknown", raw } };
}

if (result.success && "workspaceId" in result) {
// Lazy workspace creation path returns metadata/workspaceId; normalize to void success for callers
// Normalize to Result<void> for callers - they just care about success/failure
if (result.success) {
return { success: true, data: undefined };
}

return result;
return { success: false, error: result.error };
}

/**
Expand Down