@anthropic-ai/sdk 0.116.0 → 0.118.0

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.
Files changed (246) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/client.d.mts +2 -2
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +2 -2
  5. package/client.d.ts.map +1 -1
  6. package/client.js +1 -1
  7. package/client.js.map +1 -1
  8. package/client.mjs +1 -1
  9. package/client.mjs.map +1 -1
  10. package/core/api-promise.d.mts +1 -0
  11. package/core/api-promise.d.mts.map +1 -1
  12. package/core/api-promise.d.ts +1 -0
  13. package/core/api-promise.d.ts.map +1 -1
  14. package/core/api-promise.js +7 -2
  15. package/core/api-promise.js.map +1 -1
  16. package/core/api-promise.mjs +8 -3
  17. package/core/api-promise.mjs.map +1 -1
  18. package/core/error.d.mts +1 -0
  19. package/core/error.d.mts.map +1 -1
  20. package/core/error.d.ts +1 -0
  21. package/core/error.d.ts.map +1 -1
  22. package/core/error.js +1 -0
  23. package/core/error.js.map +1 -1
  24. package/core/error.mjs +1 -0
  25. package/core/error.mjs.map +1 -1
  26. package/core/middleware.js +1 -1
  27. package/core/middleware.js.map +1 -1
  28. package/core/middleware.mjs +2 -2
  29. package/core/middleware.mjs.map +1 -1
  30. package/helpers/beta/zod.d.mts.map +1 -1
  31. package/helpers/beta/zod.d.ts.map +1 -1
  32. package/helpers/beta/zod.js +1 -2
  33. package/helpers/beta/zod.js.map +1 -1
  34. package/helpers/beta/zod.mjs +1 -2
  35. package/helpers/beta/zod.mjs.map +1 -1
  36. package/helpers/zod.d.mts.map +1 -1
  37. package/helpers/zod.d.ts.map +1 -1
  38. package/helpers/zod.js +1 -2
  39. package/helpers/zod.js.map +1 -1
  40. package/helpers/zod.mjs +1 -2
  41. package/helpers/zod.mjs.map +1 -1
  42. package/internal/parse.d.mts +2 -1
  43. package/internal/parse.d.mts.map +1 -1
  44. package/internal/parse.d.ts +2 -1
  45. package/internal/parse.d.ts.map +1 -1
  46. package/internal/parse.js +6 -6
  47. package/internal/parse.js.map +1 -1
  48. package/internal/parse.mjs +5 -5
  49. package/internal/parse.mjs.map +1 -1
  50. package/lib/BetaMessageStream.d.mts +2 -0
  51. package/lib/BetaMessageStream.d.mts.map +1 -1
  52. package/lib/BetaMessageStream.d.ts +2 -0
  53. package/lib/BetaMessageStream.d.ts.map +1 -1
  54. package/lib/BetaMessageStream.js +20 -7
  55. package/lib/BetaMessageStream.js.map +1 -1
  56. package/lib/BetaMessageStream.mjs +20 -7
  57. package/lib/BetaMessageStream.mjs.map +1 -1
  58. package/lib/MessageStream.d.mts +2 -0
  59. package/lib/MessageStream.d.mts.map +1 -1
  60. package/lib/MessageStream.d.ts +2 -0
  61. package/lib/MessageStream.d.ts.map +1 -1
  62. package/lib/MessageStream.js +17 -6
  63. package/lib/MessageStream.js.map +1 -1
  64. package/lib/MessageStream.mjs +17 -6
  65. package/lib/MessageStream.mjs.map +1 -1
  66. package/lib/environments/worker.d.mts +10 -6
  67. package/lib/environments/worker.d.mts.map +1 -1
  68. package/lib/environments/worker.d.ts +10 -6
  69. package/lib/environments/worker.d.ts.map +1 -1
  70. package/lib/environments/worker.js +51 -18
  71. package/lib/environments/worker.js.map +1 -1
  72. package/lib/environments/worker.mjs +51 -18
  73. package/lib/environments/worker.mjs.map +1 -1
  74. package/lib/tools/BetaRunnableTool.d.mts +20 -42
  75. package/lib/tools/BetaRunnableTool.d.mts.map +1 -1
  76. package/lib/tools/BetaRunnableTool.d.ts +20 -42
  77. package/lib/tools/BetaRunnableTool.d.ts.map +1 -1
  78. package/lib/tools/BetaRunnableTool.js +3 -17
  79. package/lib/tools/BetaRunnableTool.js.map +1 -1
  80. package/lib/tools/BetaRunnableTool.mjs +3 -17
  81. package/lib/tools/BetaRunnableTool.mjs.map +1 -1
  82. package/lib/tools/BetaToolRunner.d.mts.map +1 -1
  83. package/lib/tools/BetaToolRunner.d.ts.map +1 -1
  84. package/lib/tools/BetaToolRunner.js +11 -12
  85. package/lib/tools/BetaToolRunner.js.map +1 -1
  86. package/lib/tools/BetaToolRunner.mjs +11 -12
  87. package/lib/tools/BetaToolRunner.mjs.map +1 -1
  88. package/lib/tools/SessionToolRunner.d.mts +6 -0
  89. package/lib/tools/SessionToolRunner.d.mts.map +1 -1
  90. package/lib/tools/SessionToolRunner.d.ts +6 -0
  91. package/lib/tools/SessionToolRunner.d.ts.map +1 -1
  92. package/lib/tools/SessionToolRunner.js +37 -8
  93. package/lib/tools/SessionToolRunner.js.map +1 -1
  94. package/lib/tools/SessionToolRunner.mjs +38 -9
  95. package/lib/tools/SessionToolRunner.mjs.map +1 -1
  96. package/package.json +9 -15
  97. package/resources/beta/agents/agents.d.mts +3 -3
  98. package/resources/beta/agents/agents.d.ts +3 -3
  99. package/resources/beta/agents/agents.js +1 -1
  100. package/resources/beta/agents/agents.mjs +1 -1
  101. package/resources/beta/beta.d.mts +5 -5
  102. package/resources/beta/beta.d.mts.map +1 -1
  103. package/resources/beta/beta.d.ts +5 -5
  104. package/resources/beta/beta.d.ts.map +1 -1
  105. package/resources/beta/beta.js.map +1 -1
  106. package/resources/beta/beta.mjs.map +1 -1
  107. package/resources/beta/dreams.d.mts +48 -8
  108. package/resources/beta/dreams.d.mts.map +1 -1
  109. package/resources/beta/dreams.d.ts +48 -8
  110. package/resources/beta/dreams.d.ts.map +1 -1
  111. package/resources/beta/environments/environments.d.mts +4 -3
  112. package/resources/beta/environments/environments.d.mts.map +1 -1
  113. package/resources/beta/environments/environments.d.ts +4 -3
  114. package/resources/beta/environments/environments.d.ts.map +1 -1
  115. package/resources/beta/environments/environments.js.map +1 -1
  116. package/resources/beta/environments/environments.mjs.map +1 -1
  117. package/resources/beta/index.d.mts +2 -2
  118. package/resources/beta/index.d.mts.map +1 -1
  119. package/resources/beta/index.d.ts +2 -2
  120. package/resources/beta/index.d.ts.map +1 -1
  121. package/resources/beta/index.js.map +1 -1
  122. package/resources/beta/index.mjs.map +1 -1
  123. package/resources/beta/memory-stores/index.d.mts +1 -1
  124. package/resources/beta/memory-stores/index.d.mts.map +1 -1
  125. package/resources/beta/memory-stores/index.d.ts +1 -1
  126. package/resources/beta/memory-stores/index.d.ts.map +1 -1
  127. package/resources/beta/memory-stores/index.js.map +1 -1
  128. package/resources/beta/memory-stores/index.mjs.map +1 -1
  129. package/resources/beta/memory-stores/memory-stores.d.mts +2 -2
  130. package/resources/beta/memory-stores/memory-stores.d.mts.map +1 -1
  131. package/resources/beta/memory-stores/memory-stores.d.ts +2 -2
  132. package/resources/beta/memory-stores/memory-stores.d.ts.map +1 -1
  133. package/resources/beta/memory-stores/memory-stores.js.map +1 -1
  134. package/resources/beta/memory-stores/memory-stores.mjs.map +1 -1
  135. package/resources/beta/memory-stores/memory-versions.d.mts +17 -2
  136. package/resources/beta/memory-stores/memory-versions.d.mts.map +1 -1
  137. package/resources/beta/memory-stores/memory-versions.d.ts +17 -2
  138. package/resources/beta/memory-stores/memory-versions.d.ts.map +1 -1
  139. package/resources/beta/messages/batches.d.mts +1 -1
  140. package/resources/beta/messages/batches.d.ts +1 -1
  141. package/resources/beta/messages/batches.js +1 -1
  142. package/resources/beta/messages/batches.mjs +1 -1
  143. package/resources/beta/messages/index.d.mts +1 -1
  144. package/resources/beta/messages/index.d.mts.map +1 -1
  145. package/resources/beta/messages/index.d.ts +1 -1
  146. package/resources/beta/messages/index.d.ts.map +1 -1
  147. package/resources/beta/messages/index.js.map +1 -1
  148. package/resources/beta/messages/index.mjs.map +1 -1
  149. package/resources/beta/messages/messages.d.mts +4 -22
  150. package/resources/beta/messages/messages.d.mts.map +1 -1
  151. package/resources/beta/messages/messages.d.ts +4 -22
  152. package/resources/beta/messages/messages.d.ts.map +1 -1
  153. package/resources/beta/messages/messages.js +2 -2
  154. package/resources/beta/messages/messages.js.map +1 -1
  155. package/resources/beta/messages/messages.mjs +2 -2
  156. package/resources/beta/messages/messages.mjs.map +1 -1
  157. package/resources/beta/sessions/sessions.d.mts +1 -1
  158. package/resources/beta/sessions/sessions.d.ts +1 -1
  159. package/resources/beta/user-profiles.d.mts +37 -11
  160. package/resources/beta/user-profiles.d.mts.map +1 -1
  161. package/resources/beta/user-profiles.d.ts +37 -11
  162. package/resources/beta/user-profiles.d.ts.map +1 -1
  163. package/resources/beta/user-profiles.js +5 -5
  164. package/resources/beta/user-profiles.mjs +5 -5
  165. package/resources/index.d.mts +1 -1
  166. package/resources/index.d.mts.map +1 -1
  167. package/resources/index.d.ts +1 -1
  168. package/resources/index.d.ts.map +1 -1
  169. package/resources/index.js.map +1 -1
  170. package/resources/index.mjs.map +1 -1
  171. package/resources/messages/batches.d.mts +1 -1
  172. package/resources/messages/batches.d.ts +1 -1
  173. package/resources/messages/batches.js +1 -1
  174. package/resources/messages/batches.mjs +1 -1
  175. package/resources/messages/index.d.mts +1 -1
  176. package/resources/messages/index.d.mts.map +1 -1
  177. package/resources/messages/index.d.ts +1 -1
  178. package/resources/messages/index.d.ts.map +1 -1
  179. package/resources/messages/index.js.map +1 -1
  180. package/resources/messages/index.mjs.map +1 -1
  181. package/resources/messages/messages.d.mts +4 -22
  182. package/resources/messages/messages.d.mts.map +1 -1
  183. package/resources/messages/messages.d.ts +4 -22
  184. package/resources/messages/messages.d.ts.map +1 -1
  185. package/resources/messages/messages.js +2 -2
  186. package/resources/messages/messages.js.map +1 -1
  187. package/resources/messages/messages.mjs +2 -2
  188. package/resources/messages/messages.mjs.map +1 -1
  189. package/src/client.ts +1 -3
  190. package/src/core/api-promise.ts +14 -4
  191. package/src/core/error.ts +2 -0
  192. package/src/core/middleware.ts +2 -2
  193. package/src/helpers/beta/zod.ts +1 -3
  194. package/src/helpers/zod.ts +1 -3
  195. package/src/internal/parse.ts +6 -6
  196. package/src/lib/BetaMessageStream.ts +23 -5
  197. package/src/lib/MessageStream.ts +19 -4
  198. package/src/lib/environments/worker.ts +56 -17
  199. package/src/lib/tools/BetaRunnableTool.ts +20 -44
  200. package/src/lib/tools/BetaToolRunner.ts +11 -12
  201. package/src/lib/tools/SessionToolRunner.ts +39 -6
  202. package/src/resources/beta/agents/agents.ts +3 -3
  203. package/src/resources/beta/beta.ts +7 -2
  204. package/src/resources/beta/dreams.ts +56 -7
  205. package/src/resources/beta/environments/environments.ts +4 -3
  206. package/src/resources/beta/index.ts +3 -1
  207. package/src/resources/beta/memory-stores/index.ts +1 -0
  208. package/src/resources/beta/memory-stores/memory-stores.ts +2 -0
  209. package/src/resources/beta/memory-stores/memory-versions.ts +21 -1
  210. package/src/resources/beta/messages/batches.ts +1 -1
  211. package/src/resources/beta/messages/index.ts +0 -1
  212. package/src/resources/beta/messages/messages.ts +3 -26
  213. package/src/resources/beta/sessions/sessions.ts +1 -1
  214. package/src/resources/beta/user-profiles.ts +46 -17
  215. package/src/resources/index.ts +0 -1
  216. package/src/resources/messages/batches.ts +1 -1
  217. package/src/resources/messages/index.ts +0 -1
  218. package/src/resources/messages/messages.ts +4 -27
  219. package/src/tools/agent-toolset/fs-util.ts +43 -23
  220. package/src/tools/agent-toolset/node.ts +1 -1
  221. package/src/tools/agent-toolset/skills.ts +127 -31
  222. package/src/version.ts +1 -1
  223. package/tools/agent-toolset/fs-util.d.mts +14 -2
  224. package/tools/agent-toolset/fs-util.d.mts.map +1 -1
  225. package/tools/agent-toolset/fs-util.d.ts +14 -2
  226. package/tools/agent-toolset/fs-util.d.ts.map +1 -1
  227. package/tools/agent-toolset/fs-util.js +46 -24
  228. package/tools/agent-toolset/fs-util.js.map +1 -1
  229. package/tools/agent-toolset/fs-util.mjs +45 -24
  230. package/tools/agent-toolset/fs-util.mjs.map +1 -1
  231. package/tools/agent-toolset/node.js +1 -1
  232. package/tools/agent-toolset/node.js.map +1 -1
  233. package/tools/agent-toolset/node.mjs +1 -1
  234. package/tools/agent-toolset/node.mjs.map +1 -1
  235. package/tools/agent-toolset/skills.d.mts +26 -3
  236. package/tools/agent-toolset/skills.d.mts.map +1 -1
  237. package/tools/agent-toolset/skills.d.ts +26 -3
  238. package/tools/agent-toolset/skills.d.ts.map +1 -1
  239. package/tools/agent-toolset/skills.js +114 -29
  240. package/tools/agent-toolset/skills.js.map +1 -1
  241. package/tools/agent-toolset/skills.mjs +113 -30
  242. package/tools/agent-toolset/skills.mjs.map +1 -1
  243. package/version.d.mts +1 -1
  244. package/version.d.ts +1 -1
  245. package/version.js +1 -1
  246. package/version.mjs +1 -1
