Merge remote-tracking branch 'origin/master' into fix/human-transcript-projection
# Conflicts: # packages/host/apiproxy/README.i18n.yaml # packages/host/apiproxy/src/api-proxy.ts # packages/ui/tui/README.i18n.yaml
This commit is contained in:
@@ -10,7 +10,9 @@ import type { HostFrame, MuxFrame } from './events.ts'
|
||||
import type { Wire } from './rpc.schema.ts'
|
||||
import { rpcErrorSchema, rpcIdSchema } from './rpc.schema.ts'
|
||||
import { approvalRequestIdSchema } from './approvals.schema.ts'
|
||||
import { contentBlockSchema, sessionEventSchema, sessionIdSchema, toolEventViewSchema } from './sessions.schema.ts'
|
||||
import {
|
||||
contentBlockSchema, inboxItemIdSchema, sessionEventSchema, sessionIdSchema, toolEventViewSchema,
|
||||
} from './sessions.schema.ts'
|
||||
import { workspaceIdSchema, workspaceViewSchema } from './workspace.schema.ts'
|
||||
|
||||
/** Question shape validated strictly against core dsh-user-interaction. */
|
||||
@@ -42,7 +44,14 @@ export const muxFrameSchema = z.discriminatedUnion('type', [
|
||||
// and must fail loud here, not reach the composer.
|
||||
z.object({ type: z.literal('question/requested'), sessionId: sessionIdSchema, questions: z.array(askUserQuestionItemSchema).min(1) }),
|
||||
z.object({ type: z.literal('question/resolved'), sessionId: sessionIdSchema, questionRpcId: rpcIdSchema, outcome: z.union([z.literal('answered'), z.literal('cancelled')]) }),
|
||||
z.object({ type: z.literal('session/queued'), sessionId: sessionIdSchema, message: messageSchema, steering: z.boolean() }),
|
||||
z.object({
|
||||
type: z.literal('session/queue'),
|
||||
sessionId: sessionIdSchema,
|
||||
items: z.array(z.object({
|
||||
id: inboxItemIdSchema,
|
||||
message: messageSchema,
|
||||
})),
|
||||
}),
|
||||
// value stays wide: it already passed its unit's own schema on the host,
|
||||
// and deep-validating here would import every domain's schema into the carrier.
|
||||
z.object({ type: z.literal('session/projection'), sessionId: sessionIdSchema, key: z.string().min(1), value: z.unknown(), seq: z.number().int().nonnegative() }),
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import type { AskUserQuestionItem } from '@deepseek-ai/dsh-user-interaction/types'
|
||||
import type { ApprovalOutcome, ApprovalRequestId } from '@deepseek-ai/dsh-user-approval/types'
|
||||
import type { Message } from '@deepseek-ai/dsh-llm/types'
|
||||
import type { InboxItemId } from '@deepseek-ai/dsh-agent/brand'
|
||||
import type { CallId } from '@deepseek-ai/dsh-llm/brand'
|
||||
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { ToolCallView, ToolResultView } from '@deepseek-ai/dsh-tools/presentation'
|
||||
@@ -31,6 +32,14 @@ export type ToolEventView =
|
||||
| { for: 'call'; view: ToolCallView }
|
||||
| { for: 'result'; view: ToolResultView }
|
||||
|
||||
/** One pending queued occurrence in an authoritative queue snapshot. */
|
||||
export interface QueuedInboxItem {
|
||||
/** Agent-owned occurrence identity used by queue mutations. */
|
||||
id: InboxItemId
|
||||
/** Complete pending message; it is not durable until the Agent claims it. */
|
||||
message: Message
|
||||
}
|
||||
|
||||
/** Streaming face of the contract: the two SSE stream openers (mux + host). */
|
||||
export interface EventsApi {
|
||||
/**
|
||||
@@ -62,18 +71,13 @@ export type MuxFrame =
|
||||
| { type: 'question/requested'; sessionId: SessionId; questions: AskUserQuestionItem[] }
|
||||
| { type: 'question/resolved'; sessionId: SessionId; questionRpcId: RpcId; outcome: 'answered' | 'cancelled' }
|
||||
/**
|
||||
* A message entered the addressed agent's inbox. A queued message is not
|
||||
* model-visible, so there is no session event to carry it; this transient
|
||||
* frame is the only wire signal. On stream open the
|
||||
* host replays the current queue snapshot for every attached session (same
|
||||
* refresh-recovery baseline as pending questions); queue clearing on cancel
|
||||
* has no dedicated frame — clients fold it from the status flip.
|
||||
* `steering` is the host's acceptance-time queue classification and remains
|
||||
* authoritative in reconnect snapshots. `message.source` carries the prompt's rpcId
|
||||
* when the message came over this wire (the client's provisional-echo
|
||||
* reconciliation key).
|
||||
* Complete transient queue state after every enqueue, mutation, claim, or
|
||||
* discard. Pending work is not model-visible and therefore has no durable
|
||||
* session event; the whole snapshot makes edit, deletion, cancel, and
|
||||
* reconnect converge through one authoritative signal. Pending steering is
|
||||
* outside this Web queue projection.
|
||||
*/
|
||||
| { type: 'session/queued'; sessionId: SessionId; message: Message; steering: boolean }
|
||||
| { type: 'session/queue'; sessionId: SessionId; items: QueuedInboxItem[] }
|
||||
/**
|
||||
* One projection unit's finished value changed (session-projection RFC).
|
||||
* Live push state, never logged — replay recomputes on the host (the
|
||||
|
||||
@@ -29,13 +29,13 @@ export interface ApiProxy {
|
||||
// ---- Domain interfaces and payload entities ----
|
||||
export type {
|
||||
HistoryEntry, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning,
|
||||
ModelReasoningEffort, ModelTarget, SessionModels, SessionProjectionsBlock, SessionsApi, SessionSummary,
|
||||
ModelReasoningEffort, ModelTarget, QueueAction, SessionModels, SessionProjectionsBlock, SessionsApi, SessionSummary,
|
||||
} from './sessions.ts'
|
||||
export type { DirectoryEntry, DirectoryListing, HostApi } from './host.ts'
|
||||
export type { WorkspaceApi, WorkspaceId, WorkspaceView } from './workspace.ts'
|
||||
export type { CommandsApi, CommandDescriptor } from './commands.ts'
|
||||
export type { SkillsApi, SkillEntry } from './skills.ts'
|
||||
export type { EventsApi, MuxFrame, HostFrame, ToolCallView, ToolEventView, ToolResultView } from './events.ts'
|
||||
export type { EventsApi, MuxFrame, HostFrame, QueuedInboxItem, ToolCallView, ToolEventView, ToolResultView } from './events.ts'
|
||||
export type { GoalsApi, GoalId, GoalRef } from './goals.ts'
|
||||
export type { ApprovalResponsePayload } from './approvals.ts'
|
||||
export type { QuestionResponsePayload } from './questions.ts'
|
||||
@@ -56,6 +56,7 @@ export type {
|
||||
// ---- Errors and ids ----
|
||||
export { RpcId, transportError } from './rpc.ts'
|
||||
export type { RpcError, RpcErrorCode, RpcErrorDetailsMap, RpcResult } from './rpc.ts'
|
||||
export type { InboxItemId } from '@deepseek-ai/dsh-agent/brand'
|
||||
|
||||
// ---- Method registry and derived generics ----
|
||||
export type { RequestPayload, ResponseValue, RpcMethodMap } from './rpc-map.ts'
|
||||
|
||||
@@ -25,6 +25,7 @@ export interface RpcMethodMap {
|
||||
'session.selectModel': SessionsApi['selectModel']
|
||||
'session.rename': SessionsApi['rename']
|
||||
'session.prompt': SessionsApi['prompt']
|
||||
'session.updateQueue': SessionsApi['updateQueue']
|
||||
'session.cancel': SessionsApi['cancel']
|
||||
'host.describe': HostApi['describe']
|
||||
'host.pickDirectory': HostApi['pickDirectory']
|
||||
|
||||
@@ -47,6 +47,7 @@ export const rpcErrorSchema: z.ZodType<RpcError> = z.discriminatedUnion('code',
|
||||
z.object({ code: z.literal('directory-create-failed'), message: z.string(), details: z.object({ path: z.string() }) }),
|
||||
z.object({ code: z.literal('directory-picker-unavailable'), message: z.string(), details: z.object({ capability: z.string() }) }),
|
||||
z.object({ code: z.literal('agent-busy'), message: z.string(), details: z.object({ reason: z.string() }) }),
|
||||
z.object({ code: z.literal('queue-item-not-found'), message: z.string(), details: z.object({ itemId: z.string() }) }),
|
||||
z.object({ code: z.literal('command-error'), message: z.string(), details: z.object({}) }),
|
||||
z.object({ code: z.literal('unknown-command'), message: z.string(), details: z.object({}) }),
|
||||
z.object({ code: z.literal('title-invalid'), message: z.string(), details: z.object({ sessionId: z.string() }) }),
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { z as zCore } from 'zod'
|
||||
type ZodIssue = zCore.core.$ZodIssue
|
||||
import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { InboxItemId } from '@deepseek-ai/dsh-agent/brand'
|
||||
|
||||
/**
|
||||
* Message correlation id: the initiator mints it on a request; a response
|
||||
@@ -44,6 +45,7 @@ export interface RpcErrorDetailsMap {
|
||||
'directory-create-failed': { path: string }
|
||||
'directory-picker-unavailable': { capability: string }
|
||||
'agent-busy': { reason: string }
|
||||
'queue-item-not-found': { itemId: InboxItemId }
|
||||
/** A known slash command reported a usage/state error; the message is the command's own text. */
|
||||
'command-error': {}
|
||||
/** A leading-/ prompt named no registered command; the message names the token. */
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { z } from 'zod'
|
||||
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { InboxItemId } from '@deepseek-ai/dsh-agent/brand'
|
||||
import type { RequestPayload, ResponseValue } from './rpc-map.ts'
|
||||
import type { Wire } from './rpc.schema.ts'
|
||||
import type {
|
||||
@@ -19,6 +20,9 @@ import type { WorkspaceId } from './workspace.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<SessionId>
|
||||
|
||||
/** InboxItemId: one brand cast after non-empty string validation. */
|
||||
export const inboxItemIdSchema = z.string().min(1) as unknown as z.ZodType<InboxItemId>
|
||||
|
||||
/**
|
||||
* WorkspaceId: the workspace domain's one brand cast. Hosted here rather
|
||||
* than in workspace.schema because session.create references it while
|
||||
@@ -214,6 +218,21 @@ export const sessionPromptValueSchema = z.object({
|
||||
}).optional(),
|
||||
}) satisfies z.ZodType<Wire<ResponseValue<'session.prompt'>>>
|
||||
|
||||
/** session.updateQueue request payload. */
|
||||
export const sessionUpdateQueueRequestSchema = z.object({
|
||||
sessionId: sessionIdSchema,
|
||||
itemId: inboxItemIdSchema,
|
||||
action: z.discriminatedUnion('kind', [
|
||||
z.object({ kind: z.literal('edit'), content: z.array(contentBlockSchema) }),
|
||||
z.object({ kind: z.literal('remove') }),
|
||||
]),
|
||||
}) as unknown as z.ZodType<RequestPayload<'session.updateQueue'>>
|
||||
|
||||
/** session.updateQueue response value. */
|
||||
export const sessionUpdateQueueValueSchema = z.object({
|
||||
accepted: z.literal(true),
|
||||
}) satisfies z.ZodType<Wire<ResponseValue<'session.updateQueue'>>>
|
||||
|
||||
/** session.cancel request payload. */
|
||||
export const sessionCancelRequestSchema = z.object({
|
||||
sessionId: sessionIdSchema,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { ContentBlock } from '@deepseek-ai/dsh-llm/types'
|
||||
import type { InboxItemId } from '@deepseek-ai/dsh-agent/brand'
|
||||
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
// The pure-type outlet: api/ is browser-importable, and the package root's
|
||||
// cordis Context merge (via dsh-agent) must not enter client aggregates.
|
||||
@@ -124,6 +125,11 @@ export interface SessionModels {
|
||||
failures: ModelCatalogFailure[]
|
||||
}
|
||||
|
||||
/** A client-requested mutation of one still-pending queue item. */
|
||||
export type QueueAction =
|
||||
| { kind: 'edit'; content: ContentBlock[] }
|
||||
| { kind: 'remove' }
|
||||
|
||||
/** Session list entry (v1 builds no index: list does readdir+stat). */
|
||||
export interface SessionSummary {
|
||||
sessionId: SessionId
|
||||
@@ -231,6 +237,12 @@ export interface SessionsApi {
|
||||
prompt(request: RpcRequest<{ sessionId: SessionId; mode: 'queue' | 'steer'; content: ContentBlock[] }>):
|
||||
Promise<RpcResponse<{ accepted: true; command?: { kind: 'success'; text?: string } }>>
|
||||
|
||||
/**
|
||||
* Edits or removes one pending queued occurrence.
|
||||
*/
|
||||
updateQueue(request: RpcRequest<{ sessionId: SessionId; itemId: InboxItemId; action: QueueAction }>):
|
||||
Promise<RpcResponse<{ accepted: true }>>
|
||||
|
||||
/** Stops: clears both FIFOs + aborts the current step (1:1 with agent.cancel). */
|
||||
cancel(request: RpcRequest<{ sessionId: SessionId }>): Promise<RpcResponse<{ accepted: true }>>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user