feat: workspace source-of-truth hard cut

This commit is contained in:
Mohamed Boudra
2026-03-03 23:28:16 +07:00
parent f3796e2ba5
commit cb92f39e73
20 changed files with 1813 additions and 945 deletions
+82
View File
@@ -512,6 +512,45 @@ export const FetchAgentsRequestMessageSchema = z.object({
.optional(),
})
const WorkspaceStateBucketSchema = z.enum([
'needs_input',
'failed',
'running',
'attention',
'done',
])
export const FetchWorkspacesRequestMessageSchema = z.object({
type: z.literal('fetch_workspaces_request'),
requestId: z.string(),
filter: z
.object({
query: z.string().optional(),
projectId: z.string().optional(),
idPrefix: z.string().optional(),
})
.optional(),
sort: z
.array(
z.object({
key: z.enum(['status_priority', 'activity_at', 'name', 'project_id']),
direction: z.enum(['asc', 'desc']),
})
)
.optional(),
page: z
.object({
limit: z.number().int().positive().max(200),
cursor: z.string().min(1).optional(),
})
.optional(),
subscribe: z
.object({
subscriptionId: z.string().optional(),
})
.optional(),
})
export const FetchAgentRequestMessageSchema = z.object({
type: z.literal('fetch_agent_request'),
requestId: z.string(),
@@ -1068,6 +1107,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion('type', [
AbortRequestMessageSchema,
AudioPlayedMessageSchema,
FetchAgentsRequestMessageSchema,
FetchWorkspacesRequestMessageSchema,
FetchAgentRequestMessageSchema,
DeleteAgentRequestMessageSchema,
ArchiveAgentRequestMessageSchema,
@@ -1426,6 +1466,14 @@ export const ProjectPlacementPayloadSchema = z.object({
checkout: ProjectCheckoutLitePayloadSchema,
})
export const WorkspaceDescriptorPayloadSchema = z.object({
id: z.string(),
projectId: z.string(),
name: z.string(),
status: WorkspaceStateBucketSchema,
activityAt: z.string().nullable(),
})
export const AgentUpdateMessageSchema = z.object({
type: z.literal('agent_update'),
payload: z.discriminatedUnion('kind', [
@@ -1488,6 +1536,34 @@ export const FetchAgentsResponseMessageSchema = z.object({
}),
})
export const FetchWorkspacesResponseMessageSchema = z.object({
type: z.literal('fetch_workspaces_response'),
payload: z.object({
requestId: z.string(),
subscriptionId: z.string().nullable().optional(),
entries: z.array(WorkspaceDescriptorPayloadSchema),
pageInfo: z.object({
nextCursor: z.string().nullable(),
prevCursor: z.string().nullable(),
hasMore: z.boolean(),
}),
}),
})
export const WorkspaceUpdateMessageSchema = z.object({
type: z.literal('workspace_update'),
payload: z.discriminatedUnion('kind', [
z.object({
kind: z.literal('upsert'),
workspace: WorkspaceDescriptorPayloadSchema,
}),
z.object({
kind: z.literal('remove'),
id: z.string(),
}),
]),
})
export const FetchAgentResponseMessageSchema = z.object({
type: z.literal('fetch_agent_response'),
payload: z.object({
@@ -2051,9 +2127,11 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion('type', [
RpcErrorMessageSchema,
ArtifactMessageSchema,
AgentUpdateMessageSchema,
WorkspaceUpdateMessageSchema,
AgentStreamMessageSchema,
AgentStatusMessageSchema,
FetchAgentsResponseMessageSchema,
FetchWorkspacesResponseMessageSchema,
FetchAgentResponseMessageSchema,
FetchAgentTimelineResponseMessageSchema,
SendAgentMessageResponseMessageSchema,
@@ -2119,7 +2197,10 @@ export type AgentStreamMessage = z.infer<typeof AgentStreamMessageSchema>
export type AgentStatusMessage = z.infer<typeof AgentStatusMessageSchema>
export type ProjectCheckoutLitePayload = z.infer<typeof ProjectCheckoutLitePayloadSchema>
export type ProjectPlacementPayload = z.infer<typeof ProjectPlacementPayloadSchema>
export type WorkspaceStateBucket = z.infer<typeof WorkspaceStateBucketSchema>
export type WorkspaceDescriptorPayload = z.infer<typeof WorkspaceDescriptorPayloadSchema>
export type FetchAgentsResponseMessage = z.infer<typeof FetchAgentsResponseMessageSchema>
export type FetchWorkspacesResponseMessage = z.infer<typeof FetchWorkspacesResponseMessageSchema>
export type FetchAgentResponseMessage = z.infer<typeof FetchAgentResponseMessageSchema>
export type FetchAgentTimelineResponseMessage = z.infer<
typeof FetchAgentTimelineResponseMessageSchema
@@ -2144,6 +2225,7 @@ export type ActivityLogPayload = z.infer<typeof ActivityLogPayloadSchema>
// Type exports for inbound message types
export type VoiceAudioChunkMessage = z.infer<typeof VoiceAudioChunkMessageSchema>
export type FetchAgentsRequestMessage = z.infer<typeof FetchAgentsRequestMessageSchema>
export type FetchWorkspacesRequestMessage = z.infer<typeof FetchWorkspacesRequestMessageSchema>
export type FetchAgentRequestMessage = z.infer<typeof FetchAgentRequestMessageSchema>
export type SendAgentMessageRequest = z.infer<typeof SendAgentMessageRequestSchema>
export type WaitForFinishRequest = z.infer<typeof WaitForFinishRequestSchema>
@@ -0,0 +1,42 @@
import { describe, expect, test } from 'vitest'
import {
SessionInboundMessageSchema,
SessionOutboundMessageSchema,
} from './messages.js'
describe('workspace message schemas', () => {
test('parses fetch_workspaces_request', () => {
const parsed = SessionInboundMessageSchema.parse({
type: 'fetch_workspaces_request',
requestId: 'req-1',
filter: {
query: 'repo',
projectId: 'remote:github.com/acme/repo',
idPrefix: '/Users/me',
},
sort: [{ key: 'activity_at', direction: 'desc' }],
page: { limit: 50 },
subscribe: {},
})
expect(parsed.type).toBe('fetch_workspaces_request')
})
test('rejects invalid workspace update payload', () => {
const result = SessionOutboundMessageSchema.safeParse({
type: 'workspace_update',
payload: {
kind: 'upsert',
workspace: {
id: '/repo',
projectId: '/repo',
name: '',
status: 'not-a-bucket',
activityAt: null,
},
},
})
expect(result.success).toBe(false)
})
})