@@ -7,7 +7,7 @@ import {
7
7
  type APIResponseProps,
8
8
  type WithRequestID,
9
9
  defaultParseResponse,
10
- addRequestID,
10
+ addResponseIDs,
11
11
  } from '../internal/parse';
12
12
 
13
13
  /**
@@ -37,7 +37,7 @@ export class APIPromise<T> extends Promise<WithRequestID<T>> {
37
37
 
38
38
  _thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
39
39
  return new APIPromise(this.#client, this.responsePromise, async (client, props) =>
40
- addRequestID(transform(await this.parseResponse(client, props), props), props.response),
40
+ addResponseIDs(transform(await this.parseResponse(client, props), props), props.response),
41
41
  );
42
42
  }
43
43
 
@@ -68,9 +68,19 @@ export class APIPromise<T> extends Promise<WithRequestID<T>> {
68
68
  * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
69
69
  * to your `tsconfig.json`.
70
70
  */
71
- async withResponse(): Promise<{ data: T; response: Response; request_id: string | null | undefined }> {
71
+ async withResponse(): Promise<{
72
+ data: T;
73
+ response: Response;
74
+ request_id: string | null | undefined;
75
+ workspace_id: string | null | undefined;
76
+ }> {
72
77
  const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
73
- return { data, response, request_id: response.headers.get('request-id') };
78
+ return {
79
+ data,
80
+ response,
81
+ request_id: response.headers.get('request-id'),
82
+ workspace_id: response.headers.get('anthropic-workspace-id'),
83
+ };
74
84
  }
75
85
 
76
86
  private parse(): Promise<WithRequestID<T>> {
package/src/core/error.ts CHANGED
@@ -18,6 +18,7 @@ export class APIError<
18
18
  readonly error: TError;
19
19
 
20
20
  readonly requestID: string | null | undefined;
21
+ readonly workspaceID: string | null | undefined;
21
22
 
22
23
  /** The `error.type` from the API response body, e.g. `"rate_limit_error"` */
23
24
  readonly type: ErrorType | null;
@@ -33,6 +34,7 @@ export class APIError<
33
34
  this.status = status;
34
35
  this.headers = headers;
35
36
  this.requestID = headers?.get('request-id');
37
+ this.workspaceID = headers?.get('anthropic-workspace-id');
36
38
  this.error = error;
37
39
  this.type = type ?? null;
38
40
  }
@@ -1,7 +1,7 @@
1
1
  import type { BaseAnthropic } from '../client';
2
2
  import type { Fetch } from '../internal/builtin-types';
3
3
  import { castToError, isAbortError } from '../internal/errors';
4
- import { addRequestID } from '../internal/parse';
4
+ import { addResponseIDs } from '../internal/parse';
5
5
  import type { FinalRequestOptions } from '../internal/request-options';
6
6
  import { defaultLogger, loggerFor, type Logger } from '../internal/utils/log';
7
7
  import type { APIRequest } from './api';
@@ -299,7 +299,7 @@ async function parseMiddlewareResponse(
299
299
  // if there is no content we can't do anything
300
300
  return undefined;
301
301
  }
302
- return addRequestID(await response.clone().json(), response);
302
+ return addResponseIDs(await response.clone().json(), response);
303
303
  }
