@ai-sdk/harness-claude-code 1.0.99 → 1.0.101

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness-claude-code",
3
- "version": "1.0.99",
3
+ "version": "1.0.101",
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.95",
29
+ "@ai-sdk/harness": "1.0.97",
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
- const mcpPrefix = 'mcp__harness-tools__';
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.startsWith('mcp__');
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. To match it, derive
295
- * a binary code from is_error: 1 on failure, 0 on success. This is
296
- * a stand-in for failed/succeeded, not the process's true exit
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 result =
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',
@@ -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
- }