| 316 | } |
| 317 | |
| 318 | async *chatStream( |
| 319 | options: TextOptions<ResolvedOptions<TModelOptions>>, |
| 320 | ): AsyncIterable<StreamChunk> { |
| 321 | const { logger } = options |
| 322 | let handle: AcpSessionHandle | undefined |
| 323 | let bridge: HostToolBridge | undefined |
| 324 | let transport: AcpSessionTransport | undefined |
| 325 | const externalSignal = |
| 326 | options.abortController?.signal ?? options.request?.signal ?? undefined |
| 327 | let onAbort: (() => void) | undefined |
| 328 | |
| 329 | try { |
| 330 | const sandbox = this.sandboxFrom(options) |
| 331 | const modelOptions = options.modelOptions |
| 332 | const cwd = modelOptions?.cwd ?? this.harness.cwd ?? DEFAULT_WORKDIR |
| 333 | const harnessCwd = resolveHarnessCwd(sandbox, cwd) |
| 334 | const runId = options.runId ?? this.generateId() |
| 335 | const threadId = options.threadId ?? this.generateId() |
| 336 | const channel = createBridgeEventChannel({ |
| 337 | model: this.model, |
| 338 | threadId, |
| 339 | runId, |
| 340 | }) |
| 341 | |
| 342 | const sessionId = modelOptions?.sessionId |
| 343 | const { prompt: resumePrompt } = this.buildPrompt( |
| 344 | options.messages, |
| 345 | sessionId, |
| 346 | ) |
| 347 | |
| 348 | // Bridge chat()-provided tools into the agent over MCP (ACP http server). |
| 349 | const bridgedToolNames = new Set( |
| 350 | (options.tools ?? []).map((tool) => tool.name), |
| 351 | ) |
| 352 | if (options.tools && options.tools.length > 0) { |
| 353 | const provisioner = |
| 354 | (options.capabilities |
| 355 | ? getToolBridgeProvisioner(options.capabilities, { optional: true }) |
| 356 | : undefined) ?? nodeHttpBridgeProvisioner |
| 357 | bridge = await provisioner.provision(options.tools, { |
| 358 | provider: sandbox.provider, |
| 359 | context: options.context, |
| 360 | emitCustomEvent: channel.emitCustomEvent, |
| 361 | ...(externalSignal ? { signal: externalSignal } : {}), |
| 362 | }) |
| 363 | } |
| 364 | |
| 365 | // Project workspace skills declared via withSandbox. MCP skills ride ACP's |
| 366 | // native `mcpServers` (below); gitSkills are linked into `skillsDir`. |
| 367 | let workspaceServers: Array<AcpMcpServer> = [] |
| 368 | const projection = options.capabilities |
| 369 | ? getWorkspaceProjection(options.capabilities, { optional: true }) |
| 370 | : undefined |
| 371 | if (projection !== undefined) { |
| 372 | await projectAcpWorkspace(sandbox, projection, { |
| 373 | ...(this.harness.skillsDir !== undefined && { |
| 374 | skillsDir: this.harness.skillsDir, |
| 375 | }), |