@ai-sdk/harness-acp 1.0.38 → 1.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,6 +104,13 @@ let coldRestorationMethod: ACPSessionRestorationMethod | undefined;
104
104
  let activePermissionController:
105
105
  | ReturnType<typeof createACPPermissionController>
106
106
  | undefined;
107
+ let activeQuestionRequest:
108
+ | {
109
+ method: string;
110
+ turn: BridgeTurn;
111
+ emitStreamEvent: ReturnType<typeof createEmitStreamEvent>;
112
+ }
113
+ | undefined;
107
114
 
108
115
  await runBridge<StartMessage>({
109
116
  bridgeType,
@@ -252,15 +259,36 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
252
259
  const emitStreamEvent = createEmitStreamEvent({
253
260
  emit: event => turn.emit(event as BridgeEvent),
254
261
  emitToolCallCandidate: ({ toolCall }) => {
262
+ const requestId = crypto.randomUUID();
255
263
  turn.emit({
256
264
  type: 'acp-tool-call-candidate',
265
+ requestId,
257
266
  toolCall,
258
267
  });
268
+ void turn.requestToolResult(requestId).then(result => {
269
+ if (
270
+ result.output != null &&
271
+ typeof result.output === 'object' &&
272
+ 'suppress' in result.output &&
273
+ result.output.suppress === true
274
+ ) {
275
+ emitStreamEvent.suppressToolCall({
276
+ toolCallId: toolCall.toolCallId,
277
+ });
278
+ }
279
+ });
259
280
  },
260
281
  builtinTools: start.builtinTools,
261
282
  hostToolServerName: HOST_TOOL_MCP_SERVER_NAME,
262
283
  hostTools: start.tools ?? [],
263
284
  });
285
+ if (bridgeConfiguration.askUserQuestionsRequestMethod != null) {
286
+ activeQuestionRequest = {
287
+ method: bridgeConfiguration.askUserQuestionsRequestMethod,
288
+ turn,
289
+ emitStreamEvent,
290
+ };
291
+ }
264
292
  const permissionController = createACPPermissionController({
265
293
  turn,
266
294
  sessionId: activeSession.sessionId,
@@ -353,6 +381,9 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
353
381
  if (emitStreamEvent.message({ message })) return;
354
382
  }
355
383
  } finally {
384
+ if (activeQuestionRequest?.turn === turn) {
385
+ activeQuestionRequest = undefined;
386
+ }
356
387
  permissionController.cancelAll();
357
388
  activePermissionController = undefined;
358
389
  activeHostToolRelay.unbindTurn({ turn: relayTurn });
@@ -458,7 +489,7 @@ async function ensureSession({
458
489
  stream: acp.ndJsonStream(input, output),
459
490
  });
460
491
  streamCapture = capturedStream.capture;
461
- connection = acp
492
+ let client = acp
462
493
  .client({ name: clientApp.name })
463
494
  .onRequest(
464
495
  acp.methods.client.session.requestPermission,
@@ -475,8 +506,58 @@ async function ensureSession({
475
506
  notification: params,
476
507
  update: params.update,
477
508
  });
478
- })
479
- .connect(capturedStream.stream);
509
+ });
510
+ if (bridgeConfiguration.askUserQuestionsRequestMethod != null) {
511
+ const method = bridgeConfiguration.askUserQuestionsRequestMethod;
512
+ client = client.onRequest<unknown, unknown>(
513
+ method,
514
+ value => value,
515
+ async ({ params }) => {
516
+ const active = activeQuestionRequest;
517
+ if (active == null || active.method !== method) {
518
+ throw acp.RequestError.methodNotFound(method);
519
+ }
520
+ const requestId = crypto.randomUUID();
521
+ const nativeToolCallId = getNativeQuestionToolCallId({ params });
522
+ active.turn.emit({
523
+ type: 'acp-question-request',
524
+ requestId,
525
+ nativeRequest: params,
526
+ ...(nativeToolCallId == null
527
+ ? {}
528
+ : {
529
+ nativeToolCall:
530
+ active.emitStreamEvent.getToolCall({
531
+ toolCallId: nativeToolCallId,
532
+ }) ?? undefined,
533
+ }),
534
+ });
535
+ const result = await active.turn.requestToolResult(requestId);
536
+ const classification = result.output as
537
+ | { type: 'unhandled' }
538
+ | { type: 'handled'; toolCallId: string }
539
+ | undefined;
540
+ if (classification?.type !== 'handled') {
541
+ throw acp.RequestError.methodNotFound(method);
542
+ }
543
+ active.emitStreamEvent.suppressToolCall({
544
+ toolCallId: nativeToolCallId ?? classification.toolCallId,
545
+ });
546
+ try {
547
+ const nativeResult = await active.turn.requestToolResult(
548
+ classification.toolCallId,
549
+ );
550
+ return nativeResult.output;
551
+ } finally {
552
+ active.turn.emit({
553
+ type: 'acp-question-resolved',
554
+ requestId,
555
+ });
556
+ }
557
+ },
558
+ );
559
+ }
560
+ connection = client.connect(capturedStream.stream);
480
561
 
