import { randomUUID } from 'node:crypto' import { isAbsolute } from 'node:path' import { AgentSideConnection, PROTOCOL_VERSION, RequestError, type Agent, type AgentSideConnection as AcpAgentConnection, type ContentBlock as AcpContentBlock, type Stream } from '@agentclientprotocol/sdk' import type { Context } from '@deepseek-ai/cordis' import type { AgentHandle } from '@deepseek-ai/dsh-agent' import { AttachmentError, isImageAdmissionError, type ImageAttachmentRef, type SaveImageAttachment } from '@deepseek-ai/dsh-attachment' import { CredentialProvider, type CredentialInfo, type CredentialKey, type CredentialRecord, type CredentialRecordEntry, type CredentialRecordInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials' import { createUserMessage, errorChain, type ContentBlock as HarnessContentBlock, type TokenUsage } from '@deepseek-ai/dsh-llm' import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' import type { ToolDefinition } from '@deepseek-ai/dsh-tools' import * as ToolSkill from '@deepseek-ai/dsh-tool-skill' import { GOODBUDDY_CONTROL_PROTOCOL_VERSION, GOODBUDDY_CREDENTIAL, GOODBUDDY_EVENT, GOODBUDDY_HANDSHAKE, GOODBUDDY_NATIVE_SNAPSHOT, GOODBUDDY_PREPARE, GOODBUDDY_RELEASE, GOODBUDDY_SHUTDOWN, GOODBUDDY_TOOLS_CALL, GOODBUDDY_TOOLS_LIST } from './deepseek-harness-protocol' import { GoodBuddyHarnessAttachmentStore } from './goodbuddy-harness-attachment-store' export { GOODBUDDY_CONTROL_PROTOCOL_VERSION, GOODBUDDY_CREDENTIAL, GOODBUDDY_EVENT, GOODBUDDY_HANDSHAKE, GOODBUDDY_NATIVE_SNAPSHOT, GOODBUDDY_RELEASE, GOODBUDDY_SHUTDOWN, GOODBUDDY_TOOLS_CALL, GOODBUDDY_TOOLS_LIST, GOODBUDDY_PREPARE } from './deepseek-harness-protocol' const DEFAULT_MAX_EVENT_CHARACTERS = 64 * 1024 const DELTA_BATCH_CHARACTERS = 4 * 1024 const DELTA_BATCH_INTERVAL_MS = 100 const MAX_SUMMARY_CHARACTERS = 4_000 const MAX_MCP_PROXY_RESULT_BYTES = 256 * 1024 const MAX_NATIVE_SKILLS = 200 const MAX_NATIVE_TOOLS = 200 const NATIVE_SNAPSHOT_TIMEOUT_MS = 2_000 const MAIN_WEB_TOOL_NAMES = new Set(['web_search', 'web_fetch']) const GOODBUDDY_EXECUTION_GUIDANCE = [ 'GoodBuddy work mode rules:', '- In Execute mode, act through the available tools instead of writing a long implementation plan.', '- Inspect only what is needed, then create or update the requested workspace files promptly.', '- Work in small verifiable steps and use tool results as the source of truth.', '- Keep reasoning concise. Do not narrate code that can be written and checked with tools.', '- In Ask mode, remain read-only and do not attempt mutations.' ].join('\n') export type GoodBuddyWorkMode = 'ask' | 'execute' export type GoodBuddyHarnessCapabilities = { controlProtocolVersion: 1 harnessVersion: string acpProtocolVersion: number supports: { cancellation: true sessionRelease: true reasoningEvents: boolean toolEvents: boolean usageEvents: boolean credentialResolution: true } execution: { mode: 'host' } } export type GoodBuddyHarnessControlConfig = { provider: string model: string supportsImageInput?: boolean workspace: string harnessVersion: string execution: GoodBuddyHarnessCapabilities['execution'] credentialRefs: readonly string[] skills: readonly { name: string description: string content: string directory: string }[] trustedAskToolDefinitions?: ReadonlyMap stream?: Stream maxEventCharacters?: number } type Preparation = { requestId: string mode: GoodBuddyWorkMode } type OwnedSession = { handle: AgentHandle attachmentRefs: ImageAttachmentRef[] preparation?: Preparation proxyTools: Map< string, { definition: ToolDefinition dispose: () => void } > askToolDefinitions: Map inflight?: { requestId: string messageId: string mode: GoodBuddyWorkMode turn?: number endReason?: string turnError?: unknown resolve: (reason: string) => void reject: (error: unknown) => void eventTail: Promise eventError?: unknown pendingDelta?: { type: 'text' | 'reasoning' delta: string } pendingDeltaTimer?: ReturnType } } type ProxyToolDefinition = { name: string description: string inputSchema: Record } function validateProxyToolResult( value: unknown ): Record { if ( !value || typeof value !== 'object' || Array.isArray(value) ) { throw new Error('GoodBuddy MCP tool result is invalid') } const serialized = JSON.stringify(value) if ( Buffer.byteLength(serialized, 'utf8') > MAX_MCP_PROXY_RESULT_BYTES ) { throw new Error( 'GoodBuddy MCP tool result exceeds safety limit' ) } const content = (value as Record).content if ( !Array.isArray(content) || content.length > 100 || content.some( (part) => !part || typeof part !== 'object' || Array.isArray(part) || (part as Record).type !== 'text' || typeof (part as Record).text !== 'string' ) ) { throw new Error('GoodBuddy MCP tool result is invalid') } return value as Record } function parseProxyToolCatalog( value: unknown ): ProxyToolDefinition[] { if (!Array.isArray(value) || value.length > 100) { throw new Error('GoodBuddy MCP tool catalog is invalid') } const names = new Set() return value.map((candidate) => { if ( !candidate || typeof candidate !== 'object' || Array.isArray(candidate) ) { throw new Error('GoodBuddy MCP tool definition is invalid') } const tool = candidate as Record if ( typeof tool.name !== 'string' || !/^[a-zA-Z0-9_-]{1,64}$/u.test(tool.name) || names.has(tool.name) || typeof tool.description !== 'string' || tool.description.length > (tool.name === 'generate_image' ? imageToolDescriptionLimit : 1_000) || !tool.inputSchema || typeof tool.inputSchema !== 'object' || Array.isArray(tool.inputSchema) ) { throw new Error('GoodBuddy MCP tool definition is invalid') } names.add(tool.name) return { name: tool.name, description: tool.description, inputSchema: tool.inputSchema as Record } }) } type CredentialResolver = ( ref: string ) => Promise /** * Memory-only credential provider. It deliberately has no writable operation * and can resolve only references registered by the trusted host. */ export class GoodBuddyCredentialProvider extends CredentialProvider { private resolver?: CredentialResolver constructor( ctx: Context, private readonly allowedRefs: ReadonlySet ) { super(ctx) } bind(resolver: CredentialResolver): void { if (this.resolver) { throw new Error('GoodBuddy credential resolver is already bound') } this.resolver = resolver } async resolve( ref: CredentialRef ): Promise { if (!this.allowedRefs.has(ref) || !this.resolver) { return undefined } const value = await this.resolver(ref) return value ? { value, source: 'goodbuddy-main' } : undefined } async describe(ref: CredentialRef): Promise { const available = this.allowedRefs.has(ref) && this.resolver !== undefined return { configured: available, source: available ? 'goodbuddy-main' : undefined, writable: false } } async set(ref: CredentialRef, value: string): Promise { void ref void value throw new Error('GoodBuddy Harness credentials are read-only') } async unset(ref: CredentialRef): Promise { void ref throw new Error('GoodBuddy Harness credentials are read-only') } async readRecord( key: CredentialKey ): Promise { void key return undefined } async describeRecord( key: CredentialKey ): Promise { void key return { configured: false, writable: false } } async listRecords(): Promise { return [] } async modifyRecord( key: CredentialKey, mutate: ( current: CredentialRecord | undefined ) => Promise ): Promise { void key void mutate throw new Error('GoodBuddy Harness credentials are read-only') } async deleteRecord(key: CredentialKey): Promise { void key throw new Error('GoodBuddy Harness credentials are read-only') } } export function createBoundedAcpStream( stream: Stream, maxFrameBytes: number ): Stream { if ( !Number.isSafeInteger(maxFrameBytes) || maxFrameBytes < 1 ) { throw new TypeError('maxFrameBytes must be a positive integer') } return { readable: stream.readable.pipeThrough( new TransformStream({ transform(message, controller) { if ( Buffer.byteLength(JSON.stringify(message), 'utf8') > maxFrameBytes ) { throw new Error( `ACP input frame exceeds ${maxFrameBytes} bytes` ) } controller.enqueue(message) } }) ), writable: new WritableStream({ async write(message) { if ( Buffer.byteLength(JSON.stringify(message), 'utf8') > maxFrameBytes ) { throw new Error( `ACP output frame exceeds ${maxFrameBytes} bytes` ) } const writer = stream.writable.getWriter() try { await writer.write(message) } finally { writer.releaseLock() } }, async close() { const writer = stream.writable.getWriter() try { await writer.close() } finally { writer.releaseLock() } }, async abort(reason) { await stream.writable.abort(reason) } }) } } function requiredString( params: Record, key: string, maxLength = 512 ): string { const value = params[key] if ( typeof value !== 'string' || value.length === 0 || value.length > maxLength ) { throw RequestError.invalidParams( undefined, `${key} must be a non-empty bounded string` ) } return value } function boundedJson(value: unknown): string | undefined { if (value === undefined) { return undefined } try { return ( typeof value === 'string' ? value : JSON.stringify(value) ).slice(0, MAX_SUMMARY_CHARACTERS) } catch { return '[unserializable]' } } function promptText(prompt: readonly AcpContentBlock[]): string { const text: string[] = [] for (const block of prompt) { switch (block.type) { case 'text': text.push(block.text) break case 'image': case 'resource_link': break case 'audio': case 'resource': throw RequestError.invalidParams( undefined, `unsupported ACP prompt content: ${block.type}` ) default: block satisfies never } } return text.join('') } function turnReason(event: SessionEvent): string | undefined { if (event.type !== 'turn/end') { return undefined } switch (event.data.reason.kind) { case 'aborted': case 'interrupted': return 'cancelled' case 'max-tokens': return 'max_tokens' default: return 'end_turn' } } export class GoodBuddyHarnessControlPlane { private readonly sessions = new Map() private readonly allowedCredentialRefs: ReadonlySet private readonly maxEventCharacters: number private connection?: AcpAgentConnection private credentialProvider?: GoodBuddyCredentialProvider private handshaken = false private observing = false private closed = false private disposing?: Promise constructor( private readonly ctx: Context, private readonly config: GoodBuddyHarnessControlConfig ) { this.allowedCredentialRefs = new Set(config.credentialRefs) this.maxEventCharacters = config.maxEventCharacters ?? DEFAULT_MAX_EVENT_CHARACTERS } bindCredentialProvider( provider: GoodBuddyCredentialProvider ): void { if (this.connection) { throw new Error( 'Credential provider must be bound before bridge start' ) } this.credentialProvider = provider } start(): AcpAgentConnection { if (this.connection) { throw new Error( 'GoodBuddy Harness control plane is already started' ) } if (!this.config.stream) { throw new Error( 'GoodBuddy Harness control plane requires its internal Host transport' ) } const connection = new AgentSideConnection( () => this.createAgentApi(), this.config.stream ) this.connection = connection this.credentialProvider?.bind(async (ref) => { if (!this.handshaken || !this.allowedCredentialRefs.has(ref)) { return undefined } const response = await connection.extMethod( GOODBUDDY_CREDENTIAL, { ref } ) const value = response.value return typeof value === 'string' && value.length > 0 ? value : undefined }) void connection.closed.finally(() => this.dispose()) return connection } private capabilities(): GoodBuddyHarnessCapabilities { return { controlProtocolVersion: GOODBUDDY_CONTROL_PROTOCOL_VERSION, harnessVersion: this.config.harnessVersion, acpProtocolVersion: PROTOCOL_VERSION, supports: { cancellation: true, sessionRelease: true, reasoningEvents: true, toolEvents: true, usageEvents: true, credentialResolution: true }, execution: this.config.execution } } private requireSession(sessionId: string): OwnedSession { const session = this.sessions.get(sessionId) if (!session) { throw RequestError.invalidParams( undefined, `unknown session: ${sessionId}` ) } return session } private async sendEvent( sessionId: string, event: Record ): Promise { const record = this.sessions.get(sessionId) const inflight = record?.inflight if (!inflight || !this.connection) { return } const params = { sessionId, requestId: inflight.requestId, ...event } const text = JSON.stringify(params) if (text.length > this.maxEventCharacters) { throw new Error( 'GoodBuddy Harness control event exceeds safety limit' ) } await this.connection.extNotification(GOODBUDDY_EVENT, params) } private enqueueEvent( sessionId: string, event: Record ): void { const record = this.sessions.get(sessionId) const inflight = record?.inflight if (!record || !inflight) { return } const previous = inflight.eventTail const queued = previous.then(() => this.sendEvent(sessionId, event) ) inflight.eventTail = queued.catch((error: unknown) => { inflight.eventError ??= error record.handle.agent.cancel({ kind: 'user' }) }) } private flushPendingDelta(sessionId: string): void { const inflight = this.sessions.get(sessionId)?.inflight const pending = inflight?.pendingDelta if (!inflight || !pending) { return } if (inflight.pendingDeltaTimer) { clearTimeout(inflight.pendingDeltaTimer) inflight.pendingDeltaTimer = undefined } inflight.pendingDelta = undefined this.enqueueEvent(sessionId, pending) } private maxDeltaBatchCharacters( sessionId: string, type: 'text' | 'reasoning' ): number { const inflight = this.sessions.get(sessionId)?.inflight if (!inflight) { return 0 } const envelopeLength = JSON.stringify({ sessionId, requestId: inflight.requestId, type, delta: '' }).length return Math.min( DELTA_BATCH_CHARACTERS, this.maxEventCharacters - envelopeLength ) } private queueDelta( sessionId: string, type: 'text' | 'reasoning', delta: string ): void { const inflight = this.sessions.get(sessionId)?.inflight if (!inflight || !delta) { return } const batchLimit = this.maxDeltaBatchCharacters( sessionId, type ) if (batchLimit < 1) { this.failEventStream( sessionId, new Error( 'GoodBuddy Harness control event exceeds safety limit' ) ) return } let remaining = delta while (remaining) { if ( inflight.pendingDelta && inflight.pendingDelta.type !== type ) { this.flushPendingDelta(sessionId) } inflight.pendingDelta ??= { type, delta: '' } const available = batchLimit - inflight.pendingDelta.delta.length if (available <= 0) { this.flushPendingDelta(sessionId) continue } inflight.pendingDelta.delta += remaining.slice(0, available) remaining = remaining.slice(available) if (inflight.pendingDelta.delta.length >= batchLimit) { this.flushPendingDelta(sessionId) } } if ( inflight.pendingDelta && !inflight.pendingDeltaTimer ) { inflight.pendingDeltaTimer = setTimeout(() => { this.flushPendingDelta(sessionId) }, DELTA_BATCH_INTERVAL_MS) } } private queueEvent( sessionId: string, event: Record ): void { this.flushPendingDelta(sessionId) this.enqueueEvent(sessionId, event) } private failEventStream(sessionId: string, error: unknown): void { const record = this.sessions.get(sessionId) const inflight = record?.inflight if (!record || !inflight) { return } inflight.eventError ??= error record.handle.agent.cancel({ kind: 'user' }) } private observeSessions(): void { if (this.observing) { return } this.observing = true this.ctx.on('tools/execute', async (exec, next) => { const sessionId = exec.agent?.session.id const record = sessionId ? this.sessions.get(sessionId) : undefined if ( record && record.handle.agent === exec.agent && record.inflight?.mode === 'ask' ) { const registeredDefinition = record.askToolDefinitions.get(exec.name) const executingDefinition = record.handle.agent.ctx.tools.get( exec.name, exec.agent ) if ( !registeredDefinition || executingDefinition !== registeredDefinition ) { throw new Error( `Ask 模式不允许执行非只读工具:${exec.name}` ) } } return next() }) this.ctx.on('agent/assistant-stream', ({ agent, frame }) => { const record = this.sessions.get(agent.session.id) if ( record?.handle.agent !== agent || !record.inflight || frame.type !== 'chunk' ) { return } const chunk = frame.chunk if (chunk.type === 'text-delta' || chunk.type === 'reasoning-delta') { this.queueDelta( agent.session.id, chunk.type === 'text-delta' ? 'text' : 'reasoning', chunk.text ) } else if (chunk.type === 'usage') { this.queueUsage(agent.session.id, chunk.usage) } }) this.ctx.on( 'session/event', (session, event: SessionEvent) => { const record = this.sessions.get(session.header.id) if (!record || record.handle.agent.session !== session) { return } const inflight = record.inflight if (!inflight) { return } if (event.type === 'tool/call') { this.queueEvent(session.header.id, { type: 'tool', callId: event.data.callId, name: event.data.name, state: 'pending', input: event.data.arguments.slice( 0, MAX_SUMMARY_CHARACTERS ) }) } else if (event.type === 'tool/result') { const toolResult = event.data.message this.queueEvent(session.header.id, { type: 'tool', callId: toolResult.toolCallId, name: 'tool', state: event.data.error || toolResult.isError === true ? 'failed' : 'completed', output: boundedJson(event.data.message.content) }) } if ( event.type === 'turn/end' && inflight.turn === event.data.turn ) { inflight.endReason = turnReason(event) } } ) this.ctx.on( 'agent/inbox/claimed', ({ agent, message, turn }) => { const record = this.sessions.get(agent.session.id) if ( record?.handle.agent === agent && record.inflight?.messageId === message.id ) { record.inflight.turn = turn } } ) this.ctx.on( 'agent/error', ({ agent, error }) => { const record = this.sessions.get(agent.session.id) if (record?.handle.agent === agent) { if (record.inflight) { record.inflight.turnError = error } } } ) } private queueUsage(sessionId: string, usage: TokenUsage): void { this.flushPendingDelta(sessionId) this.enqueueEvent(sessionId, { type: 'model-usage', callId: randomUUID(), provider: this.config.provider, model: this.config.model, inputTokens: usage.inputTokens, outputTokens: usage.outputTokens, cacheReadTokens: usage.cacheReadTokens ?? 0, cacheWriteTokens: usage.cacheWriteTokens ?? 0 }) } private proxyToolDefinition( sessionId: string, tool: ProxyToolDefinition ): ToolDefinition { return { name: tool.name, description: tool.description, parameters: tool.inputSchema, output: { schema: { type: 'object', properties: { content: { type: 'array', items: { type: 'object', additionalProperties: true } } }, required: ['content'], additionalProperties: false }, render(_args, result) { const content = ( result as { content: Array<{ type?: unknown text?: unknown }> } ).content return content .filter( ( part ): part is { type: 'text' text: string } => part.type === 'text' && typeof part.text === 'string' ) .map((part) => ({ type: 'text' as const, text: part.text })) } }, execute: async (argumentsValue) => { const result = await this.connection!.extMethod( GOODBUDDY_TOOLS_CALL, { sessionId, name: tool.name, callId: randomUUID(), arguments: argumentsValue } ) return validateProxyToolResult(result) } } } private async refreshProxyTools( sessionId: string, record: OwnedSession ): Promise { const response = await this.connection!.extMethod( GOODBUDDY_TOOLS_LIST, { sessionId } ) const tools = parseProxyToolCatalog(response.tools) const nextNames = new Set(tools.map((tool) => tool.name)) for (const [name, registration] of record.proxyTools) { if (!nextNames.has(name)) { registration.dispose() record.proxyTools.delete(name) record.askToolDefinitions.delete(name) } } for (const tool of tools) { if (!record.proxyTools.has(tool.name)) { const definition = this.proxyToolDefinition(sessionId, tool) const dispose = record.handle.agent.ctx.tools.register(definition) record.proxyTools.set(tool.name, { definition, dispose }) if (MAIN_WEB_TOOL_NAMES.has(tool.name) || isStoryGraphTool(tool.name)) { record.askToolDefinitions.set(tool.name, definition) } } } } private async nativeSnapshot( workspace = this.config.workspace ): Promise> { const controller = new AbortController() const timer = setTimeout( () => controller.abort( new Error('DeepSeek Harness native inventory timed out') ), NATIVE_SNAPSHOT_TIMEOUT_MS ) try { const skills = await Promise.race([ this.ctx.skills.list({ cwd: workspace, signal: controller.signal }), new Promise((_resolve, reject) => { controller.signal.addEventListener( 'abort', () => reject(controller.signal.reason), { once: true } ) }) ]) let toolsSupported = true let tools: Array<{ id: string name: string description?: string }> = [] try { tools = this.ctx.tools .schemas() .slice(0, MAX_NATIVE_TOOLS) .flatMap((tool) => { const name = tool.name.trim().slice(0, 128) if (!name) { return [] } const description = tool.description.trim().slice(0, 2_000) return [ { id: name, name: name.slice(0, 200), ...(description ? { description } : {}) } ] }) } catch { toolsSupported = false } return { tools, toolsSupported, skills: skills.slice(0, MAX_NATIVE_SKILLS).map((skill) => ({ id: skill.name.slice(0, 128), name: skill.name.slice(0, 200), description: skill.description.slice(0, 2_000), source: skill.source.slice(0, 128), provider: skill.provider.slice(0, 128) })) } } finally { clearTimeout(timer) } } private attachmentStore(): GoodBuddyHarnessAttachmentStore { const store = this.ctx.get('attachments') if (!(store instanceof GoodBuddyHarnessAttachmentStore)) { throw RequestError.internalError( undefined, 'Harness image attachment service is unavailable' ) } return store } private releaseAttachments(refs: readonly ImageAttachmentRef[]): void { if (refs.length === 0) { return } const store = this.attachmentStore() for (const ref of refs) { store.releaseImage(ref) } } private async storePromptImages( prompt: readonly AcpContentBlock[] ): Promise { const imageBlocks = prompt.filter( ( block ): block is Extract => block.type === 'image' ) if (imageBlocks.length === 0) { return [] } if (!this.config.supportsImageInput) { throw RequestError.invalidParams( undefined, 'the selected model does not accept image input' ) } const store = this.attachmentStore() if (imageBlocks.length > store.imageLimits.maxImagesPerMessage) { throw RequestError.invalidParams( undefined, 'too many images in one prompt' ) } const inputs: SaveImageAttachment[] = [] let totalBytes = 0 for (const block of imageBlocks) { if ( block.uri != null || !store.imageLimits.mediaTypes.includes( block.mimeType as SaveImageAttachment['mediaType'] ) || block.data.length === 0 || block.data.length % 4 !== 0 || block.data.length > Math.ceil(store.imageLimits.maxImageBytes / 3) * 4 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test( block.data ) ) { throw RequestError.invalidParams( undefined, 'invalid inline image prompt content' ) } const data = Buffer.from(block.data, 'base64') totalBytes += data.byteLength if ( data.byteLength === 0 || data.byteLength > store.imageLimits.maxImageBytes || totalBytes > store.imageLimits.maxMessageImageBytes || data.toString('base64') !== block.data ) { throw RequestError.invalidParams( undefined, 'invalid inline image prompt content' ) } inputs.push({ data, mediaType: block.mimeType as SaveImageAttachment['mediaType'] }) } try { return await store.saveImages(inputs) } catch (error) { if (error instanceof AttachmentError) { if (isImageAdmissionError(error)) { throw RequestError.invalidParams( undefined, 'invalid inline image prompt content' ) } if (error.code === 'ATTACHMENT_WRITE_FAILED') { throw RequestError.internalError( undefined, 'Harness image attachment service is unavailable' ) } } throw RequestError.internalError( undefined, 'Harness image attachment storage failed' ) } } private createAgentApi(): Agent { this.observeSessions() return { initialize: async () => ({ protocolVersion: PROTOCOL_VERSION, agentInfo: { name: 'goodbuddy-deepseek-harness', version: this.config.harnessVersion }, agentCapabilities: { promptCapabilities: { image: this.config.supportsImageInput === true, audio: false, embeddedContext: false }, sessionCapabilities: { close: {} } }, authMethods: [] }), authenticate: async () => undefined, newSession: async (params) => { if (this.closed) { throw RequestError.internalError( undefined, 'bridge is shutting down' ) } if ( !isAbsolute(params.cwd) || params.mcpServers.length > 0 ) { throw RequestError.invalidParams( undefined, 'an absolute workspace and no MCP servers are required' ) } const sessionId = SessionId(randomUUID()) let genuineSkillDefinition: ToolDefinition | undefined const handle = await this.ctx.agents.create({ sessionId, meta: { cwd: params.cwd }, agentOptions: { provider: this.config.provider, model: this.config.model }, setup: async (agentCtx) => { agentCtx.systemPrompt.section({ name: 'goodbuddy:controlled-execution', order: 50, text: GOODBUDDY_EXECUTION_GUIDANCE }) const skillRegistrations = agentCtx.inject( ['skills'], (skillCtx) => { for (const skill of this.config.skills) { skillCtx.skills.register({ name: skill.name, description: skill.description, content: skill.content, source: 'bundled', resourceBase: { kind: 'directory', path: skill.directory }, invocation: { modelInvocable: true, userInvocable: true } }) } } ) await agentCtx.plugin(ToolSkill) genuineSkillDefinition = agentCtx.tools.get('skill') await skillRegistrations } }) const askToolDefinitions = new Map( this.config.trustedAskToolDefinitions ?? [] ) if (genuineSkillDefinition) { askToolDefinitions.set( 'skill', genuineSkillDefinition ) } this.sessions.set(sessionId, { handle, attachmentRefs: [], proxyTools: new Map(), askToolDefinitions }) return { sessionId, modes: { currentModeId: 'ask', availableModes: [ { id: 'ask', name: 'Ask', description: 'Read-only' } ] } } }, prompt: async (params) => { const record = this.requireSession(params.sessionId) if (record.inflight) { throw RequestError.invalidParams( undefined, 'a prompt is already in flight' ) } const preparation = record.preparation record.preparation = undefined if (!preparation) { throw RequestError.invalidParams( undefined, 'a single-use goodbuddy/session/prepare is required' ) } await this.refreshProxyTools(params.sessionId, record) const text = promptText(params.prompt) if (!text.trim()) { throw RequestError.invalidParams( undefined, 'empty prompt' ) } const attachmentRefs = await this.storePromptImages( params.prompt ) let message: ReturnType try { const content: HarnessContentBlock[] = [ { type: 'text', text }, ...attachmentRefs.map((attachment) => ({ type: 'image' as const, attachment })) ] message = createUserMessage({ content, source: { kind: 'user' } }) } catch (error) { this.releaseAttachments(attachmentRefs) throw error } const stopReason = await new Promise( (resolve, reject) => { record.inflight = { requestId: preparation.requestId, messageId: message.id, mode: preparation.mode, resolve, reject, eventTail: Promise.resolve() } try { record.handle.agent.followup(message) } catch (error) { record.inflight = undefined this.releaseAttachments(attachmentRefs) reject(error) return } record.attachmentRefs.push(...attachmentRefs) void record.handle.agent.whenIdle().then(() => { const current = record.inflight if (current?.messageId !== message.id) { return } this.flushPendingDelta(params.sessionId) void current.eventTail.then(() => { if (record.inflight === current) { record.inflight = undefined } if (current.eventError) { current.reject(current.eventError) return } if (current.turnError) { const details = errorChain( current.turnError ).slice(0, MAX_SUMMARY_CHARACTERS) current.reject( RequestError.internalError( { details }, `DeepSeek Harness turn failed: ${details}` ) ) return } current.resolve(current.endReason ?? 'cancelled') }) }, (error) => { if (record.inflight?.messageId === message.id) { record.inflight = undefined } reject(error) }) } ) return { stopReason: stopReason === 'cancelled' ? 'cancelled' : stopReason === 'max_tokens' ? 'max_tokens' : 'end_turn' } }, cancel: async ({ sessionId }) => { const record = this.sessions.get(sessionId) record?.handle.agent.cancel({ kind: 'user' }) if (record?.inflight) { const inflight = record.inflight if (inflight.pendingDeltaTimer) { clearTimeout(inflight.pendingDeltaTimer) } record.inflight = undefined inflight.resolve('cancelled') } }, closeSession: async ({ sessionId }) => { await this.releaseSession(sessionId) }, extMethod: (method, params) => this.extensionMethod(method, params), extNotification: async () => { throw RequestError.methodNotFound('extension notification') } } } async extensionMethod( method: string, params: Record ): Promise> { if (method === GOODBUDDY_HANDSHAKE) { const version = params.controlProtocolVersion if (version !== GOODBUDDY_CONTROL_PROTOCOL_VERSION) { throw RequestError.invalidParams( undefined, 'incompatible GoodBuddy Harness control protocol' ) } this.handshaken = true return this.capabilities() as unknown as Record< string, unknown > } if (!this.handshaken) { throw RequestError.invalidParams( undefined, 'GoodBuddy handshake is required' ) } if (method === GOODBUDDY_PREPARE) { const sessionId = requiredString(params, 'sessionId') const requestId = requiredString(params, 'requestId') const mode = params.mode if (mode !== 'ask' && mode !== 'execute') { throw RequestError.invalidParams( undefined, 'mode must be ask or execute' ) } const record = this.requireSession(sessionId) if (record.inflight || record.preparation) { throw RequestError.invalidParams( undefined, 'session is already prepared or running' ) } record.preparation = { requestId, mode } return { prepared: true } } if (method === GOODBUDDY_RELEASE) { await this.releaseSession( requiredString(params, 'sessionId') ) return { released: true } } if (method === GOODBUDDY_NATIVE_SNAPSHOT) { const workspace = params.workspace === undefined ? this.config.workspace : requiredString(params, 'workspace') if (!isAbsolute(workspace)) throw RequestError.invalidParams(undefined, 'workspace must be absolute') return this.nativeSnapshot(workspace) } if (method === GOODBUDDY_SHUTDOWN) { await this.dispose() return { shutdown: true } } throw RequestError.methodNotFound(method) } async releaseSession(sessionId: string): Promise { const record = this.sessions.get(sessionId) if (!record) { return } this.sessions.delete(sessionId) record.handle.agent.cancel({ kind: 'user' }) if (record.inflight) { if (record.inflight.pendingDeltaTimer) { clearTimeout(record.inflight.pendingDeltaTimer) } record.inflight.resolve('cancelled') record.inflight = undefined } try { await record.handle.dispose() } finally { this.releaseAttachments(record.attachmentRefs) } } async dispose(): Promise { this.disposing ??= (async () => { this.closed = true const sessions = [...this.sessions.entries()] this.sessions.clear() for (const [, record] of sessions) { record.handle.agent.cancel({ kind: 'disposed' }) if (record.inflight?.pendingDeltaTimer) { clearTimeout(record.inflight.pendingDeltaTimer) } record.inflight?.resolve('cancelled') } await Promise.allSettled( sessions.map(([, record]) => record.handle.dispose()) ) for (const [, record] of sessions) { this.releaseAttachments(record.attachmentRefs) } })() await this.disposing } } import { imageToolDescriptionLimit } from '../../shared/image-generation-contracts' import { isStoryGraphTool } from '../../shared/story-graph-tools'