feat: expand multi-runtime workflows for 0.10.0
GoodBuddy previously exposed Runtime capabilities, MCP assignments, plugin controls, context compaction, usage reporting, and task-completion behavior through incomplete or inconsistent paths. This release unifies managed OpenCode, Continue, and DeepSeek Harness controls; adds DSH plugin and image workflows; strengthens MCP and Runtime lifecycle bounds; fixes Windows notification activation; validates cross-architecture packages; and presents the approved bilingual four-section release notes. The DSH plugin marketplace remains a default-off preview whose trusted third-party code runs with current-user permissions. Ask remains read-only, Execute keeps approval controls, and context compaction may use the selected model without deleting GoodBuddy chat history. Release note: GoodBuddy 0.10.0 重点完善多 Runtime 工作流,统一 OpenCode、Continue 与 DeepSeek Harness 的能力、MCP、插件和上下文管理,并提升长对话、多会话与任务通知的连贯性。
This commit is contained in:
@@ -7,16 +7,40 @@ export const releaseVersionSchema = z
|
||||
'Release version must be a stable semantic version'
|
||||
)
|
||||
|
||||
const localizedReleaseNotesSchema = z
|
||||
.object({
|
||||
features: z.array(z.string().trim().min(1).max(240)).max(20),
|
||||
fixes: z.array(z.string().trim().min(1).max(240)).max(20)
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
(notes) => notes.features.length > 0 || notes.fixes.length > 0,
|
||||
'Release notes must contain at least one item'
|
||||
)
|
||||
const releaseNoteItemSchema = z.string().trim().min(1).max(500)
|
||||
|
||||
const localizedReleaseNotesSchema = z.preprocess(
|
||||
(value) => {
|
||||
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
||||
return value
|
||||
}
|
||||
const notes = value as Record<string, unknown>
|
||||
if ('highlights' in notes || 'notices' in notes) {
|
||||
return value
|
||||
}
|
||||
return {
|
||||
highlights: [],
|
||||
...notes,
|
||||
notices: []
|
||||
}
|
||||
},
|
||||
z
|
||||
.object({
|
||||
highlights: z.array(releaseNoteItemSchema).max(3),
|
||||
features: z.array(releaseNoteItemSchema).max(20),
|
||||
fixes: z.array(releaseNoteItemSchema).max(20),
|
||||
notices: z.array(releaseNoteItemSchema).max(20)
|
||||
})
|
||||
.strict()
|
||||
.refine(
|
||||
(notes) =>
|
||||
notes.highlights.length > 0 ||
|
||||
notes.features.length > 0 ||
|
||||
notes.fixes.length > 0 ||
|
||||
notes.notices.length > 0,
|
||||
'Release notes must contain at least one item'
|
||||
)
|
||||
)
|
||||
|
||||
export const releaseNoteSchema = z
|
||||
.object({
|
||||
@@ -48,17 +72,23 @@ export const releaseNotesFileSchema = z
|
||||
})
|
||||
}
|
||||
versions.add(release.version)
|
||||
if (
|
||||
release.notes['zh-CN'].features.length !==
|
||||
release.notes['en-US'].features.length ||
|
||||
release.notes['zh-CN'].fixes.length !==
|
||||
release.notes['en-US'].fixes.length
|
||||
) {
|
||||
context.addIssue({
|
||||
code: 'custom',
|
||||
message: 'Localized release-note sections must have matching counts',
|
||||
path: ['releases', index, 'notes']
|
||||
})
|
||||
for (const section of [
|
||||
'highlights',
|
||||
'features',
|
||||
'fixes',
|
||||
'notices'
|
||||
] as const) {
|
||||
if (
|
||||
release.notes['zh-CN'][section].length !==
|
||||
release.notes['en-US'][section].length
|
||||
) {
|
||||
context.addIssue({
|
||||
code: 'custom',
|
||||
message:
|
||||
'Localized release-note sections must have matching counts',
|
||||
path: ['releases', index, 'notes', section]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -58,8 +58,69 @@ export type RuntimeSelectionRepairSettings = {
|
||||
| { kind: 'profile'; profileId: string }
|
||||
}
|
||||
|
||||
type RuntimeModelSource =
|
||||
| { kind: 'platform' }
|
||||
| { kind: 'profile'; profileId: string }
|
||||
|
||||
export type RuntimeSelectionDefaultSettings = {
|
||||
provider: AgentRuntimeSelection['provider']
|
||||
defaultModelProfileId: string
|
||||
opencodeBaseUrl: string
|
||||
opencodeEmbedded: boolean
|
||||
opencodeModelSource?: RuntimeModelSource
|
||||
continueModelSource?: RuntimeModelSource
|
||||
deepseekHarnessModelSource?: RuntimeModelSource
|
||||
opencodeModelProfile?: { id: string }
|
||||
continueModelProfile?: { id: string }
|
||||
deepseekHarnessModelProfile?: { id: string }
|
||||
}
|
||||
|
||||
type ChannelModelProfile = RuntimeSelectionRepairSettings['modelProfiles'][number]
|
||||
|
||||
export function getRuntimeSelectionForProvider(
|
||||
provider: Exclude<AgentRuntimeSelection['provider'], 'auto'>,
|
||||
settings: RuntimeSelectionDefaultSettings
|
||||
): AgentRuntimeSelection {
|
||||
if (provider === 'model') {
|
||||
return {
|
||||
provider,
|
||||
profileId: settings.defaultModelProfileId
|
||||
}
|
||||
}
|
||||
const source =
|
||||
provider === 'opencode'
|
||||
? settings.opencodeModelSource
|
||||
: provider === 'continue'
|
||||
? settings.continueModelSource
|
||||
: settings.deepseekHarnessModelSource
|
||||
const resolvedProfile =
|
||||
provider === 'opencode'
|
||||
? settings.opencodeModelProfile
|
||||
: provider === 'continue'
|
||||
? settings.continueModelProfile
|
||||
: settings.deepseekHarnessModelProfile
|
||||
return {
|
||||
provider,
|
||||
...(source?.kind === 'profile'
|
||||
? { profileId: source.profileId }
|
||||
: !source && resolvedProfile
|
||||
? { profileId: resolvedProfile.id }
|
||||
: {})
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultRuntimeSelection(
|
||||
settings: RuntimeSelectionDefaultSettings
|
||||
): AgentRuntimeSelection {
|
||||
const provider = settings.provider
|
||||
if (provider !== 'auto') {
|
||||
return getRuntimeSelectionForProvider(provider, settings)
|
||||
}
|
||||
return settings.opencodeBaseUrl || settings.opencodeEmbedded
|
||||
? getRuntimeSelectionForProvider('opencode', settings)
|
||||
: getRuntimeSelectionForProvider('model', settings)
|
||||
}
|
||||
|
||||
export function isChannelModelProfileUsable(
|
||||
profile: ChannelModelProfile
|
||||
): boolean {
|
||||
|
||||
Reference in New Issue
Block a user