@ai-sdk/harness-claude-code 1.0.99 → 1.0.100
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 +10 -0
- package/dist/bridge/index.mjs +15 -7
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +5 -20
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +24 -12
- package/src/bridge/index.ts +2 -0
- package/src/claude-code-bootstrap.ts +5 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness-claude-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.100",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ai-sdk/harness": "1.0.
|
|
29
|
+
"@ai-sdk/harness": "1.0.96",
|
|
30
30
|
"@ai-sdk/provider-utils": "5.0.34",
|
|
31
31
|
"ws": "^8.21.0"
|
|
32
32
|
},
|
|
@@ -42,6 +42,7 @@ export type ClaudeMessage = {
|
|
|
42
42
|
usage?: Record<string, unknown>;
|
|
43
43
|
total_cost_usd?: number;
|
|
44
44
|
structured_output?: unknown;
|
|
45
|
+
tool_use_result?: unknown;
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
type MessageBlock = {
|
|
@@ -103,6 +104,13 @@ export function createClaudeStreamEventState(): ClaudeStreamEventState {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
const UNRECOVERABLE_API_RETRY_STATUSES = new Set([401, 403, 404]);
|
|
107
|
+
const HOST_TOOL_PREFIX = 'mcp__harness-tools__';
|
|
108
|
+
|
|
109
|
+
export function isExternalMcpTool(nativeName: string): boolean {
|
|
110
|
+
return (
|
|
111
|
+
nativeName.startsWith('mcp__') && !nativeName.startsWith(HOST_TOOL_PREFIX)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
106
114
|
|
|
107
115
|
export function createEmitStreamEvent({
|
|
108
116
|
state,
|
|
@@ -231,15 +239,14 @@ export function createEmitStreamEvent({
|
|
|
231
239
|
state.structuredOutputToolUseIds.add(block.id);
|
|
232
240
|
continue;
|
|
233
241
|
}
|
|
234
|
-
|
|
235
|
-
if (block.name.startsWith(mcpPrefix)) {
|
|
242
|
+
if (block.name.startsWith(HOST_TOOL_PREFIX)) {
|
|
236
243
|
state.pendingStepToolUseIds.add(block.id);
|
|
237
244
|
state.mcpToolUseIds.add(block.id);
|
|
238
245
|
opensStep = true;
|
|
239
246
|
continue;
|
|
240
247
|
}
|
|
241
248
|
state.nativeToolCallNames.set(block.id, block.name);
|
|
242
|
-
const dynamic = block.name
|
|
249
|
+
const dynamic = isExternalMcpTool(block.name);
|
|
243
250
|
if (dynamic) state.externalMcpToolUseIds.add(block.id);
|
|
244
251
|
if (state.approvalRequestedToolUseIds.has(block.id)) {
|
|
245
252
|
continue;
|
|
@@ -265,6 +272,12 @@ export function createEmitStreamEvent({
|
|
|
265
272
|
}
|
|
266
273
|
|
|
267
274
|
if (type === 'user' && msg.message?.content) {
|
|
275
|
+
const toolResultBlocks = msg.message.content.filter(
|
|
276
|
+
block => block.type === 'tool_result',
|
|
277
|
+
);
|
|
278
|
+
const toolUseResult =
|
|
279
|
+
toolResultBlocks.length === 1 ? msg.tool_use_result : undefined;
|
|
280
|
+
|
|
268
281
|
for (const block of msg.message.content) {
|
|
269
282
|
if (
|
|
270
283
|
block.type === 'tool_result' &&
|
|
@@ -291,17 +304,19 @@ export function createEmitStreamEvent({
|
|
|
291
304
|
* numeric exit code — the SDK exposes only stdout/stderr text and
|
|
292
305
|
* an is_error flag. Consumers (and the example UI) render bash
|
|
293
306
|
* failures from an `exitCode` field on a structured result, the
|
|
294
|
-
* shape Codex's shell tool provides natively.
|
|
295
|
-
* a binary code from is_error: 1 on
|
|
296
|
-
*
|
|
297
|
-
* status.
|
|
307
|
+
* shape Codex's shell tool provides natively. When Claude omits
|
|
308
|
+
* `tool_use_result`, derive a binary code from is_error: 1 on
|
|
309
|
+
* failure, 0 on success. This fallback is a stand-in for
|
|
310
|
+
* failed/succeeded, not the process's true exit status.
|
|
298
311
|
*/
|
|
299
|
-
const
|
|
312
|
+
const contentResult =
|
|
300
313
|
toolName === 'bash'
|
|
301
314
|
? { exitCode: isError ? 1 : 0, stdout: content }
|
|
302
315
|
: dynamic
|
|
303
316
|
? parseMcpToolResult(content)
|
|
304
317
|
: content;
|
|
318
|
+
const result =
|
|
319
|
+
toolUseResult !== undefined ? toolUseResult : contentResult;
|
|
305
320
|
emit({
|
|
306
321
|
type: 'tool-result',
|
|
307
322
|
toolCallId: block.tool_use_id,
|
|
@@ -388,8 +403,6 @@ function formatApiRetryWarning(msg: ClaudeMessage): string {
|
|
|
388
403
|
: 'Claude Code API retry';
|
|
389
404
|
}
|
|
390
405
|
|
|
391
|
-
const HOST_TOOL_PREFIX = 'mcp__harness-tools__';
|
|
392
|
-
|
|
393
406
|
function handleStreamEvent({
|
|
394
407
|
event,
|
|
395
408
|
state,
|
|
@@ -428,8 +441,7 @@ function handleStreamEvent({
|
|
|
428
441
|
const hostToolName = nativeName.startsWith(HOST_TOOL_PREFIX)
|
|
429
442
|
? nativeName.slice(HOST_TOOL_PREFIX.length)
|
|
430
443
|
: undefined;
|
|
431
|
-
const dynamic =
|
|
432
|
-
hostToolName === undefined && nativeName.startsWith('mcp__');
|
|
444
|
+
const dynamic = isExternalMcpTool(nativeName);
|
|
433
445
|
partialBlocks.set(index, { id, kind: 'tool-input' });
|
|
434
446
|
send({
|
|
435
447
|
type: 'tool-input-start',
|
package/src/bridge/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
defaultUsage,
|
|
44
44
|
emitFinishStep,
|
|
45
45
|
finishApprovalStep,
|
|
46
|
+
isExternalMcpTool,
|
|
46
47
|
mapUsage,
|
|
47
48
|
type ClaudeMessage,
|
|
48
49
|
} from './create-emit-stream-event';
|
|
@@ -201,6 +202,7 @@ function createPermissionOptions(input: {
|
|
|
201
202
|
nativeName: toolName,
|
|
202
203
|
input: JSON.stringify(toolInput ?? {}),
|
|
203
204
|
providerExecuted: true,
|
|
205
|
+
...(isExternalMcpTool(toolName) ? { dynamic: true } : {}),
|
|
204
206
|
});
|
|
205
207
|
input.emit({
|
|
206
208
|
type: 'tool-approval-request',
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
1
|
import type { HarnessV1Bootstrap } from '@ai-sdk/harness';
|
|
2
|
+
import { createReadBridgeAsset } from '@ai-sdk/harness/utils';
|
|
3
|
+
|
|
4
|
+
const readBridgeAsset = createReadBridgeAsset({
|
|
5
|
+
resolveAssetUrl: name => new URL(`./bridge/${name}`, import.meta.url),
|
|
6
|
+
});
|
|
4
7
|
|
|
5
8
|
/*
|
|
6
9
|
* Bootstrap is derived state stored under the sandbox's default working
|
|
@@ -49,25 +52,3 @@ export async function getClaudeCodeBootstrap(): Promise<HarnessV1Bootstrap> {
|
|
|
49
52
|
};
|
|
50
53
|
return cachedBootstrap;
|
|
51
54
|
}
|
|
52
|
-
|
|
53
|
-
async function readBridgeAsset(name: string): Promise<string> {
|
|
54
|
-
return readFile(
|
|
55
|
-
fileURLToPath(
|
|
56
|
-
resolveBridgeAssetUrl({
|
|
57
|
-
name,
|
|
58
|
-
moduleUrl: import.meta.url,
|
|
59
|
-
}),
|
|
60
|
-
),
|
|
61
|
-
'utf8',
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function resolveBridgeAssetUrl({
|
|
66
|
-
name,
|
|
67
|
-
moduleUrl,
|
|
68
|
-
}: {
|
|
69
|
-
name: string;
|
|
70
|
-
moduleUrl: string | URL;
|
|
71
|
-
}): URL {
|
|
72
|
-
return new URL(`./bridge/${name}`, moduleUrl);
|
|
73
|
-
}
|