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:
@@ -77,6 +77,7 @@ const MAX_NATIVE_MCP_SERVERS =
|
||||
runtimeNativeInventoryLimits.mcpServers
|
||||
const MAX_NATIVE_SKILLS = runtimeNativeInventoryLimits.skills
|
||||
const MAX_NATIVE_RESOURCES = runtimeNativeInventoryLimits.resources
|
||||
const COMPACTION_USAGE_EVENT_GRACE_MS = 1_000
|
||||
const EMBEDDED_SERVER_USERNAME = 'goodbuddy'
|
||||
const OPENCODE_SKILL_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u
|
||||
const TEMPORARY_MCP_PREFIXES = [
|
||||
@@ -2407,27 +2408,120 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
if (context.error || !context.data) {
|
||||
throw new Error('OpenCode 原生上下文不可用,无法执行 Compact')
|
||||
}
|
||||
signal.throwIfAborted()
|
||||
const compact = await client.v2.session.compact(
|
||||
{ sessionID: sessionId },
|
||||
{ signal }
|
||||
)
|
||||
if (compact.error) {
|
||||
throw new Error(
|
||||
opencodeErrorMessage(
|
||||
compact.error,
|
||||
'OpenCode 原生 Compact 失败'
|
||||
)
|
||||
)
|
||||
}
|
||||
return {
|
||||
result: {
|
||||
provider: 'opencode',
|
||||
strategy: 'native',
|
||||
compacted: true,
|
||||
detail: 'OpenCode 已完成原生上下文压缩'
|
||||
const latestAssistant = [...context.data.data]
|
||||
.reverse()
|
||||
.find((message) => message.type === 'assistant')
|
||||
const configuredModel = this.options.modelProfile
|
||||
? {
|
||||
providerID: resolveOpenCodeProvider(
|
||||
this.options.modelProfile
|
||||
).id,
|
||||
modelID: this.options.modelProfile.modelName
|
||||
}
|
||||
: latestAssistant?.type === 'assistant'
|
||||
? {
|
||||
providerID: latestAssistant.model.providerID,
|
||||
modelID: latestAssistant.model.id
|
||||
}
|
||||
: undefined
|
||||
if (!configuredModel) {
|
||||
return {
|
||||
result: {
|
||||
provider: 'opencode',
|
||||
strategy: 'native',
|
||||
compacted: false,
|
||||
detail: '当前 OpenCode 会话尚无可用于压缩的模型记录'
|
||||
}
|
||||
}
|
||||
}
|
||||
signal.throwIfAborted()
|
||||
const subscriptionController = new AbortController()
|
||||
const subscription = await client.event.subscribe(
|
||||
{ directory: this.options.defaultWorkspace },
|
||||
{
|
||||
signal: AbortSignal.any([
|
||||
signal,
|
||||
subscriptionController.signal
|
||||
])
|
||||
}
|
||||
)
|
||||
const usageEvents: RuntimeModelUsageEvent[] = []
|
||||
const reportedMessageIds = new Set<string>()
|
||||
const usageCapture = (async () => {
|
||||
for await (const event of subscription.stream) {
|
||||
if (
|
||||
event.type === 'message.updated' &&
|
||||
event.properties.sessionID === sessionId &&
|
||||
event.properties.info.sessionID === sessionId &&
|
||||
event.properties.info.role === 'assistant' &&
|
||||
!reportedMessageIds.has(event.properties.info.id)
|
||||
) {
|
||||
const usage = createUsageEvent(
|
||||
request.requestId,
|
||||
event.properties.info
|
||||
)
|
||||
if (usage) {
|
||||
reportedMessageIds.add(event.properties.info.id)
|
||||
usageEvents.push(usage)
|
||||
}
|
||||
}
|
||||
if (
|
||||
event.type === 'session.idle' &&
|
||||
event.properties.sessionID === sessionId
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})()
|
||||
try {
|
||||
const compact = await client.session.summarize(
|
||||
{
|
||||
sessionID: sessionId,
|
||||
directory: this.options.defaultWorkspace,
|
||||
providerID: configuredModel.providerID,
|
||||
modelID: configuredModel.modelID,
|
||||
auto: false
|
||||
},
|
||||
{ signal }
|
||||
)
|
||||
if (compact.error || compact.data !== true) {
|
||||
throw new Error(
|
||||
opencodeErrorMessage(
|
||||
compact.error,
|
||||
'OpenCode 原生 Compact 失败'
|
||||
)
|
||||
)
|
||||
}
|
||||
let graceTimer: ReturnType<typeof setTimeout> | undefined
|
||||
try {
|
||||
await Promise.race([
|
||||
usageCapture,
|
||||
new Promise<void>((resolveGrace) => {
|
||||
graceTimer = setTimeout(
|
||||
resolveGrace,
|
||||
COMPACTION_USAGE_EVENT_GRACE_MS
|
||||
)
|
||||
graceTimer.unref?.()
|
||||
})
|
||||
])
|
||||
} finally {
|
||||
if (graceTimer) {
|
||||
clearTimeout(graceTimer)
|
||||
}
|
||||
}
|
||||
return {
|
||||
result: {
|
||||
provider: 'opencode',
|
||||
strategy: 'native',
|
||||
compacted: true,
|
||||
detail: 'OpenCode 已完成原生上下文压缩'
|
||||
},
|
||||
...(usageEvents.length > 0 ? { usageEvents } : {})
|
||||
}
|
||||
} finally {
|
||||
subscriptionController.abort()
|
||||
await usageCapture.catch(() => undefined)
|
||||
}
|
||||
} finally {
|
||||
releaseConversation?.()
|
||||
releaseEmbedded()
|
||||
|
||||
Reference in New Issue
Block a user