Merge remote-tracking branch 'upstream/master' into feat/produced-files-folder

This commit is contained in:
ZiyaZhang
2026-08-11 05:58:13 -07:00
126 changed files with 7555 additions and 215 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/host/apiproxy/README.md
README.md: 8ade64849193df61a48effaea5739907fda901bf
README.zh.md: 5634100ff2a34f507f3e922729a6781ee5c508a5
README.md: 541ebdb7a6802286b9b698575136534486387a8c
README.zh.md: 595ef03c24873272fa78ad67289b264458083614

View File

@@ -36,6 +36,8 @@ Session titles ride the generic projection pair like every other domain — the
Session model selection is a session-domain contract. `session.models` returns the current `ModelSelection` separately from provider-grouped advisory models, exact-model reasoning metadata, and provider-local lookup failures. The selection may be absent from the groups and is never injected as a synthetic row; clients can prompt for another selection without turning the directory into a routing whitelist. `session.selectModel` validates the optional adapter-owned reasoning effort and assigns the complete selection for the next prompt assembly. Catalog membership is not validation: an adapter may resolve an unlisted model, while an unavailable provider or unsupported effort returns `model-unavailable`. `session.models` additionally reports `routable`: whether an adapter currently serves the selected provider. This is deliberately not derivable from the groups because an adapter may serve an unadvertised model. `session.prompt` refuses on the same fact with `model-unavailable` before opening a turn; a disabled composer is a client affordance, and the method remains callable.
`session.prompt` and `subagent.prompt` accept optional request-local `clientTimeZone` provenance. When present, the Host validates and canonicalizes `UTC` or an IANA Area/Location before Agent entry, rejects invalid input with `invalid-time-zone`, and records the canonical value on that exact `user-rpc` message beside its `rpcId`. The value is not Session, connection, create, resume, or fork state; non-browser callers may omit it.
Pending queued input is a live control-plane contract, not conversation history. The gateway derives the complete `next-turn` queue from durable `agent/inbox/spliced` mutations and broadcasts authoritative `session/queue` snapshots after each change and on reconnect; pending `next-step` steering stays outside this Web projection. Within `next-step`, user-origin messages carry the `steering` placement while injected context (approval notices, task completion, attached snapshots) carries `context` and is not surfaced until claimed. The message-local `agent/inbox/inserted`, `claimed`, and `discarded` notifications remain available to lifecycle observers but do not build the queue view. `session.updateQueue` addresses one `MessageId`; edit and remove mutate the attached Agent through `Inbox.splice()`. A claim's pure deletion splice wins races before pre-step admission, so a later operation returns `queue-item-not-found`. `session.cancel` aborts only the active turn and preserves pending inbox work; after cancellation reaches quiescence and the closing turn flushes, AgentLoop claims the next waking message in FIFO order, and the browser never resends or promotes it. Queue operations never resume a cold session, and the client never infers retirement from turn or status events.
Background tasks ride the same live-push posture. When `ctx.tasks` is composed, the gateway subscribes to its change feed and broadcasts a whole `session/tasks` snapshot after every registry commit that alters what a session can see — registration, the stopping transition, settlement, and owner-disposal removal — plus a subscription baseline for each session that already has tasks (an absent baseline is the empty set; a change that empties a set still sends `[]`). A change carrying an owner reads through that exact `Agent`, so a push stays correct while its scope tears down; the baseline reads `ctx.agents.get(sessionId)`, which yields only unowned tasks for a session with no live Agent and never resumes a cold one. An unowned change fans out to every subscribed session, because unowned tasks are visible to every caller. The wire `TaskView` drops `ownerSession`, `reported`, and `outputLimitBytes`: the frame's own `sessionId` carries the first, and the other two are internal notice and model-presentation policy. A composition without the registry emits no such frames.

View File

