Expose saved decisions, timelines and source evidence through read-only Story Graph tools across Model, Harness and local/remote OpenCode and Continue. Supervisor enablement and runtime assignments govern discovery and calls; reads do not invoke models or modify stored evidence. Remote use requires matching Desktop and Agent changes. Make Continue discussion show the complete editable message with a separated destination and shared action layout. Sending uses the visible draft without reattaching removed context. Validation: existing focused MCP, runtime, controlled Linux Host, discussion component and Electron layout checks plus typechecks; staged diff check passed. Cross-scope fact reuse, historical reconstruction and full package acceptance remain deferred. Release note: Agents can consult saved supervisor decisions and their sources when continuing work. Continue discussion now lets you review and edit the complete message before sending.
489 lines
13 KiB
TypeScript
489 lines
13 KiB
TypeScript
import { z } from 'zod'
|
|
import { settingsWarningsSchema } from './settings-warning-contracts'
|
|
|
|
const controlCharacterFreeString = (maximumLength: number) =>
|
|
z
|
|
.string()
|
|
.trim()
|
|
.min(1)
|
|
.max(maximumLength)
|
|
.refine(
|
|
(value) =>
|
|
[...value].every((character) => {
|
|
const code = character.charCodeAt(0)
|
|
return code > 31 && code !== 127
|
|
}),
|
|
'值包含控制字符'
|
|
)
|
|
|
|
export const runtimeTargetSchema = z.enum([
|
|
'model',
|
|
'opencode',
|
|
'continue',
|
|
'deepseek-harness'
|
|
])
|
|
export type RuntimeTarget = z.infer<typeof runtimeTargetSchema>
|
|
|
|
export const capabilityAssignmentsSchema = z
|
|
.array(runtimeTargetSchema)
|
|
.max(4)
|
|
.refine(
|
|
(assignments) => new Set(assignments).size === assignments.length,
|
|
'Runtime 分配不能重复'
|
|
)
|
|
export type CapabilityAssignments = z.infer<
|
|
typeof capabilityAssignmentsSchema
|
|
>
|
|
|
|
export const builtinMcpServerIdSchema = z.enum([
|
|
'story-graph',
|
|
'knowledge-base',
|
|
'magic-notes',
|
|
'goodbuddy-config',
|
|
'builtin-browser',
|
|
'obsidian'
|
|
])
|
|
export type BuiltinMcpServerId = z.infer<
|
|
typeof builtinMcpServerIdSchema
|
|
>
|
|
|
|
export const builtinMcpAssignmentsSchema =
|
|
capabilityAssignmentsSchema.refine(
|
|
(assignments) => !assignments.includes('deepseek-harness'),
|
|
'DeepSeek Harness 当前不支持内置 MCP'
|
|
)
|
|
|
|
export const builtinMcpServerToggleInputSchema = z
|
|
.object({
|
|
serverId: builtinMcpServerIdSchema,
|
|
enabled: z.boolean()
|
|
})
|
|
.strict()
|
|
|
|
export const builtinMcpServerAssignmentsInputSchema = z
|
|
.object({
|
|
serverId: builtinMcpServerIdSchema,
|
|
assignments: capabilityAssignmentsSchema
|
|
})
|
|
.strict()
|
|
.refine(value => value.serverId === 'story-graph' || !value.assignments.includes('deepseek-harness'), 'DeepSeek Harness only supports the Story Graph built-in MCP')
|
|
|
|
export const builtinMcpServerStateSummarySchema = z
|
|
.object({
|
|
id: builtinMcpServerIdSchema,
|
|
enabled: z.boolean(),
|
|
assignments: capabilityAssignmentsSchema
|
|
})
|
|
.strict()
|
|
.refine(value => value.id === 'story-graph' || !value.assignments.includes('deepseek-harness'), 'DeepSeek Harness only supports the Story Graph built-in MCP')
|
|
export type BuiltinMcpServerStateSummary = z.infer<
|
|
typeof builtinMcpServerStateSummarySchema
|
|
>
|
|
|
|
export const secretActionSchema = z.discriminatedUnion('action', [
|
|
z.object({ action: z.literal('keep') }).strict(),
|
|
z
|
|
.object({
|
|
action: z.literal('replace'),
|
|
value: controlCharacterFreeString(8_192)
|
|
})
|
|
.strict(),
|
|
z.object({ action: z.literal('clear') }).strict()
|
|
])
|
|
export type SecretAction = z.infer<typeof secretActionSchema>
|
|
|
|
export const skillIdSchema = z
|
|
.string()
|
|
.min(1)
|
|
.max(128)
|
|
.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)
|
|
|
|
export const skillImportKindSchema = z.enum(['directory', 'zip'])
|
|
export type SkillImportKind = z.infer<typeof skillImportKindSchema>
|
|
|
|
export const skillToggleInputSchema = z
|
|
.object({
|
|
skillId: skillIdSchema,
|
|
enabled: z.boolean()
|
|
})
|
|
.strict()
|
|
|
|
export const skillAssignmentsInputSchema = z
|
|
.object({
|
|
skillId: skillIdSchema,
|
|
assignments: capabilityAssignmentsSchema
|
|
})
|
|
.strict()
|
|
|
|
export const skillSummarySchema = z
|
|
.object({
|
|
id: skillIdSchema,
|
|
name: z.string().min(1).max(80),
|
|
description: z.string().min(1).max(500),
|
|
version: z.string().max(32).optional(),
|
|
tags: z.array(z.string().min(1).max(32)).max(12),
|
|
source: z.enum(['builtin', 'imported']),
|
|
digest: z.string().regex(/^[a-f0-9]{64}$/),
|
|
enabled: z.boolean(),
|
|
assignments: capabilityAssignmentsSchema
|
|
})
|
|
.strict()
|
|
export type SkillSummary = z.infer<typeof skillSummarySchema>
|
|
|
|
export const computerCapabilityIdSchema = z.enum([
|
|
'host-browser-control',
|
|
'linux-desktop-control'
|
|
])
|
|
export type ComputerCapabilityId = z.infer<
|
|
typeof computerCapabilityIdSchema
|
|
>
|
|
|
|
export const browserProfileIdSchema = z.string().uuid()
|
|
export const browserProfileNameSchema = controlCharacterFreeString(80)
|
|
|
|
export const browserProfileCreateInputSchema = z
|
|
.object({
|
|
name: browserProfileNameSchema
|
|
})
|
|
.strict()
|
|
export type BrowserProfileCreateInput = z.infer<
|
|
typeof browserProfileCreateInputSchema
|
|
>
|
|
|
|
export const browserProfileRenameInputSchema = z
|
|
.object({
|
|
profileId: browserProfileIdSchema,
|
|
name: browserProfileNameSchema
|
|
})
|
|
.strict()
|
|
export type BrowserProfileRenameInput = z.infer<
|
|
typeof browserProfileRenameInputSchema
|
|
>
|
|
|
|
export const browserProfileSelectionInputSchema = z
|
|
.object({
|
|
profileId: browserProfileIdSchema
|
|
})
|
|
.strict()
|
|
|
|
export const browserProfileSummarySchema = z
|
|
.object({
|
|
id: browserProfileIdSchema,
|
|
name: browserProfileNameSchema,
|
|
mode: z.literal('managed-isolated')
|
|
})
|
|
.strict()
|
|
export type BrowserProfileSummary = z.infer<
|
|
typeof browserProfileSummarySchema
|
|
>
|
|
|
|
export const browserProfilesSummarySchema = z
|
|
.object({
|
|
profiles: z.array(browserProfileSummarySchema).max(32),
|
|
defaultProfileId: browserProfileIdSchema.nullable()
|
|
})
|
|
.strict()
|
|
export type BrowserProfilesSummary = z.infer<
|
|
typeof browserProfilesSummarySchema
|
|
>
|
|
|
|
export const computerCapabilityToggleInputSchema = z
|
|
.object({
|
|
capabilityId: computerCapabilityIdSchema,
|
|
enabled: z.boolean()
|
|
})
|
|
.strict()
|
|
|
|
export const computerCapabilityConfigInputSchema = z
|
|
.object({
|
|
capabilityId: computerCapabilityIdSchema,
|
|
browserProfileId: browserProfileIdSchema.nullable()
|
|
})
|
|
.strict()
|
|
|
|
export const computerCapabilityConfigSummarySchema = z
|
|
.object({
|
|
id: computerCapabilityIdSchema,
|
|
name: z.string().min(1).max(80),
|
|
description: z.string().min(1).max(500),
|
|
enabled: z.boolean(),
|
|
supported: z.boolean(),
|
|
browserProfileId: browserProfileIdSchema.nullable(),
|
|
riskSummary: z.string().min(1).max(500)
|
|
})
|
|
.strict()
|
|
export type ComputerCapabilityConfigSummary = z.infer<
|
|
typeof computerCapabilityConfigSummarySchema
|
|
>
|
|
|
|
export const capabilityDiagnosticStatusSchema = z.enum([
|
|
'available',
|
|
'degraded',
|
|
'unavailable',
|
|
'disabled'
|
|
])
|
|
export type CapabilityDiagnosticStatus = z.infer<
|
|
typeof capabilityDiagnosticStatusSchema
|
|
>
|
|
|
|
export const capabilityDiagnosticCheckStatusSchema =
|
|
capabilityDiagnosticStatusSchema.exclude(['disabled'])
|
|
|
|
export const capabilityDiagnosticCheckSchema = z
|
|
.object({
|
|
id: z
|
|
.string()
|
|
.min(1)
|
|
.max(80)
|
|
.regex(/^[a-z][a-z0-9-]*$/u),
|
|
status: capabilityDiagnosticCheckStatusSchema,
|
|
summary: z.string().min(1).max(240),
|
|
remedy: z.string().min(1).max(400).optional()
|
|
})
|
|
.strict()
|
|
|
|
export const capabilityDiagnosticReportSchema = z
|
|
.object({
|
|
capabilityId: computerCapabilityIdSchema,
|
|
status: capabilityDiagnosticStatusSchema,
|
|
checkedAt: z.string().datetime(),
|
|
checks: z.array(capabilityDiagnosticCheckSchema).max(16)
|
|
})
|
|
.strict()
|
|
export type CapabilityDiagnosticReport = z.infer<
|
|
typeof capabilityDiagnosticReportSchema
|
|
>
|
|
|
|
export const mcpTransportSchema = z.enum(['stdio', 'http', 'sse'])
|
|
export type McpTransport = z.infer<typeof mcpTransportSchema>
|
|
|
|
export const mcpServerIdSchema = z.string().uuid()
|
|
const mcpServerNameSchema = controlCharacterFreeString(80)
|
|
const mcpServerDescriptionSchema = z.string().trim().max(500)
|
|
const mcpCommandSchema = controlCharacterFreeString(4_096)
|
|
const mcpArgumentSchema = controlCharacterFreeString(4_096)
|
|
const mcpRemoteUrlSchema = z
|
|
.string()
|
|
.url()
|
|
.max(2_048)
|
|
.superRefine((value, context) => {
|
|
if (!['http:', 'https:'].includes(new URL(value).protocol)) {
|
|
context.addIssue({
|
|
code: 'custom',
|
|
message: 'MCP URL 必须使用 HTTP 或 HTTPS'
|
|
})
|
|
}
|
|
})
|
|
|
|
const mcpCommonInputShape = {
|
|
name: mcpServerNameSchema,
|
|
description: mcpServerDescriptionSchema,
|
|
enabled: z.boolean(),
|
|
allowDynamicTools: z.boolean(),
|
|
assignments: capabilityAssignmentsSchema,
|
|
secret: secretActionSchema
|
|
}
|
|
|
|
export const mcpServerInputSchema = z.discriminatedUnion('transport', [
|
|
z
|
|
.object({
|
|
...mcpCommonInputShape,
|
|
transport: z.literal('stdio'),
|
|
command: mcpCommandSchema,
|
|
args: z.array(mcpArgumentSchema).max(64)
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
...mcpCommonInputShape,
|
|
transport: z.literal('http'),
|
|
url: mcpRemoteUrlSchema
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
...mcpCommonInputShape,
|
|
transport: z.literal('sse'),
|
|
url: mcpRemoteUrlSchema
|
|
})
|
|
.strict()
|
|
])
|
|
export type McpServerInput = z.infer<typeof mcpServerInputSchema>
|
|
|
|
export const mcpServerSummarySchema = z.discriminatedUnion('transport', [
|
|
z
|
|
.object({
|
|
id: mcpServerIdSchema,
|
|
name: mcpServerNameSchema,
|
|
description: mcpServerDescriptionSchema,
|
|
enabled: z.boolean(),
|
|
allowDynamicTools: z.boolean(),
|
|
assignments: capabilityAssignmentsSchema,
|
|
secretConfigured: z.boolean(),
|
|
transport: z.literal('stdio'),
|
|
command: mcpCommandSchema,
|
|
args: z.array(mcpArgumentSchema).max(64)
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
id: mcpServerIdSchema,
|
|
name: mcpServerNameSchema,
|
|
description: mcpServerDescriptionSchema,
|
|
enabled: z.boolean(),
|
|
allowDynamicTools: z.boolean(),
|
|
assignments: capabilityAssignmentsSchema,
|
|
secretConfigured: z.boolean(),
|
|
transport: z.literal('http'),
|
|
url: mcpRemoteUrlSchema
|
|
})
|
|
.strict(),
|
|
z
|
|
.object({
|
|
id: mcpServerIdSchema,
|
|
name: mcpServerNameSchema,
|
|
description: mcpServerDescriptionSchema,
|
|
enabled: z.boolean(),
|
|
allowDynamicTools: z.boolean(),
|
|
assignments: capabilityAssignmentsSchema,
|
|
secretConfigured: z.boolean(),
|
|
transport: z.literal('sse'),
|
|
url: mcpRemoteUrlSchema
|
|
})
|
|
.strict()
|
|
])
|
|
export type McpServerSummary = z.infer<typeof mcpServerSummarySchema>
|
|
|
|
export const webSearchCapabilitySchema = z
|
|
.object({
|
|
provider: z.literal('exa'),
|
|
enabled: z.boolean(),
|
|
availableIn: z.tuple([z.literal('ask'), z.literal('execute')]),
|
|
tools: z.tuple([
|
|
z.literal('web_search'),
|
|
z.literal('web_fetch')
|
|
])
|
|
})
|
|
.strict()
|
|
export type WebSearchCapability = z.infer<
|
|
typeof webSearchCapabilitySchema
|
|
>
|
|
|
|
export const obsidianSettingsSchema = z
|
|
.object({ vaultPath: z.string().trim().max(4096).default('') })
|
|
.strict()
|
|
export type ObsidianSettings = z.infer<typeof obsidianSettingsSchema>
|
|
|
|
export const obsidianConnectionTestResultSchema = z.object({
|
|
vaults: z.array(z.object({
|
|
id: z.string(),
|
|
name: z.string(),
|
|
path: z.string()
|
|
}).strict()),
|
|
toolCount: z.number().int().min(0)
|
|
}).strict()
|
|
export type ObsidianConnectionTestResult = z.infer<typeof obsidianConnectionTestResultSchema>
|
|
|
|
export const capabilitySnapshotSchema = z
|
|
.object({
|
|
skills: z.array(skillSummarySchema).max(256),
|
|
imageGeneration: z
|
|
.object({
|
|
modelProfiles: z.array(z.object({
|
|
id: z.string().uuid(),
|
|
name: z.string()
|
|
}).strict()).max(20),
|
|
assignments: capabilityAssignmentsSchema
|
|
})
|
|
.strict()
|
|
.optional(),
|
|
builtinMcpServers: z
|
|
.array(builtinMcpServerStateSummarySchema)
|
|
.max(6)
|
|
.optional(),
|
|
obsidian: obsidianSettingsSchema.default({ vaultPath: '' }),
|
|
mcpServers: z.array(mcpServerSummarySchema).max(64),
|
|
webSearch: webSearchCapabilitySchema.optional(),
|
|
computerCapabilities: z
|
|
.array(computerCapabilityConfigSummarySchema)
|
|
.max(2)
|
|
.optional(),
|
|
browserProfiles: browserProfilesSummarySchema.optional(),
|
|
warnings: settingsWarningsSchema.optional()
|
|
})
|
|
.strict()
|
|
export type CapabilitySnapshot = z.infer<typeof capabilitySnapshotSchema>
|
|
|
|
export const mcpServerTestResultSchema = z
|
|
.object({
|
|
serverName: z.string().min(1).max(120).optional(),
|
|
serverVersion: z.string().min(1).max(64).optional(),
|
|
dynamicToolsSupported: z.boolean(),
|
|
toolCount: z.number().int().min(0).max(10_000),
|
|
tools: z
|
|
.array(
|
|
z
|
|
.object({
|
|
name: z.string().min(1).max(128),
|
|
description: z.string().max(500).optional()
|
|
})
|
|
.strict()
|
|
)
|
|
.max(100),
|
|
promptCount: z.number().int().min(0).max(10_000).optional(),
|
|
prompts: z
|
|
.array(
|
|
z
|
|
.object({
|
|
name: z.string().min(1).max(128),
|
|
description: z.string().max(500).optional(),
|
|
arguments: z
|
|
.array(
|
|
z
|
|
.object({
|
|
name: z.string().min(1).max(128),
|
|
description: z.string().max(500).optional(),
|
|
required: z.boolean()
|
|
})
|
|
.strict()
|
|
)
|
|
.max(32)
|
|
})
|
|
.strict()
|
|
)
|
|
.max(100)
|
|
.optional(),
|
|
resourceCount: z.number().int().min(0).max(10_000).optional(),
|
|
resources: z
|
|
.array(
|
|
z
|
|
.object({
|
|
name: z.string().min(1).max(200),
|
|
uri: z.string().min(1).max(2_048),
|
|
description: z.string().max(500).optional(),
|
|
mimeType: z.string().min(1).max(200).optional()
|
|
})
|
|
.strict()
|
|
)
|
|
.max(100)
|
|
.optional(),
|
|
promptsSupported: z.boolean().optional(),
|
|
resourcesSupported: z.boolean().optional()
|
|
})
|
|
.strict()
|
|
export type McpServerTestResult = z.infer<
|
|
typeof mcpServerTestResultSchema
|
|
>
|
|
|
|
export const webSearchTestResultSchema = z
|
|
.object({
|
|
provider: z.literal('exa'),
|
|
query: z.string().min(1).max(120),
|
|
durationMs: z.number().int().min(0),
|
|
preview: z.string().min(1).max(500)
|
|
})
|
|
.strict()
|
|
export type WebSearchTestResult = z.infer<
|
|
typeof webSearchTestResultSchema
|
|
>
|