304
304
 
305
305
  return await response.clone().text();
@@ -16,9 +16,7 @@ import { BetaToolResultContentBlockParam } from '../../resources/beta';
16
16
  export function betaZodOutputFormat<ZodInput extends z.ZodType>(
17
17
  zodObject: ZodInput,
18
18
  ): AutoParseableBetaOutputFormat<z.infer<ZodInput>> {
19
- let jsonSchema = z.toJSONSchema(zodObject, { reused: 'ref' });
20
-
21
- jsonSchema = transformJSONSchema(jsonSchema);
19
+ const jsonSchema = transformJSONSchema(z.toJSONSchema(zodObject, { reused: 'ref' }));
22
20
 
23
21
  return {
24
22
  type: 'json_schema',
@@ -15,9 +15,7 @@ import { AutoParseableOutputFormat } from '../lib/parser';
15
15
  export function zodOutputFormat<ZodInput extends z.ZodType>(
16
16
  zodObject: ZodInput,
17
17
  ): AutoParseableOutputFormat<z.infer<ZodInput>> {
18
- let jsonSchema = z.toJSONSchema(zodObject, { reused: 'ref' });
19
-
20
- jsonSchema = transformJSONSchema(jsonSchema);
18
+ const jsonSchema = transformJSONSchema(z.toJSONSchema(zodObject, { reused: 'ref' }));
21
19
 
22
20
  return {
23
21
  type: 'json_schema',
@@ -51,7 +51,7 @@ export async function defaultParseResponse<T>(
51
51
  }
52
52
 
53
53
  const json = await response.json();
54
- return addRequestID(json as T, response);
54
+ return addResponseIDs(json as T, response);
55
55
  }
56
56
 
57
57
  const text = await response.text();
@@ -80,16 +80,16 @@ export async function defaultParseResponse<T>(
80
80
 
81
81
  export type WithRequestID<T> =
82
82
  T extends Array<any> | Response | AbstractPage<any> ? T
83
- : T extends Record<string, any> ? T & { _request_id?: string | null }
83
+ : T extends Record<string, any> ? T & { _request_id?: string | null; _workspace_id?: string | null }
84
84
  : T;
85
85
 
86
- export function addRequestID<T>(value: T, response: Response): WithRequestID<T> {
86
+ export function addResponseIDs<T>(value: T, response: Response): WithRequestID<T> {
87
87
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
88
88
  return value as WithRequestID<T>;
89
89
  }
90
90
 
91
- return Object.defineProperty(value, '_request_id', {
92
- value: response.headers.get('request-id'),
93
- enumerable: false,
91
+ return Object.defineProperties(value, {
92
+ _request_id: { value: response.headers.get('request-id'), enumerable: false },
93
+ _workspace_id: { value: response.headers.get('anthropic-workspace-id'), enumerable: false },
94
94
  }) as WithRequestID<T>;
95
95
  }
@@ -74,6 +74,7 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
74
74
  #catchingPromiseCreated = false;
75
75
  #response: Response | null | undefined;
76
76
  #request_id: string | null | undefined;
77
+ #workspace_id: string | null | undefined;
77
78
  #logger: Logger;
78
79
 
79
80
  constructor(params: MessageCreateParamsBase | null, opts?: { logger?: Logger | undefined }) {
@@ -106,6 +107,10 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
106
107
  return this.#request_id;
107
108
  }
108
109
 
110
+ get workspace_id(): string | null | undefined {
111
+ return this.#workspace_id;
112
+ }
113
+
109
114
  /**
110
115
  * Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
111
116
  * returned vie the `request-id` header which is useful for debugging requests and resporting
@@ -120,6 +125,7 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
120
125
  data: BetaMessageStream<ParsedT>;
121
126
  response: Response;
122
127
  request_id: string | null | undefined;
128
+ workspace_id: string | null | undefined;
123
129
  }> {
124
130
  this.#catchingPromiseCreated = true;
125
131
 
@@ -132,6 +138,7 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
132
138
  data: this,
133
139
  response,
134
140
  request_id: response.headers.get('request-id'),
141
+ workspace_id: response.headers.get('anthropic-workspace-id'),
135
142
  };
136
143
  }
137
144
 
@@ -223,6 +230,7 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
223
230
  if (this.ended) return;
224
231
  this.#response = response;
225
232
  this.#request_id = response?.headers.get('request-id');
233
+ this.#workspace_id = response?.headers.get('anthropic-workspace-id');
226
234
  this.#resolveConnectedPromise(response);
227
235
  this._emit('connect');
228
236
  }
@@ -581,15 +589,21 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
581
589
  case 'message_stop':
582
590
  return snapshot;
583
591
  case 'message_delta':
584
- snapshot.container = event.delta.container;
585
592
  snapshot.stop_reason = event.delta.stop_reason;
586
593
  snapshot.stop_sequence = event.delta.stop_sequence;
587
- if (event.delta.stop_details != null) {
588
- snapshot.stop_details = event.delta.stop_details;
589
- }
594
+ snapshot.stop_details = event.delta.stop_details;
590
595
  snapshot.usage.output_tokens = event.usage.output_tokens;
591
- snapshot.context_management = event.context_management;
592
596
 
597
+ if (event.delta.container != null) {
598
+ snapshot.container = event.delta.container;
599
+ }
600
+
601
+ if (event.context_management != null) {
602
+ snapshot.context_management = event.context_management;
603
+ }
604
+
605
+ // The remaining usage counters are cumulative whole-message totals that are
606
+ // omitted when they don't apply, so overwrite when present and never add.
593
607
  if (event.usage.input_tokens != null) {
594
608
  snapshot.usage.input_tokens = event.usage.input_tokens;
595
609
  }
@@ -614,6 +628,10 @@ export class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMess
614
628
  snapshot.usage.fallback_credit = event.usage.fallback_credit;
615
629
  }
616
630
 
631
+ if (event.usage.output_tokens_details != null) {
632
+ snapshot.usage.output_tokens_details = event.usage.output_tokens_details;
633
+ }
634
+
617
635
  return snapshot;
618
636
  case 'content_block_start':
619
637
  snapshot.content.push(event.content_block);
@@ -73,6 +73,7 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
73
73
  #catchingPromiseCreated = false;
74
74
  #response: Response | null | undefined;
75
75
  #request_id: string | null | undefined;
76
+ #workspace_id: string | null | undefined;
76
77
  #logger: Logger;
77
78
 
78
79
  constructor(params: MessageCreateParamsBase | null, opts?: { logger?: Logger | undefined }) {
@@ -105,6 +106,10 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
105
106
  return this.#request_id;
106
107
  }
107
108
 
109
+ get workspace_id(): string | null | undefined {
110
+ return this.#workspace_id;
111
+ }
112
+
108
113
  /**
109
114
  * Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
110
115
  * returned vie the `request-id` header which is useful for debugging requests and resporting
@@ -119,6 +124,7 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
119
124
  data: MessageStream<ParsedT>;
120
125
  response: Response;
121
126
  request_id: string | null | undefined;
127
+ workspace_id: string | null | undefined;
122
128
  }> {
123
129
  this.#catchingPromiseCreated = true;
124
130
 
@@ -131,6 +137,7 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
131
137
  data: this,
132
138
  response,
133
139
  request_id: response.headers.get('request-id'),
140
+ workspace_id: response.headers.get('anthropic-workspace-id'),
134
141
  };
135
142
  }
136
143
 
@@ -222,6 +229,7 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
222
229
  if (this.ended) return;
223
230
  this.#response = response;
224
231
  this.#request_id = response?.headers.get('request-id');
232
+ this.#workspace_id = response?.headers.get('anthropic-workspace-id');
225
233
  this.#resolveConnectedPromise(response);
226
234
  this._emit('connect');
227
235
  }
@@ -575,12 +583,15 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
575
583
  case 'message_delta':
576
584
  snapshot.stop_reason = event.delta.stop_reason;
577
585
  snapshot.stop_sequence = event.delta.stop_sequence;
578
- if (event.delta.stop_details != null) {
579
- snapshot.stop_details = event.delta.stop_details;
580
- }
586
+ snapshot.stop_details = event.delta.stop_details;
581
587
  snapshot.usage.output_tokens = event.usage.output_tokens;
582
588
 
583
- // Update other usage fields if they exist in the event
589
+ if (event.delta.container != null) {
590
+ snapshot.container = event.delta.container;
591
+ }
592
+
593
+ // The remaining usage counters are cumulative whole-message totals that are
594
+ // omitted when they don't apply, so overwrite when present and never add.
584
595
  if (event.usage.input_tokens != null) {
585
596
  snapshot.usage.input_tokens = event.usage.input_tokens;
586
597
  }
@@ -597,6 +608,10 @@ export class MessageStream<ParsedT = null> implements AsyncIterable<MessageStrea
597
608
  snapshot.usage.server_tool_use = event.usage.server_tool_use;
598
609
  }
599
610
 
611
+ if (event.usage.output_tokens_details != null) {
612
+ snapshot.usage.output_tokens_details = event.usage.output_tokens_details;
613
+ }
614
+
600
615
  return snapshot;
601
616
  case 'content_block_start':
602
617
  snapshot.content.push({ ...event.content_block });
@@ -21,6 +21,7 @@ import { copyClientForHelper } from '../helper-client';
21
21
  import type { AgentToolContext } from '../../tools/agent-toolset/node';
22
22
 
23
23
  const HEARTBEAT_DEFAULT_MS = 30_000;
24
+ const HEARTBEAT_TTL_DEFAULT_MS = 90_000;
24
25
  const NO_HEARTBEAT_SENTINEL = 'NO_HEARTBEAT';
25
26
 
26
27
  /**
@@ -51,6 +52,10 @@ export interface EnvironmentWorkerOptions {
51
52
  * Tools to expose to each claimed session. Defaults to
52
53
  * `betaAgentToolset20260401(ctx)` (the standard `agent_toolset_20260401` set
53
54
  * bound to the per-session {@link AgentToolContext}).
55
+ *
56
+ * A `run` that blocks the event loop synchronously also stalls the lease
57
+ * heartbeat, and the worker can lose the lease; keep tools non-blocking (see
58
+ * {@link BetaRunnableTool.run}).
54
59
  */
55
60
  tools?: EnvironmentWorkerTools;
56
61
  /** Base directory for the per-session {@link AgentToolContext}. Defaults to `process.cwd()`. */
@@ -109,10 +114,10 @@ type ClaimedWork = Pick<BetaSelfHostedWork, 'id' | 'environment_id' | 'data'>;
109
114
  * For each claimed `session` work item it: builds the per-session
110
115
  * {@link AgentToolContext}, downloads the session agent's skills
111
116
  * (`setupSkills`), then runs a {@link SessionToolRunner} for the session
112
- * *while* heartbeating the work-item lease in parallel; on exit it force-stops
113
- * the work item, cleans up the downloaded skills, and loops to the next one. The
114
- * lease heartbeat reports `state === "stopping"` / a lost lease back into the run
115
- * by aborting the session runner.
117
+ * while heartbeating the work-item lease on the same event loop; on exit it
118
+ * force-stops the work item, cleans up the downloaded skills, and loops to the
119
+ * next one. The lease heartbeat reports `state === "stopping"` / a lost lease
120
+ * back into the run by aborting the session runner.
116
121
  *
117
122
  * Use {@link EnvironmentWorker.handleItem} if you already hold a claimed work
118
123
  * item (e.g. a `worker poll --on-work` script handed one to a fresh process) and
@@ -195,8 +200,8 @@ export class EnvironmentWorker {
195
200
  * per-session {@link AgentToolContext} (workdir from this worker's options),
196
201
  * download the session agent's skills (`setupSkills`), run a
197
202
  * {@link SessionToolRunner} for the session while heartbeating the work-item
198
- * lease in parallel, and force-stop the work item on exit (whether the runner
199
- * finishes normally, throws, or the heartbeat loop signals shutdown).
203
+ * lease, and force-stop the work item on exit (whether the runner finishes
204
+ * normally, throws, or the heartbeat loop signals shutdown).
200
205
  *
201
206
  * Use this when something else does the claiming — e.g. a `worker poll
202
207
  * --on-work` script that hands an already-claimed item to a fresh process. The
@@ -292,19 +297,26 @@ export class EnvironmentWorker {
292
297
  const ctrl = new AbortController();
293
298
  const detachExternal = linkAbort(externalSignal, ctrl);
294
299
 
295
- const heartbeatPromise = heartbeatLoop(sessionClient, work, ctrl, log, this.requestOptions).catch((e) => {
300
+ // Inert until iterated below.
301
+ const runner = new SessionToolRunner(sessionId, {
302
+ client: sessionClient,
303
+ tools,
304
+ ...(this.maxIdleMs !== undefined ? { maxIdleMs: this.maxIdleMs } : {}),
305
+ ...(this.requestOptions !== undefined ? { requestOptions: this.requestOptions } : {}),
306
+ signal: ctrl.signal,
307
+ });
308
+
309
+ // Each heartbeat reports the lease TTL the server is enforcing; it becomes
310
+ // the runner's tool-result send retry window so a send keeps retrying
311
+ // exactly as long as the lease could still be live.
312
+ const heartbeatPromise = heartbeatLoop(sessionClient, work, ctrl, log, this.requestOptions, (ttlMs) =>
313
+ runner._setSendRetryWindow(ttlMs),
314
+ ).catch((e) => {
296
315
  if (!ctrl.signal.aborted) log.error('heartbeat loop failed', { work_id: work.id, error: String(e) });
297
316
  ctrl.abort();
298
317
  });
299
318
 
300
319
  try {
301
- const runner = new SessionToolRunner(sessionId, {
302
- client: sessionClient,
303
- tools,
304
- ...(this.maxIdleMs !== undefined ? { maxIdleMs: this.maxIdleMs } : {}),
305
- ...(this.requestOptions !== undefined ? { requestOptions: this.requestOptions } : {}),
306
- signal: ctrl.signal,
307
- });
308
320
  for await (const _ of runner) {
309
321
  // Drive the runner to completion; per-call observability is not part
310
322
  // of this composition's surface — use `SessionToolRunner` directly
@@ -348,7 +360,10 @@ async function forceStop(
348
360
  /**
349
361
  * Keep the work-item lease alive while a session is being served. Aborts `ctrl`
350
362
  * when the control plane reports the work is `stopping`/`stopped`, when the
351
- * lease is no longer extended, or on a permanent heartbeat failure.
363
+ * lease is no longer extended, on a permanent heartbeat failure, or when no
364
+ * heartbeat has succeeded for longer than the lease ttl (the lease is assumed
365
+ * lost). Each heartbeat call is cut off after the current beat interval so a
366
+ * hung request cannot outlive the lease it is meant to renew.
352
367
  */
353
368
  async function heartbeatLoop(
354
369
  client: Anthropic,
@@ -356,19 +371,31 @@ async function heartbeatLoop(
356
371
  ctrl: AbortController,
357
372
  logger: Logger,
358
373
  requestOptions?: BetaToolRunnerRequestOptions,
374
+ /** Called with the server-reported lease TTL after every successful beat. */
375
+ onLeaseTtl?: (ttlMs: number) => void,
359
376
  ): Promise<void> {
360
377
  let intervalMs = HEARTBEAT_DEFAULT_MS;
378
+ let ttlMs = HEARTBEAT_TTL_DEFAULT_MS;
379
+ let lastSuccessMs = Date.now();
361
380
  let last = NO_HEARTBEAT_SENTINEL;
362
381
  const beat = async (): Promise<void> => {
382
+ // Not the request `timeout` option: the core client retries timeouts, so
383
+ // it would not bound the call as a whole.
384
+ const beatCtrl = new AbortController();
385
+ const detach = linkAbort(ctrl.signal, beatCtrl);
386
+ const cutoff = setTimeout(() => beatCtrl.abort(), intervalMs);
363
387
  try {
364
388
  const resp = await client.beta.environments.work.heartbeat(
365
389
  work.id,
366
390
  { environment_id: work.environment_id, expected_last_heartbeat: last },
367
- { ...requestOptions, headers: buildHeaders([requestOptions?.headers]), signal: ctrl.signal },
391
+ { ...requestOptions, headers: buildHeaders([requestOptions?.headers]), signal: beatCtrl.signal },
368
392
  );
393
+ lastSuccessMs = Date.now();
369
394
  last = resp.last_heartbeat;
370
395
  if (resp.ttl_seconds > 0) {
371
- intervalMs = Math.max(1_000, Math.min((resp.ttl_seconds * 1000) / 2, HEARTBEAT_DEFAULT_MS));
396
+ ttlMs = resp.ttl_seconds * 1000;
397
+ intervalMs = Math.max(1_000, Math.min(ttlMs / 2, HEARTBEAT_DEFAULT_MS));
398
+ onLeaseTtl?.(ttlMs);
372
399
  }
373
400
  if (resp.state === 'stopping' || resp.state === 'stopped') {
374
401
  logger.info('heartbeat signals shutdown', { work_id: work.id, state: resp.state });
@@ -387,7 +414,19 @@ async function heartbeatLoop(
387
414
  ctrl.abort();
388
415
  throw e;
389
416
  }
417
+ if (Date.now() - lastSuccessMs > ttlMs) {
418
+ logger.error('lease assumed lost: no successful heartbeat in ttl', {
419
+ work_id: work.id,
420
+ ttl_ms: ttlMs,
421
+ error: String(e),
422
+ });
423
+ ctrl.abort();
424
+ return;
425
+ }
390
426
  logger.warn('transient heartbeat failure', { work_id: work.id, error: String(e) });
427
+ } finally {
428
+ clearTimeout(cutoff);
429
+ detach();
391
430
  }
392
431
  };
393
432
 
@@ -22,10 +22,7 @@ import { ToolError } from './ToolError';
22
22
 
23
23
  export type Promisable<T> = T | Promise<T>;
24
24
 
25
- /**
26
- * Tool types that can be implemented on the client.
27
- * Excludes server-side tools like code execution, web search, and MCP toolsets.
28
- */
25
+ /** Tools that run on the client. Server-side tools (code execution, web search, MCP toolsets) are not included. */
29
26
  export type BetaClientRunnableToolType =
30
27
  | BetaTool
31
28
  | BetaMemoryTool20250818
@@ -40,15 +37,9 @@ export type BetaClientRunnableToolType =
40
37
  | BetaToolTextEditor20250728;
41
38
 
42
39
  /**
43
- * The tool-use that triggered a {@link BetaRunnableTool.run}:
44
- *
45
- * - from `client.beta.messages.toolRunner`, a Messages `tool_use` content block;
46
- * - from `client.beta.sessions.events.toolRunner`, the `agent.tool_use` /
47
- * `agent.custom_tool_use` session event.
48
- *
49
- * The shapes overlap on the common fields (`id`, `name`, `input`), so code that
50
- * only reads those works without narrowing; narrow on the shape (e.g. `'type' in
51
- * x`) when you need surface-specific properties.
40
+ * The tool call being served: a Messages `tool_use` block, or a session
41
+ * `agent.tool_use` / `agent.custom_tool_use` event. All three carry `id`,
42
+ * `name` and `input`.
52
43
  */
53
44
  export type BetaToolUse =
54
45
  | BetaToolUseBlock
@@ -56,65 +47,50 @@ export type BetaToolUse =
56
47
  | BetaManagedAgentsAgentCustomToolUseEvent;
57
48
 
58
49
  export type BetaToolRunContext = {
59
- /** The tool-use that triggered this run. See {@link BetaToolUse}. */
60
50
  toolUse: BetaToolUse;
51
+ /** @deprecated Use `toolUse`. */
52
+ toolUseBlock: BetaToolUse;
61
53
  /**
62
- * @deprecated Renamed to `toolUse`. Also note that for
63
- * `client.beta.sessions.events.toolRunner` this is the `agent.tool_use` /
64
- * `agent.custom_tool_use` *event*, not a Messages content block, despite the
65
- * name — which is why it was renamed.
54
+ * Aborted when the runner is aborted or, in the session runner, when the
55
+ * per-tool timeout fires. The runner still waits for `run` to return, so
56
+ * check it in long-running work.
66
57
  */
67
- toolUseBlock: BetaToolUse;
68
58
  signal?: AbortSignal | null | undefined;
69
59
  };
70
60
 
71
- // this type is just an extension of BetaTool with a run and parse method
72
- // that will be called by `toolRunner()` helpers
61
+ /** A tool definition plus the `run` and `parse` the tool runners call. */
73
62
  export type BetaRunnableTool<Input = any> = BetaClientRunnableToolType & {
63
+ /**
64
+ * Runs on the event loop: a body that blocks synchronously stalls the runner
65
+ * and, in an `EnvironmentWorker`, the lease heartbeat. Await async work or
66
+ * move it to a worker thread.
67
+ */
74
68
  run: (
75
69
  args: Input,
76
70
  context?: BetaToolRunContext,
77
71
  ) => Promisable<string | Array<BetaToolResultContentBlockParam>>;
78
72
  parse: (content: unknown) => Input;
79
- /**
80
- * Optional cleanup hook for tools that hold process-level resources (e.g. a
81
- * persistent shell). `SessionToolRunner` (`client.beta.sessions.events.toolRunner`)
82
- * calls it once when iteration ends.
83
- */
73
+ /** Called once by the session runner when it stops. Release held resources here (e.g. a shell). */
84
74
  close?: () => Promisable<void>;
85
75
  };
86
76
 
87
- /**
88
- * Resolve the registry key for a tool — the name the model addresses it by.
89
- * MCP toolsets are keyed on `mcp_server_name`; every other tool on `name`.
90
- * Shared so the tool-name lookup is identical across `toolRunner()` surfaces.
91
- */
77
+ /** The name the model calls a tool by: `mcp_server_name` for MCP toolsets, `name` for everything else. */
92
78
  export function toolName(tool: BetaToolUnion | BetaRunnableTool): string {
93
79
  return 'name' in tool ? tool.name : tool.mcp_server_name;
94
80
  }
95
81
 
96
- /**
97
- * Format a thrown value into tool-result content: a {@link ToolError} carries
98
- * its own structured content, anything else becomes an `Error: <message>`
99
- * string. Shared so every `toolRunner()` surface reports tool failures the
100
- * same way to the model.
101
- */
82
+ /** Tool-result content for a thrown value: a {@link ToolError}'s own content, otherwise `Error: <message>`. */
102
83
  export function toolErrorContent(e: unknown): string | Array<BetaToolResultContentBlockParam> {
103
84
  return e instanceof ToolError ? e.content : `Error: ${e instanceof Error ? e.message : String(e)}`;
104
85
  }
105
86
 
106
- /** Outcome of {@link runRunnableTool}: the content to post back and whether it is an error. */
87
+ /** What a tool run produced, and whether it failed. */
107
88
  export interface RunnableToolOutcome {
108
89
  content: string | Array<BetaToolResultContentBlockParam>;
109
90
  isError: boolean;
110
91
  }
111
92
 
112
- /**
113
- * Run a {@link BetaRunnableTool} end-to-end: parse the raw input, invoke `run`,
114
- * and format any thrown value via {@link toolErrorContent}. Shared so the
115
- * parse → run → catch → format pipeline is identical across `toolRunner()`
116
- * surfaces.
117
- */
93
+ /** Parse the input, run the tool, and turn anything thrown into an error result. */
118
94
  export async function runRunnableTool(
119
95
  tool: BetaRunnableTool,
120
96
  rawInput: unknown,
@@ -222,6 +222,17 @@ export class BetaToolRunner<Stream extends boolean> {
222
222
  const message = await this.#message;
223
223
  this.#state.params.messages.push({ role: message.role, content: message.content });
224
224
 
225
+ // Container-bound server tools reject a follow-up request that omits the container the
226
+ // previous turn ran in, so carry its id forward unless the caller pinned one themselves.
227
+ const { container } = this.#state.params;
228
+ if (message.container) {
229
+ if (container == null) {
230
+ this.#state.params.container = message.container.id;
231
+ } else if (typeof container === 'object' && container.id == null) {
232
+ this.#state.params.container = { ...container, id: message.container.id };
233
+ }
234
+ }
235
+
225
236
  // Refusal-terminated turns are terminal: the refusal may have cut a tool_use off
226
237
  // with partial input, so executing this turn's tools would fire side effects the
227
238
  // model never confirmed — and once middleware strips the refusal turn, their
@@ -567,18 +578,6 @@ function applyToolChange(block: BetaContentBlockParam, available: Set<string>):
567
578
  case 'tool_addition':
568
579
  applyToolReference(block, available);
569
580
  break;
570
- case 'mid_conv_system':
571
- // A `mid_conv_system` block's content is limited by the API schema to
572
- // text / tool_addition / tool_removal, so we walk exactly one level — no recursion.
573
- for (const inner of block.content) {
574
- if (inner.type === 'tool_removal' || inner.type === 'tool_addition') {
575
- applyToolReference(inner, available);
576
- }
577
- }
578
- break;
579
- default:
580
- // Other and unknown/newer block types leave the set untouched (forward compatibility).
581
- break;
582
581
  }
583
582
  }
584
583