Files
goodbuddy/src/main/agent/deepseek-harness-extension-loader.ts
T
mesalogo cbbe30896e 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、插件和上下文管理,并提升长对话、多会话与任务通知的连贯性。
2026-08-17 12:57:12 +08:00

207 lines
5.9 KiB
TypeScript

import type { Context, Fiber, Plugin } from '@deepseek-ai/cordis'
import { createRequire } from 'node:module'
import { fileURLToPath, pathToFileURL } from 'node:url'
import {
DEEPSEEK_HARNESS_EXTENSION_ACTIVATION_TIMEOUT_MS,
DEEPSEEK_HARNESS_EXTENSION_DISPOSAL_TIMEOUT_MS,
DEEPSEEK_HARNESS_TOTAL_EXTENSION_ACTIVATION_TIMEOUT_MS
} from './deepseek-harness-control-protocol'
const ACTIVATION_TIMEOUT_MESSAGE =
'DeepSeek Harness extension activation timed out'
export type ControlledHarnessExtensionPackage = {
id: string
entrypoint: string
configuration: Record<string, unknown>
}
export type ControlledHarnessExtensionLoadResult = {
loadedIds: string[]
failedIds: string[]
failures: Array<{ id: string; message: string }>
}
type ExtensionModule = {
default?: unknown
apply?: unknown
}
// Keep the import native so Vite does not try to resolve userData file URLs
// while bundling or running Vitest.
const nativeImportModule = new Function(
'specifier',
'return import(specifier)'
) as (specifier: string) => Promise<ExtensionModule>
const requireExtension = createRequire(import.meta.url)
async function defaultImportModule(
specifier: string
): Promise<ExtensionModule> {
try {
return requireExtension(
fileURLToPath(specifier)
) as ExtensionModule
} catch (error) {
const code =
error &&
typeof error === 'object' &&
'code' in error &&
typeof error.code === 'string'
? error.code
: undefined
if (
code !== 'ERR_REQUIRE_ASYNC_MODULE' &&
code !== 'ERR_REQUIRE_ESM'
) {
throw error
}
return nativeImportModule(specifier)
}
}
function isPlugin(value: unknown): value is Plugin {
return (
typeof value === 'function' ||
(value !== null &&
typeof value === 'object' &&
typeof (value as { apply?: unknown }).apply === 'function')
)
}
function resolvePlugin(module: ExtensionModule): Plugin {
if (isPlugin(module)) {
return module
}
if (isPlugin(module.default)) {
return module.default
}
throw new Error(
'DeepSeek Harness extension must export a Cordis plugin'
)
}
async function withTimeout<T>(
operation: PromiseLike<T>,
timeoutMs: number,
message: string,
onTimeout?: () => void
): Promise<T> {
let timer: ReturnType<typeof setTimeout> | undefined
try {
return await Promise.race([
Promise.resolve(operation),
new Promise<never>((_resolve, reject) => {
timer = setTimeout(
() => {
onTimeout?.()
reject(new Error(message))
},
timeoutMs
)
})
])
} finally {
if (timer) {
clearTimeout(timer)
}
}
}
export async function loadControlledHarnessExtensions(
ctx: Context,
extensions: readonly ControlledHarnessExtensionPackage[],
options: {
activationTimeoutMs?: number
totalActivationTimeoutMs?: number
importModule?: (url: string) => Promise<ExtensionModule>
} = {}
): Promise<ControlledHarnessExtensionLoadResult> {
const loadedIds: string[] = []
const failedIds: string[] = []
const failures: Array<{ id: string; message: string }> = []
const activationTimeoutMs =
options.activationTimeoutMs ??
DEEPSEEK_HARNESS_EXTENSION_ACTIVATION_TIMEOUT_MS
const deadline =
Date.now() +
(options.totalActivationTimeoutMs ??
DEEPSEEK_HARNESS_TOTAL_EXTENSION_ACTIVATION_TIMEOUT_MS)
const importModule = options.importModule ?? defaultImportModule
for (const extension of extensions) {
let fiber: (Fiber & PromiseLike<Fiber>) | undefined
let acceptActivation = true
let activationDeadline: number | undefined
try {
const remainingMs = deadline - Date.now()
if (remainingMs <= 0) {
throw new Error(
'DeepSeek Harness extension startup deadline exceeded'
)
}
const extensionBudgetMs = Math.max(
1,
Math.min(activationTimeoutMs, remainingMs)
)
activationDeadline = Date.now() + extensionBudgetMs
const rejectLateSynchronousWork = (): void => {
if (Date.now() > activationDeadline!) {
acceptActivation = false
throw new Error(ACTIVATION_TIMEOUT_MESSAGE)
}
}
const activation = (async () => {
const module = await importModule(
pathToFileURL(extension.entrypoint).href
)
rejectLateSynchronousWork()
if (!acceptActivation) {
throw new Error(ACTIVATION_TIMEOUT_MESSAGE)
}
const plugin = resolvePlugin(module)
fiber = ctx.plugin(plugin, extension.configuration)
// A timer cannot run while CommonJS evaluation or a plugin's
// synchronous apply body owns this event loop. Re-check elapsed
// wall time immediately after those calls return so finite
// over-budget work is never reported as successfully activated.
rejectLateSynchronousWork()
await Promise.resolve(fiber)
rejectLateSynchronousWork()
})()
await withTimeout(
activation,
Math.max(1, activationDeadline - Date.now()),
ACTIVATION_TIMEOUT_MESSAGE,
() => {
acceptActivation = false
}
)
loadedIds.push(extension.id)
} catch (error) {
const failure =
activationDeadline !== undefined &&
Date.now() > activationDeadline
? new Error(ACTIVATION_TIMEOUT_MESSAGE)
: error
if (fiber) {
await withTimeout(
fiber.dispose(),
DEEPSEEK_HARNESS_EXTENSION_DISPOSAL_TIMEOUT_MS,
'DeepSeek Harness extension disposal timed out'
).catch(() => undefined)
}
failedIds.push(extension.id)
failures.push({
id: extension.id,
message:
failure instanceof Error && failure.message.trim()
? failure.message.slice(0, 1_000)
: 'DeepSeek Harness extension failed to start'
})
}
}
return { loadedIds, failedIds, failures }
}