@ai-sdk/harness-claude-code 1.0.154 → 1.0.155
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.
- package/CHANGELOG.md +11 -0
- package/dist/bridge/index.mjs +246 -54
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +189 -37
- package/src/bridge/index.ts +109 -41
package/src/bridge/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
runBridge,
|
|
9
|
+
convertHarnessToolModelOutput,
|
|
9
10
|
type BridgeEvent,
|
|
10
11
|
type BridgeTurn,
|
|
11
12
|
type Experimental_BridgeUserMessage,
|
|
@@ -46,7 +47,9 @@ import {
|
|
|
46
47
|
createEmitStreamEvent,
|
|
47
48
|
defaultUsage,
|
|
48
49
|
emitFinishStep,
|
|
49
|
-
|
|
50
|
+
markWaitingOnHostInput,
|
|
51
|
+
clearWaitingOnHostInput,
|
|
52
|
+
resolveMcpHandlerToolUseId,
|
|
50
53
|
isExternalMcpTool,
|
|
51
54
|
mapUsage,
|
|
52
55
|
type ClaudeMessage,
|
|
@@ -164,9 +167,11 @@ function createPermissionOptions(input: {
|
|
|
164
167
|
inactiveNativeTools: readonly string[];
|
|
165
168
|
turn: BridgeTurn;
|
|
166
169
|
emit: Emit;
|
|
167
|
-
|
|
170
|
+
markWaitingOnHostInput: (toolUseId: string) => void;
|
|
171
|
+
clearWaitingOnHostInput: (toolUseId: string) => void;
|
|
168
172
|
nativeToolCallNames: Map<string, string>;
|
|
169
173
|
approvalRequestedToolUseIds: Set<string>;
|
|
174
|
+
emittedToolCallIds: Set<string>;
|
|
170
175
|
}): Record<string, unknown> {
|
|
171
176
|
const permissionMode = input.start.permissionMode ?? 'allow-all';
|
|
172
177
|
const inactiveNativeTools = new Set(input.inactiveNativeTools);
|
|
@@ -222,23 +227,29 @@ function createPermissionOptions(input: {
|
|
|
222
227
|
const approvalId = options.toolUseID;
|
|
223
228
|
input.approvalRequestedToolUseIds.add(approvalId);
|
|
224
229
|
input.nativeToolCallNames.set(approvalId, toolName);
|
|
225
|
-
input.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
if (!input.emittedToolCallIds.has(approvalId)) {
|
|
231
|
+
input.emittedToolCallIds.add(approvalId);
|
|
232
|
+
input.emit({
|
|
233
|
+
type: 'tool-call',
|
|
234
|
+
toolCallId: approvalId,
|
|
235
|
+
toolName: toCommonName(toolName),
|
|
236
|
+
nativeName: toolName,
|
|
237
|
+
input: JSON.stringify(toolInput ?? {}),
|
|
238
|
+
providerExecuted: true,
|
|
239
|
+
...(isExternalMcpTool(toolName) ? { dynamic: true } : {}),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
const decisionPromise = input.turn.requestToolApproval(approvalId);
|
|
234
243
|
input.emit({
|
|
235
244
|
type: 'tool-approval-request',
|
|
236
245
|
approvalId,
|
|
237
246
|
toolCallId: approvalId,
|
|
238
247
|
});
|
|
239
|
-
input.
|
|
248
|
+
input.markWaitingOnHostInput(approvalId);
|
|
240
249
|
|
|
241
|
-
const decision = await
|
|
250
|
+
const decision = await decisionPromise.finally(() =>
|
|
251
|
+
input.clearWaitingOnHostInput(approvalId),
|
|
252
|
+
);
|
|
242
253
|
return decision.approved
|
|
243
254
|
? { behavior: 'allow', updatedInput: toolInput, toolUseID: approvalId }
|
|
244
255
|
: {
|
|
@@ -254,6 +265,8 @@ function createQuestionPreToolUseHook(input: {
|
|
|
254
265
|
turn: BridgeTurn;
|
|
255
266
|
emit: Emit;
|
|
256
267
|
nativeToolCallNames: Map<string, string>;
|
|
268
|
+
markWaitingOnHostInput: (toolUseId: string) => void;
|
|
269
|
+
clearWaitingOnHostInput: (toolUseId: string) => void;
|
|
257
270
|
}): HookCallback {
|
|
258
271
|
return async (hookInput, toolUseID): Promise<HookJSONOutput> => {
|
|
259
272
|
if (
|
|
@@ -284,20 +297,23 @@ function createQuestionPreToolUseHook(input: {
|
|
|
284
297
|
});
|
|
285
298
|
|
|
286
299
|
const questionKey = claudeCodeQuestionKey(nativeInput);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
300
|
+
input.markWaitingOnHostInput(toolCallId);
|
|
301
|
+
const result = await input.turn
|
|
302
|
+
.requestToolResult({
|
|
303
|
+
toolCallId,
|
|
304
|
+
matches: candidate => {
|
|
305
|
+
const nativeRequest = candidate.toolResult?.providerOptions?.[
|
|
306
|
+
'claude-code'
|
|
307
|
+
]?.nativeRequest as
|
|
308
|
+
| Parameters<typeof claudeCodeQuestionKey>[0]
|
|
309
|
+
| undefined;
|
|
310
|
+
return (
|
|
311
|
+
nativeRequest != null &&
|
|
312
|
+
claudeCodeQuestionKey(nativeRequest) === questionKey
|
|
313
|
+
);
|
|
314
|
+
},
|
|
315
|
+
})
|
|
316
|
+
.finally(() => input.clearWaitingOnHostInput(toolCallId));
|
|
301
317
|
const nativeResult = toClaudeCodeQuestionResult({
|
|
302
318
|
nativeInput,
|
|
303
319
|
output: result.output as Parameters<
|
|
@@ -413,21 +429,50 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
413
429
|
const [input, extra] = handlerArgs;
|
|
414
430
|
const metadataToolCallId = extra._meta?.['claudecode/toolUseId'];
|
|
415
431
|
const toolCallId =
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
:
|
|
432
|
+
resolveMcpHandlerToolUseId({
|
|
433
|
+
state: streamEventState,
|
|
434
|
+
metadataToolUseId:
|
|
435
|
+
typeof metadataToolCallId === 'string'
|
|
436
|
+
? metadataToolCallId
|
|
437
|
+
: undefined,
|
|
438
|
+
toolName: tool.name,
|
|
439
|
+
input,
|
|
440
|
+
}) ?? randomUUID();
|
|
419
441
|
streamEventState.mcpHandlerCalls.set(toolCallId, {
|
|
420
442
|
toolName: tool.name,
|
|
421
443
|
input,
|
|
422
444
|
});
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
445
|
+
if (!streamEventState.emittedToolCallIds.has(toolCallId)) {
|
|
446
|
+
streamEventState.emittedToolCallIds.add(toolCallId);
|
|
447
|
+
emit({
|
|
448
|
+
type: 'tool-call',
|
|
449
|
+
toolCallId,
|
|
450
|
+
toolName: tool.name,
|
|
451
|
+
input: JSON.stringify(input),
|
|
452
|
+
providerExecuted: false,
|
|
453
|
+
});
|
|
454
|
+
markWaitingOnHostInput({
|
|
455
|
+
state: streamEventState,
|
|
456
|
+
emit,
|
|
457
|
+
toolUseId: toolCallId,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
const { output, isError, toolResult } = await turn
|
|
461
|
+
.requestToolResult(toolCallId)
|
|
462
|
+
.finally(() => {
|
|
463
|
+
clearWaitingOnHostInput({
|
|
464
|
+
state: streamEventState,
|
|
465
|
+
toolUseId: toolCallId,
|
|
466
|
+
});
|
|
467
|
+
/*
|
|
468
|
+
* A handler call that could not be matched to a reported
|
|
469
|
+
* `tool_use` block never receives a native tool result, so it
|
|
470
|
+
* must not keep the step open once the host has answered.
|
|
471
|
+
*/
|
|
472
|
+
if (!streamEventState.mcpToolCalls.has(toolCallId)) {
|
|
473
|
+
streamEventState.pendingStepToolUseIds.delete(toolCallId);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
431
476
|
emit({
|
|
432
477
|
type: 'tool-result',
|
|
433
478
|
toolCallId,
|
|
@@ -435,6 +480,23 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
435
480
|
result: output ?? null,
|
|
436
481
|
isError: !!isError,
|
|
437
482
|
});
|
|
483
|
+
if (toolResult != null) {
|
|
484
|
+
const converted = convertHarnessToolModelOutput({
|
|
485
|
+
output: toolResult.output,
|
|
486
|
+
});
|
|
487
|
+
return {
|
|
488
|
+
content: converted.content.map(part =>
|
|
489
|
+
part.type === 'text'
|
|
490
|
+
? part
|
|
491
|
+
: {
|
|
492
|
+
type: 'image' as const,
|
|
493
|
+
data: part.data,
|
|
494
|
+
mimeType: part.mediaType,
|
|
495
|
+
},
|
|
496
|
+
),
|
|
497
|
+
isError: isError === true || converted.isError,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
438
500
|
return {
|
|
439
501
|
content: [{ type: 'text', text: JSON.stringify(output ?? null) }],
|
|
440
502
|
isError,
|
|
@@ -485,16 +547,22 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
|
|
|
485
547
|
inactiveNativeTools,
|
|
486
548
|
turn,
|
|
487
549
|
emit,
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
550
|
+
markWaitingOnHostInput: toolUseId =>
|
|
551
|
+
markWaitingOnHostInput({ state: streamEventState, emit, toolUseId }),
|
|
552
|
+
clearWaitingOnHostInput: toolUseId =>
|
|
553
|
+
clearWaitingOnHostInput({ state: streamEventState, toolUseId }),
|
|
491
554
|
nativeToolCallNames: streamEventState.nativeToolCallNames,
|
|
492
555
|
approvalRequestedToolUseIds: streamEventState.approvalRequestedToolUseIds,
|
|
556
|
+
emittedToolCallIds: streamEventState.emittedToolCallIds,
|
|
493
557
|
});
|
|
494
558
|
const questionPreToolUseHook = createQuestionPreToolUseHook({
|
|
495
559
|
turn,
|
|
496
560
|
emit,
|
|
497
561
|
nativeToolCallNames: streamEventState.nativeToolCallNames,
|
|
562
|
+
markWaitingOnHostInput: toolUseId =>
|
|
563
|
+
markWaitingOnHostInput({ state: streamEventState, emit, toolUseId }),
|
|
564
|
+
clearWaitingOnHostInput: toolUseId =>
|
|
565
|
+
clearWaitingOnHostInput({ state: streamEventState, toolUseId }),
|
|
498
566
|
});
|
|
499
567
|
|
|
500
568
|
const q = claudeSdk.query({
|