@ai-sdk/harness-claude-code 1.0.168 → 1.0.170

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.
@@ -1,11 +1,18 @@
1
- export function createClaudeCodeSystemPrompt(instructions?: string): {
1
+ export function createClaudeCodeSystemPrompt(
2
+ instructions?: string,
3
+ options?: { excludeDynamicSections?: boolean },
4
+ ): {
2
5
  type: 'preset';
3
6
  preset: 'claude_code';
4
7
  append?: string;
8
+ excludeDynamicSections?: boolean;
5
9
  } {
6
10
  return {
7
11
  type: 'preset',
8
12
  preset: 'claude_code',
9
13
  ...(instructions ? { append: instructions } : {}),
14
+ ...(options?.excludeDynamicSections !== undefined
15
+ ? { excludeDynamicSections: options.excludeDynamicSections }
16
+ : {}),
10
17
  };
11
18
  }
@@ -194,7 +194,14 @@ export function createEmitStreamEvent({
194
194
  // Emit `stream-start` once, on the first message, carrying the model the
195
195
  // CLI resolved to (the `system`/`init` message reports it — this is the
196
196
  // default model when none was configured).
197
- if (!streamStarted) {
197
+ /*
198
+ * `command_lifecycle` frames are skipped here. The CLI's stdin reader
199
+ * forwards them (e.g. `queued` and `started` for the prompt) straight to
200
+ * the output stream, independently of the engine that yields
201
+ * `system`/`init` at the start of each turn, so they can arrive before
202
+ * `init`. They produce no stream parts.
203
+ */
204
+ if (!streamStarted && type !== 'command_lifecycle') {
198
205
  const initModel =
199
206
  type === 'system' &&
200
207
  msg.subtype === 'init' &&
@@ -570,6 +570,12 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
570
570
  options: {
571
571
  ...(start.model ? { model: start.model } : {}),
572
572
  ...(start.maxTurns !== undefined ? { maxTurns: start.maxTurns } : {}),
573
+ ...(start.maxBudgetUsd !== undefined
574
+ ? { maxBudgetUsd: start.maxBudgetUsd }
575
+ : {}),
576
+ ...(start.strictMcpConfig !== undefined
577
+ ? { strictMcpConfig: start.strictMcpConfig }
578
+ : {}),
573
579
  ...(start.agentProgressSummaries !== undefined
574
580
  ? { agentProgressSummaries: start.agentProgressSummaries }
575
581
  : {}),
@@ -582,7 +588,9 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
582
588
  ...(inactiveNativeTools.length > 0
583
589
  ? { disallowedTools: inactiveNativeTools }
584
590
  : {}),
585
- systemPrompt: createClaudeCodeSystemPrompt(start.instructions),
591
+ systemPrompt: createClaudeCodeSystemPrompt(start.instructions, {
592
+ excludeDynamicSections: start.excludeDynamicSections,
593
+ }),
586
594
  thinking: start.thinking,
587
595
  ...(start.effort !== undefined ? { effort: start.effort } : {}),
588
596
  ...(start.responseFormat?.type === 'json' &&
@@ -603,6 +611,9 @@ async function runTurn(start: StartMessage, turn: BridgeTurn): Promise<void> {
603
611
  {
604
612
  matcher: 'AskUserQuestion',
605
613
  hooks: [questionPreToolUseHook],
614
+ ...(start.questionTimeoutMs !== undefined
615
+ ? { timeout: Math.ceil(start.questionTimeoutMs / 1000) }
616
+ : {}),
606
617
  },
607
618
  ],
608
619
  PostCompact: [
@@ -37,6 +37,10 @@ export const startMessageSchema = harnessV1BridgeStartBaseSchema.extend({
37
37
  thinking: thinkingSchema,
38
38
  effort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
39
39
  maxTurns: z.number().optional(),
40
+ maxBudgetUsd: z.number().optional(),
41
+ strictMcpConfig: z.boolean().optional(),
42
+ questionTimeoutMs: z.number().optional(),
43
+ excludeDynamicSections: z.boolean().optional(),
40
44
  agentProgressSummaries: z.boolean().optional(),
41
45
  forwardSubagentText: z.boolean().optional(),
42
46
  env: z.record(z.string(), z.string()).optional(),
@@ -46,6 +46,7 @@ import {
46
46
  waitForBridgeReady,
47
47
  writeSkills,
48
48
  type BridgeHello,
49
+ type SandboxChannelDebugEvent,
49
50
  type SandboxChannelReconnectOptions,
50
51
  } from '@ai-sdk/harness/utils';
51
52
  import {
@@ -103,6 +104,27 @@ export type ClaudeCodeHarnessSettings = {
103
104
  * back to the caller. Unset means the CLI's default.
104
105
  */
105
106
  readonly maxTurns?: number;
107
+ /**
108
+ * Maximum estimated spend in USD for each Claude query. When it is exceeded,
109
+ * the query stops and the turn ends with an error. Unset means no limit.
110
+ */
111
+ readonly maxBudgetUsd?: number;
112
+ /**
113
+ * Only use the MCP servers configured with `mcpServers`, and ignore any other
114
+ * MCP configuration such as a project `.mcp.json` or user settings.
115
+ */
116
+ readonly strictMcpConfig?: boolean;
117
+ /**
118
+ * Maximum milliseconds Claude waits for an answer to an `AskUserQuestion`
119
+ * question before the question fails. Unset means the Claude Code default.
120
+ */
121
+ readonly questionTimeoutMs?: number;
122
+ /**
123
+ * Moves per-session sections (working directory, memory, git status) out of
124
+ * the Claude Code system prompt and into the first user message, so the
125
+ * system prompt can be cached across sessions.
126
+ */
127
+ readonly excludeDynamicSections?: boolean;
106
128
  /**
107
129
  * Enables periodic AI-generated progress summaries for running subagents.
108
130
  * The summaries are forwarded in raw `task_progress` stream parts.
@@ -149,6 +171,14 @@ export type ClaudeCodeHarnessSettings = {
149
171
  * milliseconds up to 2 seconds.
150
172
  */
151
173
  readonly reconnect?: SandboxChannelReconnectOptions;
174
+ /**
175
+ * Called with bridge connection lifecycle events: reconnect attempts,
176
+ * successful reconnects, and `reconnect-failed` once the connection is
177
+ * closed for good. Hosts that cache idle sessions can use it to reattach
178
+ * before sending the next prompt. The `cause` of a failed reconnect can
179
+ * contain connection details, so avoid logging it as is.
180
+ */
181
+ readonly onDebug?: (event: SandboxChannelDebugEvent) => void;
152
182
  /**
153
183
  * Creates the authentication token used by the sandbox bridge. Defaults to
154
184
  * a random 32-byte hexadecimal token.
@@ -1028,6 +1058,7 @@ export function createClaudeCode(
1028
1058
  onDiagnostic,
1029
1059
  onBridgeError,
1030
1060
  reconnect: settings.reconnect,
1061
+ onDebug: settings.onDebug,
1031
1062
  });
1032
1063
  const finishListenerAttachment = isContinue
1033
1064
  ? attachChannel.beginListenerAttachment()
@@ -1048,6 +1079,10 @@ export function createClaudeCode(
1048
1079
  // sandbox is left running, stopped, or destroyed.
1049
1080
  proc: undefined,
1050
1081
  maxTurns: settings.maxTurns,
1082
+ maxBudgetUsd: settings.maxBudgetUsd,
1083
+ strictMcpConfig: settings.strictMcpConfig,
1084
+ questionTimeoutMs: settings.questionTimeoutMs,
1085
+ excludeDynamicSections: settings.excludeDynamicSections,
1051
1086
  agentProgressSummaries: settings.agentProgressSummaries,
1052
1087
  forwardSubagentText: settings.forwardSubagentText,
1053
1088
  env: sandboxClaudeEnvironment,
@@ -1191,6 +1226,7 @@ export function createClaudeCode(
1191
1226
  onDiagnostic,
1192
1227
  onBridgeError,
1193
1228
  reconnect: settings.reconnect,
1229
+ onDebug: settings.onDebug,
1194
1230
  // In replay mode the respawned bridge reloaded the finished turn from
1195
1231
  // disk; seed the cursor and resume so it streams the tail (incl.
1196
1232
  // `finish`) rather than starting empty.
@@ -1217,6 +1253,10 @@ export function createClaudeCode(
1217
1253
  finishListenerAttachment,
1218
1254
  proc,
1219
1255
  maxTurns: settings.maxTurns,
1256
+ maxBudgetUsd: settings.maxBudgetUsd,
1257
+ strictMcpConfig: settings.strictMcpConfig,
1258
+ questionTimeoutMs: settings.questionTimeoutMs,
1259
+ excludeDynamicSections: settings.excludeDynamicSections,
1220
1260
  agentProgressSummaries: settings.agentProgressSummaries,
1221
1261
  forwardSubagentText: settings.forwardSubagentText,
1222
1262
  env: sandboxClaudeEnvironment,
@@ -1255,6 +1295,10 @@ function createSession({
1255
1295
  finishListenerAttachment,
1256
1296
  proc,
1257
1297
  maxTurns,
1298
+ maxBudgetUsd,
1299
+ strictMcpConfig,
1300
+ questionTimeoutMs,
1301
+ excludeDynamicSections,
1258
1302
  agentProgressSummaries,
1259
1303
  forwardSubagentText,
1260
1304
  env,
@@ -1282,6 +1326,10 @@ function createSession({
1282
1326
  /** Undefined on `attach` — the live bridge was spawned by another process. */
1283
1327
  proc: Experimental_SandboxProcess | undefined;
1284
1328
  maxTurns: number | undefined;
1329
+ maxBudgetUsd: number | undefined;
1330
+ strictMcpConfig: boolean | undefined;
1331
+ questionTimeoutMs: number | undefined;
1332
+ excludeDynamicSections: boolean | undefined;
1285
1333
  agentProgressSummaries: boolean | undefined;
1286
1334
  forwardSubagentText: boolean | undefined;
1287
1335
  env: Readonly<Record<string, string>> | undefined;
@@ -1559,6 +1607,12 @@ function createSession({
1559
1607
  : {}),
1560
1608
  model: promptOpts.model,
1561
1609
  maxTurns,
1610
+ ...(maxBudgetUsd !== undefined ? { maxBudgetUsd } : {}),
1611
+ ...(strictMcpConfig !== undefined ? { strictMcpConfig } : {}),
1612
+ ...(questionTimeoutMs !== undefined ? { questionTimeoutMs } : {}),
1613
+ ...(excludeDynamicSections !== undefined
1614
+ ? { excludeDynamicSections }
1615
+ : {}),
1562
1616
  ...(agentProgressSummaries !== undefined
1563
1617
  ? { agentProgressSummaries }
1564
1618
  : {}),
@@ -1630,6 +1684,12 @@ function createSession({
1630
1684
  : {}),
1631
1685
  model: continueOpts.model,
1632
1686
  maxTurns,
1687
+ ...(maxBudgetUsd !== undefined ? { maxBudgetUsd } : {}),
1688
+ ...(strictMcpConfig !== undefined ? { strictMcpConfig } : {}),
1689
+ ...(questionTimeoutMs !== undefined ? { questionTimeoutMs } : {}),
1690
+ ...(excludeDynamicSections !== undefined
1691
+ ? { excludeDynamicSections }
1692
+ : {}),
1633
1693
  ...(agentProgressSummaries !== undefined
1634
1694
  ? { agentProgressSummaries }
1635
1695
  : {}),