import { createHash, randomUUID } from 'node:crypto' import { isAbsolute } from 'node:path' import { AgentSideConnection, PROTOCOL_VERSION, RequestError, type Agent, type AgentSideConnection as AcpAgentConnection, type Stream } from '@agentclientprotocol/sdk' import type { Context } from '@deepseek-ai/cordis' import type { AgentHandle } from '@deepseek-ai/dsh-agent' import { CredentialProvider, type CredentialInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials' import { createUserMessage, errorChain, type TokenUsage } from '@deepseek-ai/dsh-llm' import { SessionId, type SessionEvent } from '@deepseek-ai/dsh-session' import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy' import { setApprovalPolicy } from '@deepseek-ai/dsh-user-approval' import type { ToolDefinition, ToolExecution, ToolExecutionResult } from '@deepseek-ai/dsh-tools' import * as ToolSkill from '@deepseek-ai/dsh-tool-skill' export const GOODBUDDY_CONTROL_PROTOCOL_VERSION = 1 export const GOODBUDDY_HANDSHAKE = 'goodbuddy/handshake' export const GOODBUDDY_PREPARE = 'goodbuddy/session/prepare' export const GOODBUDDY_RELEASE = 'goodbuddy/session/release' export const GOODBUDDY_EVENT = 'goodbuddy/session/event' export const GOODBUDDY_CREDENTIAL = 'goodbuddy/credential/resolve' export const GOODBUDDY_TOOLS_LIST = 'goodbuddy/tools/list' export const GOODBUDDY_TOOLS_CALL = 'goodbuddy/tools/call' export const GOODBUDDY_SHUTDOWN = 'goodbuddy/shutdown' const DEFAULT_MAX_EVENT_CHARACTERS = 64 * 1024 const DEFAULT_MAX_REQUEST_CHARACTERS = 4 * 1024 * 1024 export const GOODBUDDY_HARNESS_MAX_STEP_TOKENS = 16 * 1024 const DELTA_BATCH_CHARACTERS = 4 * 1024 const DELTA_BATCH_INTERVAL_MS = 100 const MAX_SUMMARY_CHARACTERS = 4_000 const MAX_FINGERPRINT_BYTES = 4 * 1024 * 1024 const MAX_MCP_PROXY_RESULT_BYTES = 256 * 1024 const GOODBUDDY_EXECUTION_GUIDANCE = [ 'GoodBuddy controlled execution 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 oneShotApproval: true reasoningEvents: boolean toolEvents: boolean usageEvents: boolean credentialResolution: true } sandbox: { provider: string enforcement: 'full' | 'partial' } } export type GoodBuddyHarnessControlConfig = { provider: string model: string workspace: string harnessVersion: string sandbox: GoodBuddyHarnessCapabilities['sandbox'] credentialRefs: readonly string[] skills: readonly { name: string description: string content: string directory: string }[] stream?: Stream maxEventCharacters?: number maxRequestCharacters?: number } type Preparation = { requestId: string mode: GoodBuddyWorkMode } type OwnedSession = { handle: AgentHandle preparation?: Preparation proxyToolDisposers: Map void> sandboxRetries: GoodBuddySandboxRetryLedger inflight?: { requestId: string messageId: string turn?: number endReason?: string turnError?: unknown resolve: (reason: string) => void reject: (error: unknown) => void emittedCharacters: number 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 > 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 DeniedToolCall = { toolName: string operationFingerprint: string } type CredentialResolver = ( ref: string ) => Promise function argumentsFingerprint(value: unknown): string | undefined { try { const serialized = JSON.stringify(value, (_key, nested) => { if ( nested && typeof nested === 'object' && !Array.isArray(nested) ) { return Object.fromEntries( Object.entries(nested as Record).sort( ([left], [right]) => left.localeCompare(right) ) ) } return nested }) if ( serialized === undefined || Buffer.byteLength(serialized, 'utf8') > MAX_FINGERPRINT_BYTES ) { return undefined } return createHash('sha256').update(serialized).digest('hex') } catch { return undefined } } function isSandboxDenial( result: Readonly ): boolean { const sandboxValue = !result.isError && result.value && typeof result.value === 'object' && !Array.isArray(result.value) ? (result.value as Record).sandbox : undefined return ( (result.isError && result.error.info?.code === 'FS_SANDBOX_DENIED') || result.content.some( (content) => content.type === 'text' && content.text.includes('[sandbox: file access denied under ') ) || (!!sandboxValue && typeof sandboxValue === 'object' && !Array.isArray(sandboxValue) && (sandboxValue as Record).denied === true) ) } function requestedEscalation( value: unknown ): { mode: 'workspace-write' | 'danger-full-access' operationFingerprint: string } | undefined { if ( !value || typeof value !== 'object' || Array.isArray(value) ) { return undefined } const argumentsRecord = value as Record const mode = argumentsRecord.sandbox_permissions if ( (mode !== 'workspace-write' && mode !== 'danger-full-access') || typeof argumentsRecord.justification !== 'string' || !argumentsRecord.justification.trim() ) { return undefined } const operationArguments = { ...argumentsRecord } delete operationArguments.sandbox_permissions delete operationArguments.justification const operationFingerprint = argumentsFingerprint( operationArguments ) return operationFingerprint ? { mode, operationFingerprint } : undefined } export class GoodBuddySandboxRetryLedger { private readonly deniedToolCalls = new Map< string, DeniedToolCall >() clear(): void { this.deniedToolCalls.clear() } record( execution: Readonly, result: Readonly ): void { if (!isSandboxDenial(result)) { return } const operationFingerprint = argumentsFingerprint( execution.arguments ) if (!operationFingerprint) { return } this.deniedToolCalls.set(execution.callId, { toolName: execution.name, operationFingerprint }) } consumeRetry(toolName: string, argumentsValue: unknown): boolean { const escalation = requestedEscalation(argumentsValue) if (escalation?.mode !== 'danger-full-access') { return false } const denied = [...this.deniedToolCalls.entries()] .reverse() .find( ([, candidate]) => candidate.toolName === toolName && candidate.operationFingerprint === escalation.operationFingerprint ) if (!denied) { return false } this.deniedToolCalls.delete(denied[0]) return true } } /** * 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') } } 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 { type: string; text?: string }[] ): string { if ( prompt.some( (block) => block.type !== 'text' && block.type !== 'resource_link' ) ) { throw RequestError.invalidParams( undefined, 'only text and resource_link prompt content is supported' ) } return prompt .map((block) => (block.type === 'text' ? block.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 readonly maxRequestCharacters: 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 this.maxRequestCharacters = config.maxRequestCharacters ?? DEFAULT_MAX_REQUEST_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, oneShotApproval: true, reasoningEvents: true, toolEvents: true, usageEvents: true, credentialResolution: true }, sandbox: this.config.sandbox } } 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' ) } inflight.emittedCharacters += text.length if ( inflight.emittedCharacters > this.maxRequestCharacters ) { record.handle.agent.cancel({ kind: 'user' }) throw new Error( 'GoodBuddy Harness control request output 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( '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 === 'assistant/chunk') { const chunk = event.data.chunk if ( chunk.type === 'text-delta' || chunk.type === 'reasoning-delta' ) { this.queueDelta( session.header.id, chunk.type === 'text-delta' ? 'text' : 'reasoning', chunk.text ) } else if (chunk.type === 'usage') { this.queueUsage(session.header.id, chunk.usage) } } else 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.content.find( (content) => content.type === 'tool-result' ) this.queueEvent(session.header.id, { type: 'tool', callId: toolResult?.toolCallId ?? 'unknown-tool-call', name: 'tool', state: event.data.error ? '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 } } } ) this.ctx.on( 'tools/result', ( exec: Readonly, result: Readonly ) => { const sessionId = exec.agent?.session.id if (!sessionId) { return } const record = this.sessions.get(sessionId) if ( record?.handle.agent !== exec.agent || !record.inflight ) { return } record.sandboxRetries.record(exec, result) } ) this.ctx.on('approval/request', async (request, next) => { const record = this.sessions.get(request.agent.session.id) if ( !record || record.handle.agent !== request.agent || !record.inflight || !this.connection ) { return next() } const matchingRetry = request.callId ? record.handle.agent.session.events .filter( ( event ): event is Extract< SessionEvent, { type: 'tool/call' } > => event.type === 'tool/call' && event.data.callId === request.callId ) .at(-1) : undefined let retryArguments: unknown if (matchingRetry) { try { retryArguments = JSON.parse(matchingRetry.data.arguments) } catch { return 'rejected' } } if ( !matchingRetry || !record.sandboxRetries.consumeRetry( request.toolName, retryArguments ) ) { return 'rejected' } const response = await this.connection.requestPermission({ sessionId: request.agent.session.id, toolCall: { toolCallId: request.callId ?? `approval-${randomUUID()}`, title: request.reason ?? request.toolName }, options: [ { optionId: 'allow-once', name: 'Allow once', kind: 'allow_once' }, { optionId: 'reject-once', name: 'Reject', kind: 'reject_once' } ] }) return response.outcome.outcome === 'selected' && response.outcome.optionId === 'allow-once' ? 'allowed-once' : 'rejected' }) } 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, 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, dispose] of record.proxyToolDisposers) { if (!nextNames.has(name)) { dispose() record.proxyToolDisposers.delete(name) } } for (const tool of tools) { if (!record.proxyToolDisposers.has(tool.name)) { record.proxyToolDisposers.set( tool.name, record.handle.agent.ctx.tools.register( this.proxyToolDefinition(sessionId, tool) ) ) } } } private createAgentApi(): Agent { this.observeSessions() return { initialize: async () => ({ protocolVersion: PROTOCOL_VERSION, agentInfo: { name: 'goodbuddy-deepseek-harness', version: this.config.harnessVersion }, agentCapabilities: { promptCapabilities: { image: false, 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.cwd !== this.config.workspace || params.mcpServers.length > 0 ) { throw RequestError.invalidParams( undefined, 'the controlled workspace and no MCP servers are required' ) } const sessionId = SessionId(randomUUID()) const handle = await this.ctx.agents.create({ sessionId, meta: { cwd: params.cwd }, agentOptions: { provider: this.config.provider, model: this.config.model, maxTokens: GOODBUDDY_HARNESS_MAX_STEP_TOKENS }, setup: async (agentCtx) => { agentCtx.systemPrompt.section({ name: 'goodbuddy:controlled-execution', order: 50, text: GOODBUDDY_EXECUTION_GUIDANCE }) const skillTool = agentCtx.plugin(ToolSkill) 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 Promise.all([skillTool, skillRegistrations]) } }) setSandboxMode(handle.agent.session, 'read-only') setApprovalPolicy(handle.agent.session, 'never') this.sessions.set(sessionId, { handle, proxyToolDisposers: new Map(), sandboxRetries: new GoodBuddySandboxRetryLedger() }) 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' ) } setSandboxMode( record.handle.agent.session, preparation.mode === 'ask' ? 'read-only' : 'workspace-write' ) setApprovalPolicy( record.handle.agent.session, preparation.mode === 'ask' ? 'never' : 'ask' ) if (preparation.mode === 'execute') { await this.refreshProxyTools(params.sessionId, record) } else { for (const dispose of record.proxyToolDisposers.values()) { dispose() } record.proxyToolDisposers.clear() } record.sandboxRetries.clear() const text = promptText(params.prompt) if (!text.trim()) { throw RequestError.invalidParams( undefined, 'empty prompt' ) } const message = createUserMessage({ content: [{ type: 'text', text }], source: { kind: 'user' } }) const stopReason = await new Promise( (resolve, reject) => { record.inflight = { requestId: preparation.requestId, messageId: message.id, resolve, reject, emittedCharacters: 0, eventTail: Promise.resolve() } try { record.handle.agent.followup(message) } catch (error) { record.inflight = undefined reject(error) return } 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_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 } await record.handle.dispose() } 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()) ) })() await this.disposing } }