feat: add secure multi-runtime controls

Make agent backends configurable and governable with encrypted credentials, explicit context sharing, and tool approval boundaries.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
lofyer
2026-07-30 10:52:35 +08:00
co-authored by factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent 1b6178b841
commit 698a15ad14
27 changed files with 2465 additions and 91 deletions
+118 -2
View File
@@ -4,13 +4,113 @@ export const agentRequestSchema = z.object({
requestId: z.string().uuid(),
conversationId: z.string().min(1).max(128),
prompt: z.string().trim().min(1).max(100_000),
workspace: z.string().max(2_048).optional()
contextIds: z.array(z.string().uuid()).max(8).optional()
})
export type AgentRequest = z.infer<typeof agentRequestSchema>
export const runtimeProviderSchema = z.enum([
'auto',
'bigtoken',
'opencode',
'continue'
])
export const toolApprovalPolicySchema = z.enum([
'always',
'session',
'workspace',
'policy'
])
export const defaultRuntimeSettings = {
provider: 'auto',
bigtokenBaseUrl: 'https://bigtoken.ai',
bigtokenModel: 'sonnet-5',
toolApproval: 'always'
} as const
export const runtimeSettingsInputSchema = z
.object({
provider: runtimeProviderSchema,
bigtokenBaseUrl: z.string().url().max(2_048),
bigtokenModel: z
.string()
.trim()
.min(1)
.max(128)
.regex(/^[\w./:-]+$/, '模型名称包含不支持的字符'),
apiKey: z.discriminatedUnion('action', [
z.object({ action: z.literal('keep') }).strict(),
z
.object({
action: z.literal('replace'),
value: z
.string()
.trim()
.min(1)
.max(8_192)
.refine(
(value) =>
[...value].every((character) => {
const code = character.charCodeAt(0)
return code > 31 && code !== 127
}),
{
message: 'API Key 包含控制字符'
}
)
})
.strict(),
z.object({ action: z.literal('clear') }).strict()
]),
toolApproval: toolApprovalPolicySchema
}).strict()
.superRefine((settings, context) => {
const url = new URL(settings.bigtokenBaseUrl)
if (url.protocol !== 'https:') {
context.addIssue({
code: 'custom',
path: ['bigtokenBaseUrl'],
message: 'Bigtoken 服务必须使用 HTTPS'
})
}
if (
url.username ||
url.password ||
url.search ||
url.hash ||
(url.pathname !== '/' && url.pathname !== '')
) {
context.addIssue({
code: 'custom',
path: ['bigtokenBaseUrl'],
message: '服务地址只能包含 HTTPS origin'
})
}
})
export type RuntimeSettingsInput = z.infer<typeof runtimeSettingsInputSchema>
export type RuntimeSettings = {
provider: RuntimeSettingsInput['provider']
bigtokenBaseUrl: string
bigtokenModel: string
apiKeyConfigured: boolean
credentialSource: 'none' | 'encrypted' | 'environment'
secureStorageAvailable: boolean
toolApproval: RuntimeSettingsInput['toolApproval']
}
export type ContextAttachment = {
id: string
name: string
size: number
preview: string
}
export type AgentRuntimeStatus = {
id: 'demo' | 'bigtoken' | 'opencode'
id: 'demo' | 'bigtoken' | 'opencode' | 'continue'
label: string
available: boolean
detail: string
@@ -34,6 +134,13 @@ export type AgentEvent =
state: 'pending' | 'running' | 'completed' | 'failed'
summary: string
}
| {
requestId: string
type: 'approval'
approvalId: string
title: string
description: string
}
| {
requestId: string
type: 'done'
@@ -64,6 +171,15 @@ export type DesktopApi = {
getStatus: () => Promise<AgentRuntimeStatus>
run: (request: AgentRequest) => Promise<void>
cancel: (requestId: string) => Promise<void>
respondApproval: (approvalId: string, approved: boolean) => Promise<void>
onEvent: (listener: (event: AgentEvent) => void) => () => void
}
settings: {
getRuntime: () => Promise<RuntimeSettings>
updateRuntime: (input: RuntimeSettingsInput) => Promise<RuntimeSettings>
}
context: {
selectFiles: () => Promise<ContextAttachment[]>
remove: (contextId: string) => Promise<void>
}
}