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

@@ -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>>