authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-09-04 06:23:12-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 06:23:12-07:00
log560afffdea82000d757c98ea79678aee75f8648c
tree9872fb6eca619670988401c2d4e856414186ca31
parentcaa8a0db98f9d32e98a1645caa7f7dd37b14f187
signature Signed by PGP key B5690EEEBB952194

fix(server): update Claude Agent SDK to 0.3.260 (#9135)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

5 files changed, 357 insertions(+), 19 deletions(-)

apps/server/package.json+1-1
......@@ -22,7 +22,7 @@
2222 "test": "vp test run"
2323 },
2424 "dependencies": {
25 "@anthropic-ai/claude-agent-sdk": "^0.3.170",
25 "@anthropic-ai/claude-agent-sdk": "^0.3.260",
2626 "@effect/platform-bun": "catalog:",
2727 "@effect/platform-node": "catalog:",
2828 "@effect/sql-sqlite-bun": "catalog:",
apps/server/src/provider/Layers/ClaudeAdapter.test.ts+244-4
......@@ -2090,6 +2090,177 @@ describe("ClaudeAdapterLive", () => {
20902090 );
20912091 });
20922092
2093 it.effect("fails a turn when the result carries a give-up terminal_reason", () => {
2094 const harness = makeHarness();
2095 return Effect.gen(function* () {
2096 const adapter = yield* ClaudeAdapter;
2097
2098 const runtimeEventsFiber = yield* Stream.take(adapter.streamEvents, 7).pipe(
2099 Stream.runCollect,
2100 Effect.forkChild,
2101 );
2102
2103 const session = yield* adapter.startSession({
2104 threadId: THREAD_ID,
2105 provider: ProviderDriverKind.make("claudeAgent"),
2106 runtimeMode: "full-access",
2107 });
2108
2109 const turn = yield* adapter.sendTurn({
2110 threadId: session.threadId,
2111 input: "hello",
2112 attachments: [],
2113 });
2114
2115 // The CLI stamps subtype success with an empty error list when it
2116 // gives up after exhausting API retries; the terminal_reason is the
2117 // only structured failure signal.
2118 harness.query.emit({
2119 type: "result",
2120 subtype: "success",
2121 is_error: false,
2122 result: "",
2123 errors: [],
2124 stop_reason: null,
2125 terminal_reason: "api_error",
2126 session_id: "sdk-session-api-error",
2127 uuid: "result-api-error",
2128 } as unknown as SDKMessage);
2129
2130 const runtimeEvents = Array.from(yield* Fiber.join(runtimeEventsFiber));
2131 assert.deepEqual(
2132 runtimeEvents.map((event) => event.type),
2133 [
2134 "session.started",
2135 "session.configured",
2136 "session.state.changed",
2137 "turn.started",
2138 "thread.started",
2139 "runtime.error",
2140 "turn.completed",
2141 ],
2142 );
2143
2144 const turnCompleted = runtimeEvents[runtimeEvents.length - 1];
2145 assert.equal(turnCompleted?.type, "turn.completed");
2146 if (turnCompleted?.type === "turn.completed") {
2147 assert.equal(String(turnCompleted.turnId), String(turn.turnId));
2148 assert.equal(turnCompleted.payload.state, "failed");
2149 assert.equal(
2150 turnCompleted.payload.errorMessage,
2151 "Claude gave up after repeated API errors.",
2152 );
2153 }
2154 }).pipe(
2155 Effect.provideService(Random.Random, makeDeterministicRandomService()),
2156 Effect.provide(harness.layer),
2157 );
2158 });
2159
2160 it.effect("fails a turn for every dead-turn terminal_reason", () => {
2161 const reasons = [
2162 "blocking_limit",
2163 "rapid_refill_breaker",
2164 "prompt_too_long",
2165 "image_error",
2166 "model_error",
2167 "malformed_tool_use_exhausted",
2168 "budget_exhausted",
2169 "structured_output_retry_exhausted",
2170 "tool_deferred_unavailable",
2171 "turn_setup_failed",
2172 ];
2173 // One harness per reason: the fake query settles a single turn.
2174 const runDeadTurn = (reason: string) => {
2175 const harness = makeHarness();
2176 return Effect.gen(function* () {
2177 const adapter = yield* ClaudeAdapter;
2178 const completionFiber = yield* adapter.streamEvents.pipe(
2179 Stream.filter((event) => event.type === "turn.completed"),
2180 Stream.runHead,
2181 Effect.forkChild,
2182 );
2183 const session = yield* adapter.startSession({
2184 threadId: THREAD_ID,
2185 provider: ProviderDriverKind.make("claudeAgent"),
2186 runtimeMode: "full-access",
2187 });
2188 yield* adapter.sendTurn({ threadId: session.threadId, input: "hello", attachments: [] });
2189 harness.query.emit({
2190 type: "result",
2191 subtype: "success",
2192 is_error: false,
2193 result: "",
2194 errors: [],
2195 stop_reason: null,
2196 terminal_reason: reason,
2197 session_id: "sdk-session-dead-turn",
2198 uuid: `result-${reason}`,
2199 } as unknown as SDKMessage);
2200 const completed = yield* Fiber.join(completionFiber);
2201 assert.equal(completed._tag, "Some");
2202 if (completed._tag === "Some" && completed.value.type === "turn.completed") {
2203 assert.equal(completed.value.payload.state, "failed", reason);
2204 assert.ok(completed.value.payload.errorMessage, `${reason} carries an error message`);
2205 }
2206 }).pipe(
2207 Effect.provideService(Random.Random, makeDeterministicRandomService()),
2208 Effect.provide(harness.layer),
2209 );
2210 };
2211 return Effect.forEach(reasons, runDeadTurn, { discard: true });
2212 });
2213
2214 it.effect("fails a turn when a success result reports a 529 overload", () => {
2215 const harness = makeHarness();
2216 return Effect.gen(function* () {
2217 const adapter = yield* ClaudeAdapter;
2218
2219 const runtimeEventsFiber = yield* Stream.take(adapter.streamEvents, 7).pipe(
2220 Stream.runCollect,
2221 Effect.forkChild,
2222 );
2223
2224 const session = yield* adapter.startSession({
2225 threadId: THREAD_ID,
2226 provider: ProviderDriverKind.make("claudeAgent"),
2227 runtimeMode: "full-access",
2228 });
2229
2230 yield* adapter.sendTurn({
2231 threadId: session.threadId,
2232 input: "hello",
2233 attachments: [],
2234 });
2235
2236 harness.query.emit({
2237 type: "result",
2238 subtype: "success",
2239 is_error: true,
2240 api_error_status: 529,
2241 result: "",
2242 errors: [],
2243 stop_reason: null,
2244 session_id: "sdk-session-overload",
2245 uuid: "result-overload",
2246 } as unknown as SDKMessage);
2247
2248 const runtimeEvents = Array.from(yield* Fiber.join(runtimeEventsFiber));
2249 const turnCompleted = runtimeEvents[runtimeEvents.length - 1];
2250 assert.equal(turnCompleted?.type, "turn.completed");
2251 if (turnCompleted?.type === "turn.completed") {
2252 assert.equal(turnCompleted.payload.state, "failed");
2253 assert.equal(
2254 turnCompleted.payload.errorMessage,
2255 "Claude API is overloaded (529). Try again shortly.",
2256 );
2257 }
2258 }).pipe(
2259 Effect.provideService(Random.Random, makeDeterministicRandomService()),
2260 Effect.provide(harness.layer),
2261 );
2262 });
2263
20932264 it.effect("interruptTurn settles live tasks and closes the provider session", () => {
20942265 const harness = makeHarness();
20952266 return Effect.gen(function* () {
......@@ -3088,7 +3259,36 @@ describe("ClaudeAdapterLive", () => {
30883259 { type: "system", subtype: "plugin_install", session_id: "session", uuid: "pi" },
30893260 { type: "system", subtype: "memory_recall", session_id: "session", uuid: "mr" },
30903261 { type: "system", subtype: "elicitation_complete", session_id: "session", uuid: "ec" },
3262 {
3263 type: "system",
3264 subtype: "control_request_progress",
3265 request_id: "ctrl-1",
3266 status: "started",
3267 session_id: "session",
3268 uuid: "crp",
3269 },
3270 {
3271 type: "system",
3272 subtype: "worker_shutting_down",
3273 reason: "host_exit",
3274 session_id: "session",
3275 uuid: "wsd",
3276 },
3277 {
3278 type: "system",
3279 subtype: "informational",
3280 content: "Loaded 3 skills",
3281 level: "notice",
3282 session_id: "session",
3283 uuid: "info",
3284 },
30913285 { type: "prompt_suggestion", suggestion: "try this", session_id: "session", uuid: "ps" },
3286 {
3287 type: "conversation_reset",
3288 new_conversation_id: "conv-2",
3289 session_id: "session",
3290 uuid: "cr",
3291 },
30923292 {
30933293 type: "system",
30943294 subtype: "notification",
......@@ -3111,6 +3311,27 @@ describe("ClaudeAdapterLive", () => {
31113311 session_id: "session",
31123312 uuid: "notif-high",
31133313 } as unknown as SDKMessage);
3314 // Warning-level informational notes and refusals without a fallback
3315 // model surface as warning rows too.
3316 harness.query.emit({
3317 type: "system",
3318 subtype: "informational",
3319 content: "Stop hook prevented continuation",
3320 level: "warning",
3321 prevent_continuation: true,
3322 session_id: "session",
3323 uuid: "info-warn",
3324 } as unknown as SDKMessage);
3325 harness.query.emit({
3326 type: "system",
3327 subtype: "model_refusal_no_fallback",
3328 original_model: "claude-opus-5",
3329 request_id: null,
3330 api_refusal_explanation: "The request was declined by the API.",
3331 content: "Model refused",
3332 session_id: "session",
3333 uuid: "mrnf",
3334 } as unknown as SDKMessage);
31143335 // session_state_changed maps to the matching session states.
31153336 for (const [state, uuid] of [
31163337 ["running", "ssc-run"],
......@@ -3141,10 +3362,15 @@ describe("ClaudeAdapterLive", () => {
31413362 yield* Effect.yieldNow;
31423363
31433364 const warnings = runtimeEvents.filter((event) => event.type === "runtime.warning");
3144 // Exactly one warning: the high-priority notification. Nothing else.
3365 // Exactly three warnings: the high-priority notification, the
3366 // warning-level informational note, and the refusal. Nothing else.
31453367 assert.deepEqual(
31463368 warnings.map((event) => event.payload.message),
3147 ["context window nearly full"],
3369 [
3370 "context window nearly full",
3371 "Stop hook prevented continuation",
3372 "The request was declined by the API.",
3373 ],
31483374 );
31493375 const sessionStates = runtimeEvents
31503376 .filter((event) => event.type === "session.state.changed")
......@@ -4190,6 +4416,7 @@ describe("ClaudeAdapterLive", () => {
41904416 { command: "pwd" },
41914417 {
41924418 signal: new AbortController().signal,
4419 requestId: "request-1",
41934420 suggestions: [
41944421 {
41954422 type: "setMode",
......@@ -4301,6 +4528,7 @@ describe("ClaudeAdapterLive", () => {
43014528 { title: "hello" },
43024529 {
43034530 signal: new AbortController().signal,
4531 requestId: "request-2",
43044532 suggestions: [],
43054533 toolUseID: "tool-use-mcp-1",
43064534 },
......@@ -4327,6 +4555,7 @@ describe("ClaudeAdapterLive", () => {
43274555 { command: "git status" },
43284556 {
43294557 signal: new AbortController().signal,
4558 requestId: "request-3",
43304559 suggestions: [
43314560 {
43324561 type: "addRules",
......@@ -4385,6 +4614,7 @@ describe("ClaudeAdapterLive", () => {
43854614 {},
43864615 {
43874616 signal: new AbortController().signal,
4617 requestId: "request-4",
43884618 toolUseID: "tool-agent-1",
43894619 },
43904620 );
......@@ -4409,6 +4639,7 @@ describe("ClaudeAdapterLive", () => {
44094639 { pattern: "foo", path: "src" },
44104640 {
44114641 signal: new AbortController().signal,
4642 requestId: "request-5",
44124643 toolUseID: "tool-grep-approval-1",
44134644 },
44144645 );
......@@ -4951,6 +5182,7 @@ describe("ClaudeAdapterLive", () => {
49515182 },
49525183 {
49535184 signal: new AbortController().signal,
5185 requestId: "request-6",
49545186 toolUseID: "tool-exit-1",
49555187 },
49565188 );
......@@ -5073,7 +5305,7 @@ describe("ClaudeAdapterLive", () => {
50735305 dialogKind: "resume_return",
50745306 payload: { sessionAgeMinutes: 145, estimatedTokens: 275123 },
50755307 },
5076 { signal: new AbortController().signal },
5308 { signal: new AbortController().signal, requestId: "request-dialog" },
50775309 );
50785310
50795311 const requested = yield* Stream.runHead(adapter.streamEvents);
......@@ -5173,6 +5405,7 @@ describe("ClaudeAdapterLive", () => {
51735405
51745406 const permissionPromise = canUseTool("AskUserQuestion", askInput, {
51755407 signal: new AbortController().signal,
5408 requestId: "request-7",
51765409 toolUseID: "tool-ask-1",
51775410 });
51785411
......@@ -5299,6 +5532,7 @@ describe("ClaudeAdapterLive", () => {
52995532
53005533 const permissionPromise = canUseTool("AskUserQuestion", askInput, {
53015534 signal: new AbortController().signal,
5535 requestId: "request-8",
53025536 toolUseID: "tool-ask-2",
53035537 });
53045538
......@@ -5364,6 +5598,7 @@ describe("ClaudeAdapterLive", () => {
53645598 },
53655599 {
53665600 signal: controller.signal,
5601 requestId: "request-9",
53675602 toolUseID: "tool-ask-abort",
53685603 },
53695604 );
......@@ -5439,6 +5674,7 @@ describe("ClaudeAdapterLive", () => {
54395674 },
54405675 {
54415676 signal: controller.signal,
5677 requestId: "request-10",
54425678 toolUseID: "tool-ask-pre-aborted",
54435679 },
54445680 );
......@@ -5495,7 +5731,11 @@ describe("ClaudeAdapterLive", () => {
54955731 },
54965732 ],
54975733 },
5498 { signal: new AbortController().signal, toolUseID: "tool-ask-stop" },
5734 {
5735 signal: new AbortController().signal,
5736 requestId: "request-stop",
5737 toolUseID: "tool-ask-stop",
5738 },
54995739 );
55005740
55015741 const requestedEvent = yield* Stream.runHead(adapter.streamEvents);
apps/server/src/provider/Layers/ClaudeAdapter.ts+106-9
......@@ -434,10 +434,45 @@ function resultErrorsText(result: SDKResultMessage): string {
434434 * so they must never become the error banner.
435435 */
436436function resultUserFacingError(result: SDKResultMessage): string | undefined {
437 if (result.subtype === "success" || !Array.isArray(result.errors)) {
438 return undefined;
437 const listed =
438 result.subtype === "success" || !Array.isArray(result.errors)
439 ? undefined
440 : result.errors.find((error) => !error.startsWith("[ede_diagnostic]"));
441 if (listed) {
442 return listed;
443 }
444 // Structured failure markers for results whose error list is empty or
445 // diagnostic-only: an overloaded API (529) and the terminal reasons the
446 // CLI stamps when it gives up on a turn.
447 if (isOverloadedResult(result)) {
448 return "Claude API is overloaded (529). Try again shortly.";
449 }
450 switch (result.terminal_reason) {
451 case "api_error":
452 return "Claude gave up after repeated API errors.";
453 case "malformed_tool_use_exhausted":
454 return "Claude gave up after repeated malformed tool calls.";
455 case "budget_exhausted":
456 return "Claude stopped: the turn's token budget was exhausted.";
457 case "structured_output_retry_exhausted":
458 return "Claude could not produce the requested structured output.";
459 case "tool_deferred_unavailable":
460 return "Claude could not resume a deferred tool call: the tool is no longer available.";
461 case "turn_setup_failed":
462 return "Claude could not start the turn.";
463 case "blocking_limit":
464 return "Claude stopped: a usage limit blocked the request.";
465 case "rapid_refill_breaker":
466 return "Claude stopped: the context refilled too quickly after compaction.";
467 case "prompt_too_long":
468 return "Claude stopped: the prompt exceeds the model's context window.";
469 case "image_error":
470 return "Claude stopped: an image in the conversation could not be processed.";
471 case "model_error":
472 return "Claude stopped: the model returned an error.";
473 default:
474 return undefined;
439475 }
440 return result.errors.find((error) => !error.startsWith("[ede_diagnostic]"));
441476}
442477
443478function isInterruptedResult(result: SDKResultMessage): boolean {
......@@ -1390,7 +1425,43 @@ const buildUserMessageEffect = Effect.fn("buildUserMessageEffect")(function* (
13901425 return buildUserMessage({ sdkContent });
13911426});
13921427
1428/**
1429 * terminal_reason values the CLI classifies as dead turns: the turn died
1430 * rather than finished, even when the result subtype is success and the
1431 * error list is empty. Kept in sync with the messages in
1432 * resultUserFacingError.
1433 */
1434const FAILED_TERMINAL_REASONS: ReadonlySet<NonNullable<SDKResultMessage["terminal_reason"]>> =
1435 new Set([
1436 "api_error",
1437 "malformed_tool_use_exhausted",
1438 "budget_exhausted",
1439 "structured_output_retry_exhausted",
1440 "tool_deferred_unavailable",
1441 "turn_setup_failed",
1442 "blocking_limit",
1443 "rapid_refill_breaker",
1444 "prompt_too_long",
1445 "image_error",
1446 "model_error",
1447 ]);
1448
1449/**
1450 * The CLI reports repeated 529 overload failures as a success-subtype result
1451 * with api_error_status 529 and an empty error list; the status code is the
1452 * only structured failure signal.
1453 */
1454function isOverloadedResult(result: SDKResultMessage): boolean {
1455 return result.subtype === "success" && result.api_error_status === 529;
1456}
1457
13931458function turnStatusFromResult(result: SDKResultMessage): ProviderRuntimeTurnStatus {
1459 if (
1460 isOverloadedResult(result) ||
1461 (result.terminal_reason !== undefined && FAILED_TERMINAL_REASONS.has(result.terminal_reason))
1462 ) {
1463 return "failed";
1464 }
13941465 if (result.subtype === "success") {
13951466 return "completed";
13961467 }
......@@ -3174,15 +3245,11 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
31743245 // Undeclared-but-real subtypes (absent from the SDK's union, so they can't
31753246 // be switch cases): consumed intentionally without emitting, otherwise
31763247 // they fall through to the unknown-subtype warning and surface as spurious
3177 // error rows in client work logs. `background_tasks_changed` is a roster
3178 // snapshot ({tasks: [...]}) — the task_* lifecycle events carry the
3179 // authoritative per-agent data and the typed background_tasks control
3180 // request is the reconciliation source. `vcs_state_changed`
3248 // error rows in client work logs. `vcs_state_changed`
31813249 // ({kind: commit|push|rebase}) and `code_change_published`
31823250 // ({provider, url, repo}) are informational CLI notices; the work log
31833251 // already shows the underlying git/gh tool calls.
31843252 switch (message.subtype as string) {
3185 case "background_tasks_changed":
31863253 case "vcs_state_changed":
31873254 case "code_change_published":
31883255 return;
......@@ -3497,12 +3564,39 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
34973564 return;
34983565 // Inner protocol/UX details with no T3 surface today — consumed
34993566 // deliberately so they don't masquerade as unknown-subtype warnings.
3567 // `background_tasks_changed` is a roster snapshot ({tasks: [...]}); the
3568 // task_* lifecycle events carry the authoritative per-agent data and
3569 // the typed background_tasks control request is the reconciliation
3570 // source. `control_request_progress` is a liveness heartbeat for an
3571 // in-flight control request. `worker_shutting_down` is a Remote
3572 // Control worker notice; the session close path reports the outcome.
35003573 case "model_refusal_fallback":
35013574 case "local_command_output":
35023575 case "plugin_install":
35033576 case "commands_changed":
35043577 case "memory_recall":
35053578 case "elicitation_complete":
3579 case "background_tasks_changed":
3580 case "control_request_progress":
3581 case "worker_shutting_down":
3582 return;
3583 case "informational":
3584 // Transcript-level CLI notes. Only warnings (e.g. a Stop hook that
3585 // refused continuation) warrant a work-log row; info/notice/
3586 // suggestion levels are CLI chrome.
3587 if (message.level === "warning") {
3588 yield* emitRuntimeWarning(context, message.content, message);
3589 }
3590 return;
3591 case "model_refusal_no_fallback":
3592 // The API refused the request and no fallback model was available.
3593 // The terminal result reports the failed turn; this row carries the
3594 // refusal explanation the result's error list lacks.
3595 yield* emitRuntimeWarning(
3596 context,
3597 message.api_refusal_explanation?.trim() || message.content,
3598 message,
3599 );
35063600 return;
35073601 case "permission_denied":
35083602 yield* offerRuntimeEvent({
......@@ -3528,7 +3622,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
35283622 // handled above, so `message` narrows to never here — a new SDK
35293623 // release adding a subtype fails this typecheck instead of silently
35303624 // warning at runtime. The runtime fallback still catches undeclared
3531 // wire-only subtypes (like background_tasks_changed used to be).
3625 // wire-only subtypes (like vcs_state_changed).
35323626 message satisfies never;
35333627 const unknownMessage = message as never as { subtype: string };
35343628 yield* emitRuntimeWarning(
......@@ -3657,7 +3751,10 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* (
36573751 yield* handleSdkTelemetryMessage(context, message);
36583752 return;
36593753 // Composer prompt suggestions have no T3 surface; consumed deliberately.
3754 // `conversation_reset` announces a CLI-side conversation id swap
3755 // (e.g. /clear); T3 keeps its own thread identity and resume cursor.
36603756 case "prompt_suggestion":
3757 case "conversation_reset":
36613758 return;
36623759 default: {
36633760 // Exhaustiveness guard (see handleSystemMessage): new SDK top-level
pnpm-lock.yaml+5-5
......@@ -476,8 +476,8 @@ importers:
476476 apps/server:
477477 dependencies:
478478 '@anthropic-ai/claude-agent-sdk':
479 specifier: ^0.3.170
480 version: 0.3.170(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)
479 specifier: ^0.3.260
480 version: 0.3.260(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)
481481 '@effect/platform-bun':
482482 specifier: 4.0.0-beta.103
483483 version: 4.0.0-beta.103(bufferutil@4.1.0)(effect@4.0.0-beta.103(patch_hash=af36b7948b6f9c56623074662b51dade5699880c1a7c71245de73e13c3185fb6))(utf-8-validate@6.0.6)
......@@ -1011,8 +1011,8 @@ packages:
10111011 '@alchemy.run/node-utils@0.0.5':
10121012 resolution: {integrity: sha512-5agdhQxWBodxa5hDRyjnpx91RTU3g+qd5fxYB7uNDCaOzB0XC47UU/KHR6zf6jhz/NXf61gJS+vhYwn8NHeRoQ==}
10131013
1014 '@anthropic-ai/claude-agent-sdk@0.3.170':
1015 resolution: {integrity: sha512-pAvhfk+iTodXZ6RF18Kz7BEUWFjL7EcR3tKuhUNdPpE1NAYCR3mSHGbafi72JsrNwKEDIs7FU31z3fqhwy8QzA==}
1014 '@anthropic-ai/claude-agent-sdk@0.3.260':
1015 resolution: {integrity: sha512-PmABtP4Rwd6l95itQrqzguv6rS9uACqikPB9g8BPeWRKZOpy3xpEOjJLYauof3BFk2wNZnfhr0Ttx8ttcZzq0w==}
10161016 engines: {node: '>=18.0.0'}
10171017 peerDependencies:
10181018 '@anthropic-ai/sdk': '>=0.93.0'
......@@ -10527,7 +10527,7 @@ snapshots:
1052710527
1052810528 '@alchemy.run/node-utils@0.0.5': {}
1052910529
10530 '@anthropic-ai/claude-agent-sdk@0.3.170(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)':
10530 '@anthropic-ai/claude-agent-sdk@0.3.260(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)':
1053110531 dependencies:
1053210532 '@anthropic-ai/sdk': 0.93.0(zod@4.4.3)
1053310533 '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3)
pnpm-workspace.yaml+1
......@@ -90,6 +90,7 @@ minimumReleaseAgeExclude:
9090 - expo-updates@57.0.19
9191 - expo@57.0.18
9292 - expo-widgets@57.0.15
93 - "@anthropic-ai/claude-agent-sdk@0.3.260"
9394
9495overrides:
9596 # The SDK always receives the user's Claude executable, so its bundled binaries are unused.