@@ -36,6 +36,8 @@ Settings 分节中的 `reasoningEffort` 在 agent-default-model 插件配置中
会话模型选择属于会话领域约定。`session.models` 将当前 `ModelSelection` 与按提供方分组的建议性模型、精确模型的推理reasoning元数据和逐提供方查询失败记录分开返回。该选择可能不在这些分组中也绝不会作为合成行注入客户端可以提示用户作出另一项选择而无需把目录变成路由白名单。`session.selectModel` 校验由适配器持有的可选推理强度,并指定下次组装提示词时使用的完整选择。目录成员关系不构成校验:适配器可以解析未列出的模型,而不可用的提供方或不受支持的推理强度会返回 `model-unavailable``session.models` 还会报告 `routable`,即当前是否有适配器为所选提供方提供服务。该值刻意不从分组推导,因为适配器可以服务未公布的模型。`session.prompt` 会依据同一事实,在开启轮次之前以 `model-unavailable` 拒绝;客户端禁用 composer 只是提示性设计,这个方法始终可被调用。
`session.prompt``subagent.prompt` 接受可选的请求本地 `clientTimeZone` 来源信息。若提供该值Host 会在进入 Agent 前校验 `UTC` 或 IANA Area/Location 并将其规范化;无效输入以 `invalid-time-zone` 拒绝,规范值则与 `rpcId` 一起记录在这条确切的 `user-rpc` 消息上。该值不属于 Session、连接、create、resume 或 fork 状态;非浏览器调用方可以省略它。
待处理的 queued 输入属于实时控制平面约定,而非对话历史。网关根据持久 `agent/inbox/spliced` 变更派生完整的 `next-turn` 队列,并在每次变更后及重连时广播权威 `session/queue` 快照;待处理的 `next-step` steering中途引导不进入此 Web 投影。在 `next-step` 内,用户来源的消息携带 `steering` placement而注入上下文审批通知、任务完成、附加快照携带 `context`,领取前不对外呈现。面向单条消息的 `agent/inbox/inserted``claimed``discarded` 通知仍供生命周期观察方使用,但不用于构建队列视图。`session.updateQueue` 通过 `MessageId` 寻址单个项;编辑和移除经已挂载 Agent 的 `Inbox.splice()` 修改队列。claim 的纯删除 splice 会在 pre-step 准入前赢得竞态,因此之后的操作返回 `queue-item-not-found``session.cancel` 仅中止活动轮次并保留待处理 inbox 工作;取消达到完全停稳且结束中的轮次完成 flush 后AgentLoop 按 FIFO 顺序认领下一条可唤醒消息,浏览器绝不重发或提升它。队列操作绝不恢复冷会话,客户端也绝不根据轮次或状态事件推断某项已退出队列。
后台任务沿用同一种实时推送姿态。当组合中有 `ctx.tasks` 时,网关订阅它的变更订阅,并在注册表每一次改变某个会话可见内容的提交后——注册、转入 stopping、结算以及 owner 销毁时的移除——广播一份完整的 `session/tasks` 快照,另外为每个已经有任务的会话发送订阅 baseline没有 baseline 即表示空集;把集合清空的那次变更仍然发送 `[]`)。带 owner 的变更通过那个确切的 `Agent` 读取,因此推送在其 scope 拆除期间依然正确baseline 读 `ctx.agents.get(sessionId)`,对没有活体 Agent 的会话只得到无主任务,且绝不恢复冷会话。无主变更向每一个已订阅会话扇出,因为无主任务对所有调用方可见。线路上的 `TaskView` 丢弃 `ownerSession``reported``outputLimitBytes`:第一个由帧自身的 `sessionId` 携带,另外两个分别是内部通知位和模型呈现策略。没有该注册表的组合不发出这类帧。

View File

