feat: add DeepSeek Harness runtime

This commit is contained in:
lofyer
2026-08-14 10:04:34 +08:00
parent fca9888f83
commit 8286e120a1
69 changed files with 13660 additions and 351 deletions
+3 -2
View File
@@ -19,13 +19,14 @@ const controlCharacterFreeString = (maximumLength: number) =>
export const runtimeTargetSchema = z.enum([
'model',
'opencode',
'continue'
'continue',
'deepseek-harness'
])
export type RuntimeTarget = z.infer<typeof runtimeTargetSchema>
export const capabilityAssignmentsSchema = z
.array(runtimeTargetSchema)
.max(3)
.max(4)
.refine(
(assignments) => new Set(assignments).size === assignments.length,
'Runtime 分配不能重复'
+54 -3
View File
@@ -234,7 +234,8 @@ export const runtimeProviderSchema = z.enum([
'auto',
'model',
'opencode',
'continue'
'continue',
'deepseek-harness'
])
export const toolApprovalPolicySchema = z.enum([
@@ -391,6 +392,26 @@ export const runtimeModelSourceSchema = z.discriminatedUnion('kind', [
.strict()
])
export function isDeepSeekHarnessModelProfile(
profile: Pick<
ModelConnectionSettings,
'baseUrl' | 'protocol' | 'authentication'
>
): boolean {
if (
profile.protocol !== 'openai-chat-completions' ||
profile.authentication !== 'api-key'
) {
return false
}
try {
return new URL(profile.baseUrl).hostname.toLowerCase() ===
'api.deepseek.com'
} catch {
return false
}
}
export const runtimeSettingsInputSchema = z
.object({
provider: runtimeProviderSchema,
@@ -440,6 +461,8 @@ export const runtimeSettingsInputSchema = z
defaultModelProfileId: modelProfileIdSchema.optional(),
opencodeModelSource: runtimeModelSourceSchema.optional(),
continueModelSource: runtimeModelSourceSchema.optional(),
deepseekHarnessModelSource: runtimeModelSourceSchema
.default({ kind: 'platform' }),
toolApproval: toolApprovalPolicySchema
}).strict()
.superRefine((settings, context) => {
@@ -505,7 +528,8 @@ export const runtimeSettingsInputSchema = z
}
for (const [key, source] of [
['opencodeModelSource', settings.opencodeModelSource],
['continueModelSource', settings.continueModelSource]
['continueModelSource', settings.continueModelSource],
['deepseekHarnessModelSource', settings.deepseekHarnessModelSource]
] as const) {
if (source?.kind === 'profile' && !ids.has(source.profileId)) {
context.addIssue({
@@ -551,6 +575,24 @@ export const runtimeSettingsInputSchema = z
'Continue 独立模型连接仅支持文本对话协议,不支持图像生成协议'
})
}
const deepseekHarnessSource = settings.deepseekHarnessModelSource
const deepseekHarnessProfile =
deepseekHarnessSource?.kind === 'profile'
? settings.modelProfiles.find(
(profile) => profile.id === deepseekHarnessSource.profileId
)
: undefined
if (
deepseekHarnessProfile &&
!isDeepSeekHarnessModelProfile(deepseekHarnessProfile)
) {
context.addIssue({
code: 'custom',
path: ['deepseekHarnessModelSource'],
message:
'DeepSeek Harness 独立模型连接仅支持 api.deepseek.com 的 OpenAI Chat Completions 协议'
})
}
}
if (
settings.opencodeBaseUrl &&
@@ -615,6 +657,7 @@ export type ConfiguredRuntimeSettings = {
workspacePath: string
opencodeModelSource: RuntimeModelSource
continueModelSource: RuntimeModelSource
deepseekHarnessModelSource?: RuntimeModelSource
}
export type RuntimeSettings = {
@@ -659,6 +702,7 @@ export type RuntimeSettings = {
defaultModelProfileId: string
opencodeModelSource: RuntimeModelSource
continueModelSource: RuntimeModelSource
deepseekHarnessModelSource?: RuntimeModelSource
secureStorageAvailable: boolean
toolApproval: RuntimeSettingsInput['toolApproval']
configured?: ConfiguredRuntimeSettings
@@ -716,7 +760,12 @@ export type WindowCaptureOption = {
}
export type AgentRuntimeStatus = {
id: 'setup' | 'model' | 'opencode' | 'continue'
id:
| 'setup'
| 'model'
| 'opencode'
| 'continue'
| 'deepseek-harness'
label: string
available: boolean
detail: string
@@ -729,6 +778,7 @@ export type RuntimeBinaryDetection =
available: true
path: string
version?: string
source?: 'bundled' | 'configured' | 'automatic'
detail: string
}
| {
@@ -741,6 +791,7 @@ export type RuntimeBinaryDetection =
export type AgentRuntimeDetection = {
opencode: RuntimeBinaryDetection
continue: RuntimeBinaryDetection
deepseekHarness: RuntimeBinaryDetection
}
export const approvalDecisionSchema = z.enum([
+47 -1
View File
@@ -23,6 +23,12 @@ export const agentRuntimeSelectionSchema = z.discriminatedUnion(
provider: z.literal('continue'),
profileId: runtimeSelectionProfileIdSchema.optional()
})
.strict(),
z
.object({
provider: z.literal('deepseek-harness'),
profileId: runtimeSelectionProfileIdSchema.optional()
})
.strict()
]
)
@@ -34,6 +40,7 @@ export type AgentRuntimeSelection = z.infer<
export type RuntimeSelectionRepairSettings = {
modelProfiles: ReadonlyArray<{
id: string
baseUrl?: string
protocol?: string
authentication?: 'api-key' | 'none'
apiKeyConfigured?: boolean
@@ -45,6 +52,9 @@ export type RuntimeSelectionRepairSettings = {
continueModelSource:
| { kind: 'platform' }
| { kind: 'profile'; profileId: string }
deepseekHarnessModelSource?:
| { kind: 'platform' }
| { kind: 'profile'; profileId: string }
}
type ChannelModelProfile = RuntimeSelectionRepairSettings['modelProfiles'][number]
@@ -61,6 +71,25 @@ export function isChannelModelProfileUsable(
)
}
function isDeepSeekHarnessRepairProfileUsable(
profile: ChannelModelProfile
): boolean {
if (
profile.protocol !== 'openai-chat-completions' ||
profile.authentication !== 'api-key' ||
profile.apiKeyConfigured === false ||
!profile.baseUrl
) {
return false
}
try {
return new URL(profile.baseUrl).hostname.toLowerCase() ===
'api.deepseek.com'
} catch {
return false
}
}
export function repairChannelRuntimeSelection(
selection: AgentRuntimeSelection,
settings: RuntimeSelectionRepairSettings
@@ -86,6 +115,21 @@ export function repairChannelRuntimeSelection(
) {
return { provider: selection.provider }
}
if (selection.provider === 'deepseek-harness') {
const repaired = repairAgentRuntimeSelection(selection, settings)
if (
repaired.provider === 'deepseek-harness' &&
repaired.profileId
) {
const profile = settings.modelProfiles.find(
(candidate) => candidate.id === repaired.profileId
)
if (profile && isDeepSeekHarnessRepairProfileUsable(profile)) {
return repaired
}
}
return { provider: 'deepseek-harness' }
}
const repaired = repairAgentRuntimeSelection(selection, settings)
if (repaired.provider !== 'model') {
return repaired
@@ -120,7 +164,9 @@ export function repairAgentRuntimeSelection(
const source =
selection.provider === 'opencode'
? settings.opencodeModelSource
: settings.continueModelSource
: selection.provider === 'continue'
? settings.continueModelSource
: settings.deepseekHarnessModelSource ?? { kind: 'platform' }
return {
provider: selection.provider,
...(source.kind === 'profile'