481
562
  const initializeRequest = createACPInitializeRequest({
482
563
  protocolVersion: acp.PROTOCOL_VERSION,
@@ -627,6 +708,19 @@ async function ensureSession({
627
708
  return { initialHostToolCatalogRefreshRequired: tools.length > 0 };
628
709
  }
629
710
 
711
+ function getNativeQuestionToolCallId({
712
+ params,
713
+ }: {
714
+ params: unknown;
715
+ }): string | undefined {
716
+ if (!isRecord(params)) return undefined;
717
+ if (typeof params.toolCallId === 'string') return params.toolCallId;
718
+ const nestedParams = params.params;
719
+ return isRecord(nestedParams) && typeof nestedParams.toolCallId === 'string'
720
+ ? nestedParams.toolCallId
721
+ : undefined;
722
+ }
723
+
630
724
  function createExternalMcpServers({
631
725
  mcpServers,
632
726
  initialization,
@@ -68,6 +68,7 @@ export function createACPStreamTranslator({
68
68
  output: unknown;
69
69
  isError?: boolean;
70
70
  }) => void;
71
+ getToolCall: (options: { toolCallId: string }) => ACPToolCall | undefined;
71
72
  } {
72
73
  let openBlock:
73
74
  | {
@@ -215,6 +216,7 @@ export function createACPStreamTranslator({
215
216
  builtinTools,
216
217
  builtinToolsByName,
217
218
  });
219
+ emitToolCallCandidate?.({ toolCall: createACPToolCall({ state }) });
218
220
  if (
219
221
  !forceEmit &&
220
222
  builtin?.inputSchema != null &&
@@ -238,7 +240,6 @@ export function createACPStreamTranslator({
238
240
  programmaticName,
239
241
  toolCallId: state.toolCallId,
240
242
  });
241
- emitToolCallCandidate?.({ toolCall: createACPToolCall({ state }) });
242
243
  }
243
244
  emit({
244
245
  type: 'tool-call',
@@ -381,6 +382,10 @@ export function createACPStreamTranslator({
381
382
  close: closeBlock,
382
383
  hostToolCall,
383
384
  hostToolResult,
385
+ getToolCall: ({ toolCallId }) => {
386
+ const state = toolStates.get(toolCallId);
387
+ return state == null ? undefined : createACPToolCall({ state });
388
+ },
384
389
  finish: response => {
385
390
  if (finished) return;
386
391
  finished = true;
package/src/v1/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { createACPV1 } from './acp-v1-harness';
2
2
  export type {
3
+ ACPAskUserQuestionsSettings,
3
4
  ACPCredentialBrokering,
4
5
  ACPAuthentication,
5
6
  ACPAuthenticationMode,