@@ -247,6 +247,25 @@ function referencedImage(events: readonly SessionEvent[], attachmentId: string):
*/
const PRODUCT_SETTINGS_NAMESPACES = new Set(['ui-onboarding', AGENT_PRESET_SETTINGS_NAMESPACE])
/** Strict browser-zone profile: UTC or an IANA Area/Location-style identifier. */
const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/
/** Validate and canonicalize one browser-supplied IANA zone at the wire boundary. */
function canonicalClientTimeZone(value: string): string | undefined {
if (value.length === 0 || value.trim() !== value
|| (value !== 'UTC' && !IANA_TIME_ZONE.test(value))) return undefined
try {
const canonical = new Intl.DateTimeFormat('en-US', { timeZone: value })
.resolvedOptions().timeZone
/* v8 ignore next -- Intl returns UTC or a canonical IANA Area/Location for accepted input. */
if (canonical !== 'UTC' && !IANA_TIME_ZONE.test(canonical)) return undefined
return canonical
} catch {
// Intl rejects unsupported zone names; the RPC maps that parser rejection below.
return undefined
}
}
/** Read live abort state across awaits without treating it as synchronously immutable. */
function isAborted(signal: AbortSignal): boolean {
return signal.aborted
@@ -2333,12 +2352,26 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro
},
async prompt(request) {
const { sessionId, mode, content } = request.payload
const { sessionId, mode, content, clientTimeZone } = request.payload
const canonicalTimeZone = clientTimeZone === undefined
? undefined
: canonicalClientTimeZone(clientTimeZone)
if (clientTimeZone !== undefined && canonicalTimeZone === undefined) {
return err(request, {
code: 'invalid-time-zone',
message: 'clientTimeZone must be UTC or a valid IANA Area/Location name',
details: { value: clientTimeZone },
})
}
const resolved = await turnAgentFor<{ accepted: true }>(request, sessionId)
if ('refused' in resolved) return resolved.refused
const agent = resolved.agent
// The rpcId rides MessageSource into user/message (merge declaration in api/sessions.ts; provisional correlation).
const source: MessageSource = { kind: 'user', rpcId: request.rpcId }
// Request identity and optional browser zone ride the exact durable user message.
const source: MessageSource = {
kind: 'user',
rpcId: request.rpcId,
...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
}
const hasImage = content.some(part => part.type === 'image')
const admit = async (): Promise<RpcResponse<{ accepted: true }>> => {
try {
@@ -2595,7 +2628,17 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro
},
async prompt(request, signal) {
const { parentSessionId, childSessionId, content } = request.payload
const { parentSessionId, childSessionId, content, clientTimeZone } = request.payload
const canonicalTimeZone = clientTimeZone === undefined
? undefined
: canonicalClientTimeZone(clientTimeZone)
if (clientTimeZone !== undefined && canonicalTimeZone === undefined) {
return err(request, {
code: 'invalid-time-zone',
message: 'clientTimeZone must be UTC or a valid IANA Area/Location name',
details: { value: clientTimeZone },
})
}
const parent = ctx.agents.get(parentSessionId)
if (parent === undefined) {
return err(request, {
@@ -2610,7 +2653,11 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro
if (verified.error !== undefined) return err(request, verified.error)
try {
const messageId = await ctx.subagents.followup(parent, childSessionId, content, {
source: { kind: 'user', rpcId: request.rpcId },
source: {
kind: 'user',
rpcId: request.rpcId,
...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
},
signal,
})
return ok(request, { messageId })

View File

@@ -37,6 +37,7 @@ export const rpcErrorSchema: z.ZodType<RpcError> = z.discriminatedUnion('code',
z.object({ code: z.literal('session-not-found'), message: z.string(), details: z.object({ sessionId: z.string() }) }),
z.object({ code: z.literal('model-unavailable'), message: z.string(), details: z.object({ provider: z.string(), model: z.string() }) }),
z.object({ code: z.literal('session-conflict'), message: z.string(), details: z.object({ sessionId: z.string(), requestedCwd: z.string(), existingCwd: z.string().optional() }) }),
z.object({ code: z.literal('invalid-time-zone'), message: z.string(), details: z.object({ value: z.string() }) }),
z.object({ code: z.literal('workspace-attach-failed'), message: z.string(), details: z.object({ sessionId: z.string(), workspaceId: z.string() }) }),
z.object({ code: z.literal('workspace-not-found'), message: z.string(), details: z.object({ workspaceId: z.string() }) }),
z.object({ code: z.literal('workspace-invalid-path'), message: z.string(), details: z.object({ path: z.string() }) }),

View File

@@ -35,6 +35,7 @@ export interface RpcErrorDetailsMap {
'session-not-found': { sessionId: SessionId }
'model-unavailable': { provider: string; model: string }
'session-conflict': { sessionId: SessionId; requestedCwd: string; existingCwd?: string }
'invalid-time-zone': { value: string }
'workspace-attach-failed': { sessionId: SessionId; workspaceId: string }
'workspace-not-found': { workspaceId: string }
'workspace-invalid-path': { path: string }

View File

@@ -265,11 +265,12 @@ export const promptContentPartSchema = z.discriminatedUnion('type', [
z.object({ type: z.literal('image'), mediaType: imageMediaTypeSchema, data: z.string(), name: z.string().optional() }),
])
/** session.prompt request payload. */
/** session.prompt request payload, including optional browser-local request provenance. */
export const sessionPromptRequestSchema = z.object({
sessionId: sessionIdSchema,
mode: z.union([z.literal('queue'), z.literal('steer')]),
content: z.array(promptContentPartSchema),
clientTimeZone: z.string().optional(),
}) as unknown as z.ZodType<RequestPayload<'session.prompt'>>
/** session.prompt response value (the command slot appears only when the prompt dispatched a slash command). */

View File

@@ -21,9 +21,10 @@ declare module '@deepseek-ai/dsh-llm' {
* The prompt's rpcId is passed through MessageSource into the `user/message` event
* (the client uses it to reconcile the optimistically
* echoed provisional message with the event stream). kind stays `'user'` — the model face
* carries no transport vocabulary; rpcId is an extra durable-JSON field passed back to the client with the event.
* carries no transport vocabulary; rpcId and the optional Host-validated browser zone are
* durable JSON fields passed back to the client with the event.
*/
'user-rpc': { kind: 'user'; rpcId: RpcId }
'user-rpc': { kind: 'user'; rpcId: RpcId; clientTimeZone?: string }
}
}
@@ -308,8 +309,19 @@ export interface SessionsApi {
fork(request: RpcRequest<{ sessionId: SessionId; atSeq?: number }>):
Promise<RpcResponse<{ sessionId: SessionId }>>
/** Sends text and temporary image bytes after durable host admission. Session-backed subagents reject with `agent-busy`. */
prompt(request: RpcRequest<{ sessionId: SessionId; mode: 'queue' | 'steer'; content: PromptContentPart[] }>):
/**
* Sends text and temporary image bytes to an ordinary session Agent after durable host admission.
* Browser callers attach their current IANA zone;
* the Host validates, canonicalizes, and records it on that exact user message. Omission remains
* valid for non-browser callers. Session-backed subagents reject with `agent-busy` and use
* `subagent.prompt`.
*/
prompt(request: RpcRequest<{
sessionId: SessionId
mode: 'queue' | 'steer'
content: PromptContentPart[]
clientTimeZone?: string
}>):
Promise<RpcResponse<{ accepted: true; command?: { kind: 'success'; text?: string } }>>
/** Reads one durable image after proving that this session's log references its id. */

View File

@@ -67,6 +67,7 @@ export const subagentPromptRequestSchema = z.object({
childSessionId: sessionIdSchema,
mode: z.literal('continuable'),
content: z.array(contentBlockSchema),
clientTimeZone: z.string().optional(),
}) as unknown as z.ZodType<RequestPayload<'subagent.prompt'>>
/** subagent.interrupt request payload. */

View File

@@ -92,10 +92,15 @@ export interface SubagentsApi {
* Delivers human content to a continuable child through the exact live
* parent's continuation owner. Success identifies the message accepted by
* the child's FIFO inbox; later execution is independent of this request.
* Optional browser-zone provenance is validated and logged on that message.
*/
prompt(
request: RpcRequest<
Extract<SubagentAddress, { mode: 'continuable' }> & { content: ContentBlock[] }
Extract<SubagentAddress, { mode: 'continuable' }> & {
content: ContentBlock[]
/** Optional browser zone sampled for this exact human prompt. */
clientTimeZone?: string
}
>,
signal: AbortSignal,
): Promise<RpcResponse<SubagentPromptReceipt>>

View File

@@ -468,6 +468,80 @@ describe('subagent ownership fence', () => {
expect(response.result.ok).toBe(true)
expect(followup).toHaveBeenCalledOnce()
})
it('canonicalizes a supplied browser zone on the exact prompt and rejects invalid names', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
await ctx.plugin(AgentRegistry)
await ctx.plugin(UserInteractionService)
const session = ctx.sessions.create(sid('session-browser-zone'), { meta: { cwd: '/proj' } })
const followup = vi.fn()
const agent = { id: session.id, session, status: 'idle', ctx, followup } as unknown as Agent
ctx.agents.register(agent)
const api = createApiProxy(ctx, {
defaultModelSelection: () => ({ provider: 'p', model: 'm' }),
cwd: '/tmp',
})
const alias = 'US/Pacific'
const canonical = new Intl.DateTimeFormat('en-US', { timeZone: alias })
.resolvedOptions().timeZone
const zonedRequest = request({
sessionId: agent.id,
mode: 'queue' as const,
content: [{ type: 'text' as const, text: 'zoned work' }],
clientTimeZone: alias,
})
await expect(api.sessions.prompt(zonedRequest)).resolves.toMatchObject({
result: { ok: true },
})
expect(followup).toHaveBeenNthCalledWith(1, expect.objectContaining({
source: { kind: 'user', rpcId: zonedRequest.rpcId, clientTimeZone: canonical },
}))
const utcRequest = request({
sessionId: agent.id,
mode: 'queue' as const,
content: [{ type: 'text' as const, text: 'UTC work' }],
clientTimeZone: 'UTC',
})
await expect(api.sessions.prompt(utcRequest)).resolves.toMatchObject({
result: { ok: true },
})
expect(followup).toHaveBeenNthCalledWith(2, expect.objectContaining({
source: { kind: 'user', rpcId: utcRequest.rpcId, clientTimeZone: 'UTC' },
}))
const unzonedRequest = request({
sessionId: agent.id,
mode: 'queue' as const,
content: [{ type: 'text' as const, text: 'headless work' }],
})
await expect(api.sessions.prompt(unzonedRequest)).resolves.toMatchObject({
result: { ok: true },
})
expect(followup).toHaveBeenNthCalledWith(3, expect.objectContaining({
source: { kind: 'user', rpcId: unzonedRequest.rpcId },
}))
for (const clientTimeZone of ['', ' UTC', 'CST', 'Not/A_Real_Zone']) {
const invalid = await api.sessions.prompt(request({
sessionId: agent.id,
mode: 'queue' as const,
content: [{ type: 'text' as const, text: 'invalid zone' }],
clientTimeZone,
}))
expect(invalid.result).toEqual({
ok: false,
error: {
code: 'invalid-time-zone',
message: 'clientTimeZone must be UTC or a valid IANA Area/Location name',
details: { value: clientTimeZone },
},
})
}
expect(followup).toHaveBeenCalledTimes(3)
})
})
describe('degenerate composition (no persistence, no factory)', () => {

View File

@@ -50,7 +50,10 @@ function bench(options: {
_parent: unknown,
_childId: SessionId,
_content: unknown,
_delivery: { source: { kind: string; rpcId: RpcId }; signal: AbortSignal },
_delivery: {
source: { kind: string; rpcId: RpcId; clientTimeZone?: string }
signal: AbortSignal
},
) => options.followupError === undefined
? Promise.resolve('message-1')
: Promise.reject(options.followupError))
@@ -270,6 +273,43 @@ describe('subagent gateway', () => {
)
})
it('canonicalizes browser-zone provenance before delivering a child prompt', async () => {
const { api, parent, followup } = bench()
const alias = 'US/Pacific'
const canonical = new Intl.DateTimeFormat('en-US', { timeZone: alias })
.resolvedOptions().timeZone
const content = [{ type: 'text' as const, text: 'continue locally' }]
const signal = new AbortController().signal
await expect(api.subagents.prompt(request({
parentSessionId: PARENT,
childSessionId: CHILD,
mode: 'continuable',
content,
clientTimeZone: alias,
}), signal)).resolves.toMatchObject({ result: { ok: true } })
expect(followup).toHaveBeenCalledWith(parent, CHILD, content, {
source: { kind: 'user', rpcId: RpcId('subagent-rpc'), clientTimeZone: canonical },
signal,
})
const invalid = await api.subagents.prompt(request({
parentSessionId: PARENT,
childSessionId: CHILD,
mode: 'continuable',
content,
clientTimeZone: 'Not/A_Real_Zone',
}), signal)
expect(invalid.result).toEqual({
ok: false,
error: {
code: 'invalid-time-zone',
message: 'clientTimeZone must be UTC or a valid IANA Area/Location name',
details: { value: 'Not/A_Real_Zone' },
},
})
expect(followup).toHaveBeenCalledOnce()
})
it('fails before delivery when the parent is absent and maps continuation failures', async () => {
const absent = bench({ parentLive: false })
expect((await absent.api.subagents.prompt(request({

View File

@@ -39,6 +39,7 @@ import { hostFrameSchema, muxFrameSchema, askUserQuestionItemSchema } from '../s
import { approvalRequestIdSchema, approvalResponsePayloadSchema } from '../src/api/approvals.schema.ts'
import { askUserQuestionAnswerSchema, questionResponsePayloadSchema } from '../src/api/questions.schema.ts'
import { goalEditRequestSchema } from '../src/api/goals.schema.ts'
import { subagentPromptRequestSchema } from '../src/api/subagents.schema.ts'
describe('RpcId', () => {
it('brands a raw string at zero runtime cost', () => {
@@ -63,6 +64,7 @@ describe('rpcErrorSchema', () => {
expect(rpcErrorSchema.parse({ code: 'cancelled', message: 'm', details: {} }).code).toBe('cancelled')
expect(rpcErrorSchema.parse({ code: 'session-not-found', message: 'm', details: { sessionId: 's' } }).code).toBe('session-not-found')
expect(rpcErrorSchema.parse({ code: 'session-conflict', message: 'm', details: { sessionId: 's', requestedCwd: '/a', existingCwd: '/b' } }).code).toBe('session-conflict')
expect(rpcErrorSchema.parse({ code: 'invalid-time-zone', message: 'm', details: { value: 'CST' } }).code).toBe('invalid-time-zone')
expect(rpcErrorSchema.parse({ code: 'workspace-attach-failed', message: 'm', details: { sessionId: 's', workspaceId: 'w' } }).code).toBe('workspace-attach-failed')
expect(rpcErrorSchema.parse({ code: 'workspace-not-found', message: 'm', details: { workspaceId: 'w' } }).code).toBe('workspace-not-found')
expect(rpcErrorSchema.parse({ code: 'workspace-invalid-path', message: 'm', details: { path: '/x' } }).code).toBe('workspace-invalid-path')
@@ -248,8 +250,17 @@ describe('sessions domain schemas', () => {
}],
failures: [],
})).toThrow()
const prompt = sessionPromptRequestSchema.parse({ sessionId: 's1', mode: 'queue', content: [{ type: 'text', text: 'hi' }] })
const prompt = sessionPromptRequestSchema.parse({
sessionId: 's1',
mode: 'queue',
content: [{ type: 'text', text: 'hi' }],
clientTimeZone: 'Asia/Shanghai',
})
expect(prompt.mode).toBe('queue')
expect(prompt.clientTimeZone).toBe('Asia/Shanghai')
expect(sessionPromptRequestSchema.parse({
sessionId: 's1', mode: 'queue', content: [],
}).clientTimeZone).toBeUndefined()
expect(() => sessionPromptRequestSchema.parse({ sessionId: 's1', mode: 'inject', content: [] })).toThrow()
expect(sessionPromptValueSchema.parse({ accepted: true }).accepted).toBe(true)
// The command slot appears only when the prompt dispatched a slash command.
@@ -275,6 +286,24 @@ describe('sessions domain schemas', () => {
})
})
describe('subagent domain schemas', () => {
it('carries optional request-local browser-zone provenance on prompts', () => {
expect(subagentPromptRequestSchema.parse({
parentSessionId: 'parent',
childSessionId: 'child',
mode: 'continuable',
content: [{ type: 'text', text: 'continue' }],
clientTimeZone: 'Asia/Shanghai',
}).clientTimeZone).toBe('Asia/Shanghai')
expect(subagentPromptRequestSchema.parse({
parentSessionId: 'parent',
childSessionId: 'child',
mode: 'continuable',
content: [],
}).clientTimeZone).toBeUndefined()
})
})
describe('host domain schemas', () => {
it('validates describe request/value', () => {
expect(hostDescribeRequestSchema.parse({})).toEqual({})