Merge remote-tracking branch 'origin/master' into xjt/generated-docs-zh-translation-apply

# Conflicts:
#	.agents/notes/implemented/feature/2026-08-07-default-model-follows-the-picker.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-07-default-model-follows-the-picker.zh.md
#	docs/architecture.i18n.yaml
#	docs/architecture.zh.md
#	docs/user/guide/providers.i18n.yaml
#	docs/user/guide/providers.zh.md
#	docs/user/guide/quickstart.i18n.yaml
#	docs/user/guide/quickstart.zh.md
#	packages/boot/app-boot/README.i18n.yaml
#	packages/boot/app-boot/README.zh.md
#	packages/bundle/headless/README.i18n.yaml
#	packages/bundle/headless/README.zh.md
This commit is contained in:
xjt
2026-08-09 15:17:35 +08:00
169 changed files with 1618 additions and 1086 deletions

View File

@@ -12,7 +12,7 @@ export type {
WorkspaceApi, WorkspaceId, WorkspaceView,
CommandsApi, CommandDescriptor, SkillsApi, SkillEntry,
ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning,
ModelReasoningEffort, ModelTarget, QueueAction, QueuedInboxItem, SessionModels,
ModelReasoningEffort, ModelSelection, QueueAction, QueuedInboxItem, SessionModels,
GoalsApi, GoalRef,
SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView,
CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi,

View File

@@ -30,7 +30,7 @@ import type { CommandId } from '@deepseek-ai/dsh-commands/brand'
import { deriveEventMessage, foldSurface } from '@deepseek-ai/dsh-session/surface'
import type {
ApiProxy, ClientRequest, ClientResponse, HistoryEntry, HostFrame, MuxFrame, RpcReceipt,
ModelProviderGroup, ModelTarget, RpcRequest, RpcResponse, RpcResult, ServerRequest, ServerResponse, SessionSummary,
ModelProviderGroup, ModelSelection, RpcRequest, RpcResponse, RpcResult, ServerRequest, ServerResponse, SessionSummary,
ToolCallView, ToolEventView, ToolResultView, WorkspaceId, WorkspaceView,
} from './api.ts'
import type { RequestPayload, ResponseValue, RpcMethodMap } from '@deepseek-ai/dsh-host-apiproxy/api'
@@ -1347,7 +1347,7 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld {
{ sessionId: sid('fx-gamma'), updatedAt: Date.now() - 120_000, running: false, blank: false, cwd: '/tmp/fixture' },
]
const logs = new Map<SessionId, SessionEvent[]>([[sid('fx-alpha'), buildAlphaLog()]])
const modelTargets = new Map<SessionId, ModelTarget>(sessions.map(session => [
const modelSelections = new Map<SessionId, ModelSelection>(sessions.map(session => [
session.sessionId,
{ provider: 'deepseek-official', model: 'deepseek-v4-flash' },
]))
@@ -1989,7 +1989,7 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld {
sessionId: requestedId ?? sid(`fx-${nextSession++}`), updatedAt: Date.now(), running: false, blank: true, cwd,
}
sessions.push(created)
modelTargets.set(created.sessionId, { provider: 'deepseek-official', model: 'deepseek-v4-flash' })
modelSelections.set(created.sessionId, { provider: 'deepseek-official', model: 'deepseek-v4-flash' })
attachedSessions += 1
const emitSession = (): void => {
// Mirrors the host: the frame fires at creation, so blank is constantly true.
@@ -2098,7 +2098,7 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld {
return ok(request, { ...page, ...projections === undefined ? {} : { projections } })
},
models: request => ok(request, {
current: modelTargets.get(request.payload.sessionId)
current: modelSelections.get(request.payload.sessionId)
?? { provider: 'deepseek-official', model: 'deepseek-v4-flash' },
// The fixture's routes all serve; a surface exercising the blocked
// posture drives it through its own stub.
@@ -2107,14 +2107,14 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld {
failures: [],
}),
selectModel: (request) => {
const selected: ModelTarget = {
const selected: ModelSelection = {
provider: request.payload.provider,
model: request.payload.model,
...request.payload.reasoningEffort === undefined
? {}
: { reasoningEffort: request.payload.reasoningEffort },
}
modelTargets.set(request.payload.sessionId, selected)
modelSelections.set(request.payload.sessionId, selected)
return ok(request, { selected })
},
prompt: (request) => {
@@ -2153,11 +2153,11 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld {
// Capacity parallel of the host token-meter's request/context record:
// log-only, appended inside the open turn, and deduplicated against the
// route already recorded (the fixture never varies contextWindow).
const target = modelTargets.get(id) ?? { provider: 'deepseek', model: 'deepseek-v4-flash' }
if (lastRequestContext(logOf(id))?.model !== target.model) {
const selection = modelSelections.get(id) ?? { provider: 'deepseek', model: 'deepseek-v4-flash' }
if (lastRequestContext(logOf(id))?.model !== selection.model) {
append(id, {
type: 'request/context',
data: { provider: target.provider, model: target.model, contextWindow: 128_000 },
data: { provider: selection.provider, model: selection.model, contextWindow: 128_000 },
})
}
startReply(
@@ -2167,9 +2167,9 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld {
? MARKDOWN_FIXTURE
: userText === 'report model'
? (() => {
const target = modelTargets.get(id)
return `当前模型:${target?.provider ?? 'unknown'}/${target?.model ?? 'unknown'}`
+ (target?.reasoningEffort === undefined ? '' : ` · 推理等级:${target.reasoningEffort}`)
const selection = modelSelections.get(id)
return `当前模型:${selection?.provider ?? 'unknown'}/${selection?.model ?? 'unknown'}`
+ (selection?.reasoningEffort === undefined ? '' : ` · 推理等级:${selection.reasoningEffort}`)
})()
: `回声:${userText}。这是 fixture 的流式回复,用于验证打字机增长与定稿切换。`,
)

View File

@@ -20,7 +20,7 @@ export type {
ToolCallView, ToolResultView, WorkspaceApi, WorkspaceId, WorkspaceView,
CommandsApi, CommandDescriptor, SkillsApi, SkillEntry,
ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning,
MessageId, ModelReasoningEffort, ModelTarget, QueueAction, QueuedInboxItem, SessionModels,
MessageId, ModelReasoningEffort, ModelSelection, QueueAction, QueuedInboxItem, SessionModels,
SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt,
RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode,
ClientRequest, ServerResponse, ServerRequest, ClientResponse, RpcMessage, RpcReceipt,

View File

@@ -3,7 +3,7 @@
// deferred-controlled timing). Streams are hand pumps: pushMux/pushHost.
import type { CommandId } from '@deepseek-ai/dsh-commands/brand'
import type {
CommandDescriptor, HostFrame, IApiClient, ModelTarget, MuxFrame,
CommandDescriptor, HostFrame, IApiClient, ModelSelection, MuxFrame,
RpcRequest, RpcResponse, SessionId, SessionModels, SessionSearchItem, SkillEntry,
} from '../src/client/api.ts'
import { RpcId } from '../src/client/api.ts'
@@ -50,11 +50,11 @@ export class FakeApiClient implements IApiClient {
onRename: (payload: unknown) => Promise<RpcResponse<{ title: string; seq: number }>> = () => Promise.resolve(ok({ title: 'fk-renamed', seq: 0 }))
onFork: (payload: unknown) => Promise<RpcResponse<{ sessionId: SessionId }>> = () => Promise.resolve(ok({ sessionId: 'fk-fork' as SessionId }))
onHistory: (payload: { sessionId: SessionId; beforeSeq?: number; maxMessages?: number })
=> Promise<RpcResponse<{ events: never[]; hasMore: boolean; modelTarget: ModelTarget }>> =
=> Promise<RpcResponse<{ events: never[]; hasMore: boolean; modelSelection: ModelSelection }>> =
() => Promise.resolve(ok({
events: [],
hasMore: false,
modelTarget: { provider: 'deepseek-official', model: 'deepseek-chat' },
modelSelection: { provider: 'deepseek-official', model: 'deepseek-chat' },
}))
onModels: (payload: unknown) => Promise<RpcResponse<SessionModels>> = () => Promise.resolve(ok({
@@ -63,8 +63,8 @@ export class FakeApiClient implements IApiClient {
groups: [],
failures: [],
}))
onSelectModel: (payload: ModelTarget & { sessionId: SessionId })
=> Promise<RpcResponse<{ selected: ModelTarget }>> =
onSelectModel: (payload: ModelSelection & { sessionId: SessionId })
=> Promise<RpcResponse<{ selected: ModelSelection }>> =
payload => Promise.resolve(ok({ selected: { provider: payload.provider, model: payload.model } }))
onPrompt: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
onUpdateQueue: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
@@ -105,7 +105,7 @@ export class FakeApiClient implements IApiClient {
history: (payload: { sessionId: SessionId; beforeSeq?: number; maxMessages?: number }) =>
this.record('session.history', payload, this.onHistory(payload)),
models: (payload: unknown) => this.record('session.models', payload, this.onModels(payload)),
selectModel: (payload: ModelTarget & { sessionId: SessionId }) =>
selectModel: (payload: ModelSelection & { sessionId: SessionId }) =>
this.record('session.selectModel', payload, this.onSelectModel(payload)),
rename: (payload: unknown) => this.record('session.rename', payload, this.onRename(payload)),
fork: (payload: unknown) => this.record('session.fork', payload, this.onFork(payload)),

View File

@@ -168,7 +168,7 @@ describe('createFixtureApi', () => {
})
})
it('serves grouped models and keeps a selected target for later history and fixture requests', async () => {
it('serves grouped models and keeps a selection for later history and fixture requests', async () => {
const api = createFixtureApi()
const sessionId = sid('fx-alpha')
const catalog = await api.sessions.models(req({ sessionId }))