/** * sessions domain zod schemas (names derived from map keys: sessionListRequestSchema / * sessionListValueSchema). SessionEvent passthrough = strict envelope (type/seq/time) + wide * data: the merge-extensible event surface keeps an unknown-type branch at the union level, * with no field-level passthrough. SessionId brand cast point: sessionIdSchema, and only there. */ import { z } from 'zod' import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session/types' import type { MessageId } from '@deepseek-ai/dsh-llm/brand' import type { RequestPayload, ResponseValue } from './rpc-map.ts' import type { Wire } from './rpc.schema.ts' import type { HistoryEntry, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, ModelReasoningEffort, ModelTarget, SessionProjectionsBlock, SessionSearchItem, SessionSummary, } from './sessions.ts' import type { ToolEventView } from './events.ts' import type { WorkspaceId } from './workspace.ts' import { SESSION_SEARCH_RESULT_LIMIT, SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS, truncateUnicodeCodePoints, } from './session-search.ts' /** SessionId: one brand cast after shape validation (the only cast point in this domain). */ export const sessionIdSchema = z.string().min(1) as unknown as z.ZodType /** MessageId: one brand cast after non-empty string validation. */ export const messageIdSchema = z.string().min(1) as unknown as z.ZodType /** * WorkspaceId: the workspace domain's one brand cast. Hosted here rather * than in workspace.schema because session.create references it while * workspace.schema references sessionIdSchema — schema modules must stay a * DAG (both casts used at module top level; a cycle is a load-time TDZ). */ export const workspaceIdSchema = z.string().min(1) as unknown as z.ZodType /** SessionEvent passthrough: strict envelope, wide data (the client fold handles unknown types via its documented default). */ export const sessionEventSchema = z.object({ type: z.string(), seq: z.number().int().nonnegative(), time: z.number(), data: z.unknown(), sourceEventSeqs: z.array(z.number()).optional(), surfaceOp: z.unknown().optional(), }) as unknown as z.ZodType /** SessionSummary row of session.list (`projections` reuses the history block's shape and schema). */ export const sessionSummarySchema = z.object({ sessionId: sessionIdSchema, updatedAt: z.number(), running: z.boolean(), blank: z.boolean(), parentSessionId: sessionIdSchema.optional(), origin: z.literal('subagent').optional(), cwd: z.string().optional(), projections: z.lazy(() => sessionProjectionsBlockSchema).optional(), }) as unknown as z.ZodType> /** session.list request payload (cursor is a reserved seat, unimplemented in v1). */ export const sessionListRequestSchema = z.object({ cursor: z.string().optional(), }) satisfies z.ZodType>> /** session.list response value. */ export const sessionListValueSchema: z.ZodType>> = z.object({ items: z.array(sessionSummarySchema), }) /** Fixed wire bound for one interactive sidebar query. */ const SESSION_SEARCH_QUERY_MAX_CHARS = 500 /** session.search request payload. */ export const sessionSearchRequestSchema = z.object({ query: z.string().trim().min(1).max(SESSION_SEARCH_QUERY_MAX_CHARS) .refine(query => !query.includes('\0'), { message: 'search query must not contain NUL' }), }) satisfies z.ZodType>> /** One session.search result. */ export const sessionSearchItemSchema = z.object({ sessionId: sessionIdSchema, snippet: z.string().refine( snippet => truncateUnicodeCodePoints( snippet, SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS, ) === snippet, { message: `search snippet must contain at most ${SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS} Unicode code points` }, ), }) satisfies z.ZodType> /** session.search response value. */ export const sessionSearchValueSchema = z.object({ items: z.array(sessionSearchItemSchema).max(SESSION_SEARCH_RESULT_LIMIT), hasMore: z.boolean(), }) satisfies z.ZodType>> /** session.create request payload (at most one of workspaceId / cwd). */ export const sessionCreateRequestSchema = z.object({ workspaceId: workspaceIdSchema.optional(), cwd: z.string().optional(), sessionId: sessionIdSchema.optional(), }).refine( payload => payload.workspaceId === undefined || payload.cwd === undefined, { message: 'session.create accepts workspaceId or cwd, not both' }, ) satisfies z.ZodType>> /** session.create response value. */ export const sessionCreateValueSchema = z.object({ sessionId: sessionIdSchema, }) satisfies z.ZodType>> /** session.rename request payload (raw title; host-side normalization decides acceptance). */ export const sessionRenameRequestSchema = z.object({ sessionId: sessionIdSchema, title: z.string(), }) satisfies z.ZodType>> /** session.rename response value (the normalized accepted title and its event seq). */ export const sessionRenameValueSchema = z.object({ title: z.string().min(1), seq: z.number().int().nonnegative(), }) satisfies z.ZodType>> /** session.fork request payload (atSeq anchors the completed-turn cut). */ export const sessionForkRequestSchema = z.object({ sessionId: sessionIdSchema, atSeq: z.number().int().nonnegative().optional(), }) satisfies z.ZodType>> /** session.fork response value (the child session id). */ export const sessionForkValueSchema = z.object({ sessionId: sessionIdSchema, }) satisfies z.ZodType>> /** session.history request payload (beforeSeq/maxMessages page backwards from the window tail). */ export const sessionHistoryRequestSchema = z.object({ sessionId: sessionIdSchema, beforeSeq: z.number().int().nonnegative().optional(), maxMessages: z.number().int().positive().optional(), }) satisfies z.ZodType>> /** Complete provider/model target. */ export const modelTargetSchema = z.object({ provider: z.string().min(1), model: z.string().min(1), reasoningEffort: z.string().min(1).optional(), }) satisfies z.ZodType> /** One adapter-owned reasoning effort. */ export const modelReasoningEffortSchema = z.object({ id: z.string().min(1), name: z.string().min(1), description: z.string().optional(), }) satisfies z.ZodType> /** Exact-model reasoning metadata. */ export const modelReasoningSchema = z.object({ efforts: z.array(modelReasoningEffortSchema).min(1), defaultEffort: z.string().min(1).optional(), }) satisfies z.ZodType> /** One advisory model entry inside a provider group. */ export const modelCatalogModelSchema = z.object({ id: z.string().min(1), name: z.string().min(1), description: z.string().optional(), reasoning: modelReasoningSchema.optional(), }) satisfies z.ZodType> /** One successfully loaded provider group. */ export const modelProviderGroupSchema = z.object({ id: z.string().min(1), name: z.string().min(1), models: z.array(modelCatalogModelSchema), }) satisfies z.ZodType> /** One provider-local catalog failure. */ export const modelCatalogFailureSchema = z.object({ id: z.string().min(1), name: z.string().min(1), message: z.string(), }) satisfies z.ZodType> /** * ToolEventView passthrough: lock only the `for` discriminant and the presence * of a card-tagged `view` object. The view interior is a host-computed product * the client reads without echoing back; deep-validating it would hand-copy * the dsh-tools vocabulary into this schema and drift with it. */ export const toolEventViewSchema = z.discriminatedUnion('for', [ z.object({ for: z.literal('call'), view: z.looseObject({ card: z.string() }) }), z.object({ for: z.literal('result'), view: z.looseObject({ card: z.string() }) }), ]) as unknown as z.ZodType /** One session.history item: the session event plus its optional host-computed tool view. */ export const historyEntrySchema: z.ZodType> = z.object({ event: sessionEventSchema, view: toolEventViewSchema.optional(), }) as unknown as z.ZodType> /** * Projection baseline passthrough: `values` stays a wide record — each value * was already parsed by its provider's own schema on the host side, and * deep-validating here would import every domain's schema into the carrier. */ export const sessionProjectionsBlockSchema = z.object({ // -1 = empty log (the lastSeq convention of session/subscribed). asOfSeq: z.number().int().min(-1), values: z.record(z.string(), z.unknown()), }) as unknown as z.ZodType /** session.history response value (projections rides the tail page only). */ export const sessionHistoryValueSchema: z.ZodType>> = z.object({ events: z.array(historyEntrySchema), hasMore: z.boolean(), projections: sessionProjectionsBlockSchema.optional(), }) /** session.models request payload. */ export const sessionModelsRequestSchema = z.object({ sessionId: sessionIdSchema, }) satisfies z.ZodType>> /** session.models response value. */ export const sessionModelsValueSchema = z.object({ current: modelTargetSchema, routable: z.boolean(), groups: z.array(modelProviderGroupSchema), failures: z.array(modelCatalogFailureSchema), }) satisfies z.ZodType>> /** session.selectModel request payload. */ export const sessionSelectModelRequestSchema = z.object({ sessionId: sessionIdSchema, provider: z.string().min(1), model: z.string().min(1), reasoningEffort: z.string().min(1).optional(), }) satisfies z.ZodType>> /** session.selectModel response value. */ export const sessionSelectModelValueSchema = z.object({ selected: modelTargetSchema, }) satisfies z.ZodType>> /** ContentBlock passthrough: core is merge-extensible — the type discriminant envelope is strict, the rest stays wide. */ export const contentBlockSchema = z.looseObject({ type: z.string() }) /** session.prompt request payload. */ export const sessionPromptRequestSchema = z.object({ sessionId: sessionIdSchema, mode: z.union([z.literal('queue'), z.literal('steer')]), content: z.array(contentBlockSchema), }) as unknown as z.ZodType> /** session.prompt response value (the command slot appears only when the prompt dispatched a slash command). */ export const sessionPromptValueSchema = z.object({ accepted: z.literal(true), command: z.object({ kind: z.literal('success'), text: z.string().optional(), }).optional(), }) satisfies z.ZodType>> /** session.updateQueue request payload. */ export const sessionUpdateQueueRequestSchema = z.object({ sessionId: sessionIdSchema, itemId: messageIdSchema, action: z.discriminatedUnion('kind', [ z.object({ kind: z.literal('edit'), content: z.array(contentBlockSchema) }), z.object({ kind: z.literal('remove') }), z.object({ kind: z.literal('steer') }), ]), }) as unknown as z.ZodType> /** session.updateQueue response value. */ export const sessionUpdateQueueValueSchema = z.object({ accepted: z.literal(true), }) satisfies z.ZodType>> /** session.cancel request payload. */ export const sessionCancelRequestSchema = z.object({ sessionId: sessionIdSchema, }) satisfies z.ZodType>> /** session.cancel response value. */ export const sessionCancelValueSchema = z.object({ accepted: z.literal(true), }) satisfies z.ZodType>>