@ai-sdk/harness-claude-code 1.0.103 → 1.0.104

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.103",
3
+ "version": "1.0.104",
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.99",
29
+ "@ai-sdk/harness": "1.0.100",
30
30
  "@ai-sdk/provider-utils": "5.0.36",
31
31
  "ws": "^8.21.0"
32
32
  },
@@ -298,25 +298,15 @@ export function createEmitStreamEvent({
298
298
  const toolName = toCommonName(nativeName);
299
299
  const dynamic = state.externalMcpToolUseIds.delete(block.tool_use_id);
300
300
  const isError = !!block.is_error;
301
- const content = stringifyContent(block.content);
302
- /*
303
- * Claude Code's Bash tool does not report the command's real
304
- * numeric exit code — the SDK exposes only stdout/stderr text and
305
- * an is_error flag. Consumers (and the example UI) render bash
306
- * failures from an `exitCode` field on a structured result, the
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.
311
- */
312
- const contentResult =
313
- toolName === 'bash'
314
- ? { exitCode: isError ? 1 : 0, stdout: content }
315
- : dynamic
316
- ? parseMcpToolResult(content)
317
- : content;
318
301
  const result =
319
- toolUseResult !== undefined ? toolUseResult : contentResult;
302
+ toolUseResult !== undefined
303
+ ? toolUseResult
304
+ : resolveToolResult({
305
+ toolName,
306
+ dynamic,
307
+ isError,
308
+ rawContent: block.content,
309
+ });
320
310
  emit({
321
311
  type: 'tool-result',
322
312
  toolCallId: block.tool_use_id,
@@ -501,23 +491,61 @@ function handleStreamEvent({
501
491
  }
502
492
  }
503
493
 
494
+ function isTextEntry(entry: unknown): entry is { text?: unknown } {
495
+ return entry != null && typeof entry === 'object' && 'text' in entry;
496
+ }
497
+
504
498
  function stringifyContent(content: unknown): string {
505
499
  if (typeof content === 'string') return content;
506
500
  if (Array.isArray(content)) {
507
501
  return content
508
502
  .map(entry =>
509
- entry && typeof entry === 'object' && 'text' in entry
510
- ? String((entry as { text?: unknown }).text ?? '')
511
- : JSON.stringify(entry),
503
+ isTextEntry(entry) ? String(entry.text ?? '') : JSON.stringify(entry),
512
504
  )
513
505
  .join('');
514
506
  }
515
507
  return JSON.stringify(content);
516
508
  }
517
509
 
510
+ function hasNonTextContent(content: unknown): boolean {
511
+ return Array.isArray(content) && content.some(entry => !isTextEntry(entry));
512
+ }
513
+
514
+ function resolveToolResult({
515
+ toolName,
516
+ dynamic,
517
+ isError,
518
+ rawContent,
519
+ }: {
520
+ toolName: string;
521
+ dynamic: boolean;
522
+ isError: boolean;
523
+ rawContent: unknown;
524
+ }): unknown {
525
+ /*
526
+ * Claude Code's Bash tool does not report the command's real numeric exit
527
+ * code — the SDK exposes only stdout/stderr text and an is_error flag.
528
+ * Consumers (and the example UI) render bash failures from an `exitCode`
529
+ * field on a structured result, the shape Codex's shell tool provides
530
+ * natively. When Claude omits `tool_use_result`, derive a binary code from
531
+ * is_error: 1 on failure, 0 on success. This fallback is a stand-in for
532
+ * failed/succeeded, not the process's true exit status.
533
+ */
534
+ if (toolName === 'bash') {
535
+ return { exitCode: isError ? 1 : 0, stdout: stringifyContent(rawContent) };
536
+ }
537
+ // Must precede the MCP branch: flattening a non-text block to base64 text
538
+ // is not recoverable by parsing it back.
539
+ if (hasNonTextContent(rawContent)) return rawContent;
540
+ const content = stringifyContent(rawContent);
541
+ return dynamic ? parseMcpToolResult(content) : content;
542
+ }
543
+
518
544
  function parseMcpToolResult(content: string): unknown {
519
545
  try {
520
- return JSON.parse(content);
546
+ const parsed = JSON.parse(content);
547
+ // `JSON.parse` also succeeds on scalars; keep those as the string sent.
548
+ return parsed !== null && typeof parsed === 'object' ? parsed : content;
521
549
  } catch {
522
550
  return content;
523
551
  }
@@ -921,13 +921,18 @@ export function createClaudeCode(
921
921
  );
922
922
  }
923
923
  } else {
924
- warnCredentialBrokeringUnavailable();
925
924
  sandboxClaudeEnvironment = await applyCredentialForwarding({
926
925
  environment: sandboxClaudeEnvironment,
927
926
  credentialEnvironmentVariables:
928
927
  CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
929
928
  credentialForwarding: settings.credentialForwarding,
930
929
  });
930
+ warnCredentialBrokeringUnavailable({
931
+ environment: claudeEnvironment,
932
+ forwardedEnvironment: sandboxClaudeEnvironment,
933
+ credentialEnvironmentVariables:
934
+ CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
935
+ });
931
936
  }
932
937
  const bootstrapDir = posix.resolve(
933
938
  defaultWorkingDirectory,