@anthropic-ai/sdk 0.121.0 → 0.122.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 (153) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +1 -1
  3. package/core/middleware.d.mts.map +1 -1
  4. package/core/middleware.d.ts.map +1 -1
  5. package/core/middleware.js +4 -4
  6. package/core/middleware.js.map +1 -1
  7. package/core/middleware.mjs +4 -4
  8. package/core/middleware.mjs.map +1 -1
  9. package/internal/errors.d.mts.map +1 -1
  10. package/internal/errors.d.ts.map +1 -1
  11. package/internal/errors.js +3 -1
  12. package/internal/errors.js.map +1 -1
  13. package/internal/errors.mjs +3 -1
  14. package/internal/errors.mjs.map +1 -1
  15. package/internal/parse.js +1 -1
  16. package/internal/parse.js.map +1 -1
  17. package/internal/parse.mjs +1 -1
  18. package/internal/parse.mjs.map +1 -1
  19. package/internal/uploads.js +10 -6
  20. package/internal/uploads.js.map +1 -1
  21. package/internal/uploads.mjs +10 -6
  22. package/internal/uploads.mjs.map +1 -1
  23. package/lib/sessions/accumulate.d.mts +3 -2
  24. package/lib/sessions/accumulate.d.mts.map +1 -1
  25. package/lib/sessions/accumulate.d.ts +3 -2
  26. package/lib/sessions/accumulate.d.ts.map +1 -1
  27. package/lib/sessions/accumulate.js +3 -45
  28. package/lib/sessions/accumulate.js.map +1 -1
  29. package/lib/sessions/accumulate.mjs +3 -45
  30. package/lib/sessions/accumulate.mjs.map +1 -1
  31. package/package.json +1 -1
  32. package/resources/beta/beta.d.mts +6 -6
  33. package/resources/beta/beta.d.mts.map +1 -1
  34. package/resources/beta/beta.d.ts +6 -6
  35. package/resources/beta/beta.d.ts.map +1 -1
  36. package/resources/beta/beta.js.map +1 -1
  37. package/resources/beta/beta.mjs.map +1 -1
  38. package/resources/beta/files.d.mts +25 -5
  39. package/resources/beta/files.d.mts.map +1 -1
  40. package/resources/beta/files.d.ts +25 -5
  41. package/resources/beta/files.d.ts.map +1 -1
  42. package/resources/beta/files.js +6 -6
  43. package/resources/beta/files.js.map +1 -1
  44. package/resources/beta/files.mjs +7 -7
  45. package/resources/beta/files.mjs.map +1 -1
  46. package/resources/beta/index.d.mts +3 -3
  47. package/resources/beta/index.d.mts.map +1 -1
  48. package/resources/beta/index.d.ts +3 -3
  49. package/resources/beta/index.d.ts.map +1 -1
  50. package/resources/beta/index.js.map +1 -1
  51. package/resources/beta/index.mjs.map +1 -1
  52. package/resources/beta/messages/index.d.mts +1 -1
  53. package/resources/beta/messages/index.d.mts.map +1 -1
  54. package/resources/beta/messages/index.d.ts +1 -1
  55. package/resources/beta/messages/index.d.ts.map +1 -1
  56. package/resources/beta/messages/messages.d.mts +19 -19
  57. package/resources/beta/messages/messages.d.mts.map +1 -1
  58. package/resources/beta/messages/messages.d.ts +19 -19
  59. package/resources/beta/messages/messages.d.ts.map +1 -1
  60. package/resources/beta/organization/rate-limits.d.mts +2 -3
  61. package/resources/beta/organization/rate-limits.d.mts.map +1 -1
  62. package/resources/beta/organization/rate-limits.d.ts +2 -3
  63. package/resources/beta/organization/rate-limits.d.ts.map +1 -1
  64. package/resources/beta/organization/rate-limits.js +2 -3
  65. package/resources/beta/organization/rate-limits.js.map +1 -1
  66. package/resources/beta/organization/rate-limits.mjs +2 -3
  67. package/resources/beta/organization/rate-limits.mjs.map +1 -1
  68. package/resources/beta/organization/workspaces/rate-limits.d.mts +2 -3
  69. package/resources/beta/organization/workspaces/rate-limits.d.mts.map +1 -1
  70. package/resources/beta/organization/workspaces/rate-limits.d.ts +2 -3
  71. package/resources/beta/organization/workspaces/rate-limits.d.ts.map +1 -1
  72. package/resources/beta/organization/workspaces/rate-limits.js +2 -3
  73. package/resources/beta/organization/workspaces/rate-limits.js.map +1 -1
  74. package/resources/beta/organization/workspaces/rate-limits.mjs +2 -3
  75. package/resources/beta/organization/workspaces/rate-limits.mjs.map +1 -1
  76. package/resources/beta/skills/index.d.mts +2 -2
  77. package/resources/beta/skills/index.d.mts.map +1 -1
  78. package/resources/beta/skills/index.d.ts +2 -2
  79. package/resources/beta/skills/index.d.ts.map +1 -1
  80. package/resources/beta/skills/index.js.map +1 -1
  81. package/resources/beta/skills/index.mjs.map +1 -1
  82. package/resources/beta/skills/skills.d.mts +48 -120
  83. package/resources/beta/skills/skills.d.mts.map +1 -1
  84. package/resources/beta/skills/skills.d.ts +48 -120
  85. package/resources/beta/skills/skills.d.ts.map +1 -1
  86. package/resources/beta/skills/skills.js +12 -8
  87. package/resources/beta/skills/skills.js.map +1 -1
  88. package/resources/beta/skills/skills.mjs +12 -8
  89. package/resources/beta/skills/skills.mjs.map +1 -1
  90. package/resources/beta/skills/versions.d.mts +35 -138
  91. package/resources/beta/skills/versions.d.mts.map +1 -1
  92. package/resources/beta/skills/versions.d.ts +35 -138
  93. package/resources/beta/skills/versions.d.ts.map +1 -1
  94. package/resources/beta/skills/versions.js +18 -18
  95. package/resources/beta/skills/versions.js.map +1 -1
  96. package/resources/beta/skills/versions.mjs +18 -18
  97. package/resources/beta/skills/versions.mjs.map +1 -1
  98. package/resources/beta/webhooks.d.mts +16 -17
  99. package/resources/beta/webhooks.d.mts.map +1 -1
  100. package/resources/beta/webhooks.d.ts +16 -17
  101. package/resources/beta/webhooks.d.ts.map +1 -1
  102. package/resources/beta/webhooks.js +21 -8
  103. package/resources/beta/webhooks.js.map +1 -1
  104. package/resources/beta/webhooks.mjs +21 -8
  105. package/resources/beta/webhooks.mjs.map +1 -1
  106. package/src/core/middleware.ts +4 -3
  107. package/src/internal/errors.ts +3 -1
  108. package/src/internal/parse.ts +1 -1
  109. package/src/internal/uploads.ts +10 -7
  110. package/src/lib/sessions/accumulate.ts +7 -47
  111. package/src/resources/beta/beta.ts +12 -14
  112. package/src/resources/beta/files.ts +34 -11
  113. package/src/resources/beta/index.ts +6 -7
  114. package/src/resources/beta/messages/index.ts +1 -1
  115. package/src/resources/beta/messages/messages.ts +22 -22
  116. package/src/resources/beta/organization/rate-limits.ts +2 -3
  117. package/src/resources/beta/organization/workspaces/rate-limits.ts +2 -3
  118. package/src/resources/beta/skills/index.ts +7 -10
  119. package/src/resources/beta/skills/skills.ts +60 -150
  120. package/src/resources/beta/skills/versions.ts +43 -166
  121. package/src/resources/beta/webhooks.ts +25 -29
  122. package/src/tools/agent-toolset/node.browser.ts +0 -4
  123. package/src/tools/agent-toolset/node.ts +118 -13
  124. package/src/tools/agent-toolset/skills.ts +5 -33
  125. package/src/version.ts +1 -1
  126. package/tools/agent-toolset/node.browser.d.mts +0 -1
  127. package/tools/agent-toolset/node.browser.d.mts.map +1 -1
  128. package/tools/agent-toolset/node.browser.d.ts +0 -1
  129. package/tools/agent-toolset/node.browser.d.ts.map +1 -1
  130. package/tools/agent-toolset/node.browser.js +0 -4
  131. package/tools/agent-toolset/node.browser.js.map +1 -1
  132. package/tools/agent-toolset/node.browser.mjs +0 -3
  133. package/tools/agent-toolset/node.browser.mjs.map +1 -1
  134. package/tools/agent-toolset/node.d.mts +9 -7
  135. package/tools/agent-toolset/node.d.mts.map +1 -1
  136. package/tools/agent-toolset/node.d.ts +9 -7
  137. package/tools/agent-toolset/node.d.ts.map +1 -1
  138. package/tools/agent-toolset/node.js +96 -8
  139. package/tools/agent-toolset/node.js.map +1 -1
  140. package/tools/agent-toolset/node.mjs +96 -7
  141. package/tools/agent-toolset/node.mjs.map +1 -1
  142. package/tools/agent-toolset/skills.d.mts +0 -8
  143. package/tools/agent-toolset/skills.d.mts.map +1 -1
  144. package/tools/agent-toolset/skills.d.ts +0 -8
  145. package/tools/agent-toolset/skills.d.ts.map +1 -1
  146. package/tools/agent-toolset/skills.js +5 -25
  147. package/tools/agent-toolset/skills.js.map +1 -1
  148. package/tools/agent-toolset/skills.mjs +5 -24
  149. package/tools/agent-toolset/skills.mjs.map +1 -1
  150. package/version.d.mts +1 -1
  151. package/version.d.ts +1 -1
  152. package/version.js +1 -1
  153. package/version.mjs +1 -1
@@ -16,17 +16,17 @@ export class Versions extends APIResource {
16
16
  *
17
17
  * @example
18
18
  * ```ts
19
- * const version = await client.beta.skills.versions.create(
20
- * 'skill_id',
21
- * { files: [fs.createReadStream('path/to/file')] },
22
- * );
19
+ * const betaSkillVersion =
20
+ * await client.beta.skills.versions.create('skill_id', {
21
+ * files: [fs.createReadStream('path/to/file')],
22
+ * });
23
23
  * ```
24
24
  */
25
25
  create(
26
26
  skillID: string,
27
27
  params: VersionCreateParams,
28
28
  options?: RequestOptions,
29
- ): APIPromise<VersionCreateResponse> {
29
+ ): APIPromise<BetaSkillVersion> {
30
30
  const { betas, ...body } = params;
31
31
  return this._client.post(
32
32
  path`/v1/skills/${skillID}/versions?beta=true`,
@@ -35,7 +35,7 @@ export class Versions extends APIResource {
35
35
  body,
36
36
  ...options,
37
37
  headers: buildHeaders([
38
- { 'anthropic-beta': [...(betas ?? []), 'skills-2025-10-02'].toString() },
38
+ { ...(betas?.toString() != null ? { 'anthropic-beta': betas?.toString() } : undefined) },
39
39
  options?.headers,
40
40
  ]),
41
41
  },
@@ -50,22 +50,22 @@ export class Versions extends APIResource {
50
50
  *
51
51
  * @example
52
52
  * ```ts
53
- * const version = await client.beta.skills.versions.retrieve(
54
- * 'version',
55
- * { skill_id: 'skill_id' },
56
- * );
53
+ * const betaSkillVersion =
54
+ * await client.beta.skills.versions.retrieve('version', {
55
+ * skill_id: 'skill_id',
56
+ * });
57
57
  * ```
58
58
  */
59
59
  retrieve(
60
60
  version: string,
61
61
  params: VersionRetrieveParams,
62
62
  options?: RequestOptions,
63
- ): APIPromise<VersionRetrieveResponse> {
63
+ ): APIPromise<BetaSkillVersion> {
64
64
  const { skill_id, betas } = params;
65
65
  return this._client.get(path`/v1/skills/${skill_id}/versions/${version}?beta=true`, {
66
66
  ...options,
67
67
  headers: buildHeaders([
68
- { 'anthropic-beta': [...(betas ?? []), 'skills-2025-10-02'].toString() },
68
+ { ...(betas?.toString() != null ? { 'anthropic-beta': betas?.toString() } : undefined) },
69
69
  options?.headers,
70
70
  ]),
71
71
  });
@@ -77,7 +77,7 @@ export class Versions extends APIResource {
77
77
  * @example
78
78
  * ```ts
79
79
  * // Automatically fetches more pages as needed.
80
- * for await (const versionListResponse of client.beta.skills.versions.list(
80
+ * for await (const betaSkillVersion of client.beta.skills.versions.list(
81
81
  * 'skill_id',
82
82
  * )) {
83
83
  * // ...
@@ -88,16 +88,16 @@ export class Versions extends APIResource {
88
88
  skillID: string,
89
89
  params: VersionListParams | null | undefined = {},
90
90
  options?: RequestOptions,
91
- ): PagePromise<VersionListResponsesPageCursor, VersionListResponse> {
91
+ ): PagePromise<BetaSkillVersionsPageCursor, BetaSkillVersion> {
92
92
  const { betas, ...query } = params ?? {};
93
93
  return this._client.getAPIList(
94
94
  path`/v1/skills/${skillID}/versions?beta=true`,
95
- PageCursor<VersionListResponse>,
95
+ PageCursor<BetaSkillVersion>,
96
96
  {
97
97
  query,
98
98
  ...options,
99
99
  headers: buildHeaders([
100
- { 'anthropic-beta': [...(betas ?? []), 'skills-2025-10-02'].toString() },
100
+ { ...(betas?.toString() != null ? { 'anthropic-beta': betas?.toString() } : undefined) },
101
101
  options?.headers,
102
102
  ]),
103
103
  },
@@ -109,22 +109,22 @@ export class Versions extends APIResource {
109
109
  *
110
110
  * @example
111
111
  * ```ts
112
- * const version = await client.beta.skills.versions.delete(
113
- * 'version',
114
- * { skill_id: 'skill_id' },
115
- * );
112
+ * const betaDeletedSkillVersion =
113
+ * await client.beta.skills.versions.delete('version', {
114
+ * skill_id: 'skill_id',
115
+ * });
116
116
  * ```
117
117
  */
118
118
  delete(
119
119
  version: string,
120
120
  params: VersionDeleteParams,
121
121
  options?: RequestOptions,
122
- ): APIPromise<VersionDeleteResponse> {
122
+ ): APIPromise<BetaDeletedSkillVersion> {
123
123
  const { skill_id, betas } = params;
124
124
  return this._client.delete(path`/v1/skills/${skill_id}/versions/${version}?beta=true`, {
125
125
  ...options,
126
126
  headers: buildHeaders([
127
- { 'anthropic-beta': [...(betas ?? []), 'skills-2025-10-02'].toString() },
127
+ { ...(betas?.toString() != null ? { 'anthropic-beta': betas?.toString() } : undefined) },
128
128
  options?.headers,
129
129
  ]),
130
130
  });
@@ -150,8 +150,8 @@ export class Versions extends APIResource {
150
150
  ...options,
151
151
  headers: buildHeaders([
152
152
  {
153
- 'anthropic-beta': [...(betas ?? []), 'skills-2025-10-02'].toString(),
154
153
  Accept: 'application/binary',
154
+ ...(betas?.toString() != null ? { 'anthropic-beta': betas?.toString() } : undefined),
155
155
  },
156
156
  options?.headers,
157
157
  ]),
@@ -160,72 +160,32 @@ export class Versions extends APIResource {
160
160
  }
161
161
  }
162
162
 
163
- export type VersionListResponsesPageCursor = PageCursor<VersionListResponse>;
163
+ export type BetaSkillVersionsPageCursor = PageCursor<BetaSkillVersion>;
164
164
 
165
- export interface VersionCreateResponse {
165
+ export interface BetaDeletedSkillVersion {
166
166
  /**
167
- * Unique identifier for the skill version.
168
- *
169
- * The format and length of IDs may change over time.
167
+ * Unique identifier for this Skill Version. The id addresses the version in paths
168
+ * and pins it in references.
170
169
  */
171
170
  id: string;
172
171
 
173
172
  /**
174
- * ISO 8601 timestamp of when the skill version was created.
175
- */
176
- created_at: string;
177
-
178
- /**
179
- * Description of the skill version.
180
- *
181
- * This is extracted from the SKILL.md file in the skill upload.
182
- */
183
- description: string;
184
-
185
- /**
186
- * Directory name of the skill version.
187
- *
188
- * This is the top-level directory name that was extracted from the uploaded files.
189
- */
190
- directory: string;
191
-
192
- /**
193
- * Human-readable name of the skill version.
194
- *
195
- * This is extracted from the SKILL.md file in the skill upload.
196
- */
197
- name: string;
198
-
199
- /**
200
- * Identifier for the skill that this version belongs to.
201
- */
202
- skill_id: string;
203
-
204
- /**
205
- * Object type.
206
- *
207
- * For Skill Versions, this is always `"skill_version"`.
208
- */
209
- type: string;
210
-
211
- /**
212
- * Version identifier for the skill.
173
+ * Deleted object type.
213
174
  *
214
- * Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129").
175
+ * For Skill Versions, this is always `"skill_version_deleted"`.
215
176
  */
216
- version: string;
177
+ type: 'skill_version_deleted';
217
178
  }
218
179
 
219
- export interface VersionRetrieveResponse {
180
+ export interface BetaSkillVersion {
220
181
  /**
221
- * Unique identifier for the skill version.
222
- *
223
- * The format and length of IDs may change over time.
182
+ * Unique identifier for this Skill Version. The id addresses the version in paths
183
+ * and pins it in references.
224
184
  */
225
185
  id: string;
226
186
 
227
187
  /**
228
- * ISO 8601 timestamp of when the skill version was created.
188
+ * ISO 8601 timestamp of when the skill was created.
229
189
  */
230
190
  created_at: string;
231
191
 
@@ -237,76 +197,18 @@ export interface VersionRetrieveResponse {
237
197
  description: string;
238
198
 
239
199
  /**
240
- * Directory name of the skill version.
241
- *
242
- * This is the top-level directory name that was extracted from the uploaded files.
243
- */
244
- directory: string;
245
-
246
- /**
247
- * Human-readable name of the skill version.
248
- *
249
- * This is extracted from the SKILL.md file in the skill upload.
200
+ * The Skill's immutable kebab-case slug, set at creation from the first upload's
201
+ * SKILL.md frontmatter `name` (or its enclosing directory). Every later upload
202
+ * must resolve to the same value. Also the top-level directory of the Skill's
203
+ * mounted files and the base name of a downloaded archive.
250
204
  */
251
205
  name: string;
252
206
 
253
207
  /**
254
- * Identifier for the skill that this version belongs to.
255
- */
256
- skill_id: string;
257
-
258
- /**
259
- * Object type.
260
- *
261
- * For Skill Versions, this is always `"skill_version"`.
262
- */
263
- type: string;
264
-
265
- /**
266
- * Version identifier for the skill.
267
- *
268
- * Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129").
269
- */
270
- version: string;
271
- }
272
-
273
- export interface VersionListResponse {
274
- /**
275
- * Unique identifier for the skill version.
208
+ * Unique identifier for the skill.
276
209
  *
277
210
  * The format and length of IDs may change over time.
278
211
  */
279
- id: string;
280
-
281
- /**
282
- * ISO 8601 timestamp of when the skill version was created.
283
- */
284
- created_at: string;
285
-
286
- /**
287
- * Description of the skill version.
288
- *
289
- * This is extracted from the SKILL.md file in the skill upload.
290
- */
291
- description: string;
292
-
293
- /**
294
- * Directory name of the skill version.
295
- *
296
- * This is the top-level directory name that was extracted from the uploaded files.
297
- */
298
- directory: string;
299
-
300
- /**
301
- * Human-readable name of the skill version.
302
- *
303
- * This is extracted from the SKILL.md file in the skill upload.
304
- */
305
- name: string;
306
-
307
- /**
308
- * Identifier for the skill that this version belongs to.
309
- */
310
212
  skill_id: string;
311
213
 
312
214
  /**
@@ -314,30 +216,7 @@ export interface VersionListResponse {
314
216
  *
315
217
  * For Skill Versions, this is always `"skill_version"`.
316
218
  */
317
- type: string;
318
-
319
- /**
320
- * Version identifier for the skill.
321
- *
322
- * Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129").
323
- */
324
- version: string;
325
- }
326
-
327
- export interface VersionDeleteResponse {
328
- /**
329
- * Version identifier for the skill.
330
- *
331
- * Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129").
332
- */
333
- id: string;
334
-
335
- /**
336
- * Deleted object type.
337
- *
338
- * For Skill Versions, this is always `"skill_version_deleted"`.
339
- */
340
- type: string;
219
+ type: 'skill_version';
341
220
  }
342
221
 
343
222
  export interface VersionCreateParams {
@@ -406,11 +285,9 @@ export interface VersionDownloadParams {
406
285
 
407
286
  export declare namespace Versions {
408
287
  export {
409
- type VersionCreateResponse as VersionCreateResponse,
410
- type VersionRetrieveResponse as VersionRetrieveResponse,
411
- type VersionListResponse as VersionListResponse,
412
- type VersionDeleteResponse as VersionDeleteResponse,
413
- type VersionListResponsesPageCursor as VersionListResponsesPageCursor,
288
+ type BetaDeletedSkillVersion as BetaDeletedSkillVersion,
289
+ type BetaSkillVersion as BetaSkillVersion,
290
+ type BetaSkillVersionsPageCursor as BetaSkillVersionsPageCursor,
414
291
  type VersionCreateParams as VersionCreateParams,
415
292
  type VersionRetrieveParams as VersionRetrieveParams,
416
293
  type VersionListParams as VersionListParams,
@@ -4,17 +4,27 @@ import { APIResource } from '../../core/resource';
4
4
  import { Webhook } from 'standardwebhooks';
5
5
 
6
6
  export class Webhooks extends APIResource {
7
- unwrap(
8
- body: string,
9
- { headers, key }: { headers: Record<string, string>; key?: string },
10
- ): UnwrapWebhookEvent {
11
- if (headers !== undefined) {
12
- const keyStr: string | null = key === undefined ? this._client.webhookKey : key;
13
- if (keyStr === null) throw new Error('Webhook key must not be null in order to unwrap');
14
- const wh = new Webhook(keyStr);
15
- wh.verify(body, headers);
16
- }
17
- return JSON.parse(body) as UnwrapWebhookEvent;
7
+ /**
8
+ * Parses a webhook payload into an event without verifying its signature. Prefer
9
+ * `unwrap()` unless you have already verified the signature yourself.
10
+ */
11
+ parseUnverified(body: string): BetaWebhookEvent {
12
+ return JSON.parse(body) as BetaWebhookEvent;
13
+ }
14
+
15
+ /**
16
+ * Verifies the webhook signature from the `webhook-id`, `webhook-timestamp` and
17
+ * `webhook-signature` headers using your webhook signing key, then parses the
18
+ * payload into an event. Fails if the signature is missing or invalid.
19
+ */
20
+ unwrap(body: string, options: { headers: Record<string, string>; key?: string }): BetaWebhookEvent {
21
+ const headers = options?.headers;
22
+ if (headers == null) throw new Error('Webhook headers are required in order to verify the signature');
23
+ const keyStr: string | null = options.key === undefined ? this._client.webhookKey : options.key;
24
+ if (!keyStr) throw new Error('Webhook key must not be null or empty in order to unwrap');
25
+ const wh = new Webhook(keyStr);
26
+ wh.verify(body, headers);
27
+ return JSON.parse(body) as BetaWebhookEvent;
18
28
  }
19
29
  }
20
30
 
@@ -690,24 +700,10 @@ export interface BetaWebhookVaultDeletedEventData {
690
700
  workspace_id: string;
691
701
  }
692
702
 
693
- export interface UnwrapWebhookEvent {
694
- /**
695
- * Unique event identifier for idempotency.
696
- */
697
- id: string;
698
-
699
- /**
700
- * RFC 3339 timestamp when the event occurred.
701
- */
702
- created_at: string;
703
-
704
- data: BetaWebhookEventData;
705
-
706
- /**
707
- * Object type. Always `event` for webhook payloads.
708
- */
709
- type: 'event';
710
- }
703
+ /**
704
+ * @deprecated UnwrapWebhookEvent has been renamed to BetaWebhookEvent
705
+ */
706
+ export type UnwrapWebhookEvent = BetaWebhookEvent;
711
707
 
712
708
  export declare namespace Webhooks {
713
709
  export {
@@ -96,10 +96,6 @@ export function setupSkills(_ctx: AgentToolContext): Promise<() => Promise<void>
96
96
  return nodeOnly('setupSkills');
97
97
  }
98
98
 
99
- export function resolveSkillVersion(_client: Anthropic, _skillId: string, _version: string): Promise<string> {
100
- return nodeOnly('resolveSkillVersion');
101
- }
102
-
103
99
  export function extractSkillArchive(_resp: Response, _dest: string): Promise<void> {
104
100
  return nodeOnly('extractSkillArchive');
105
101
  }
@@ -52,7 +52,7 @@ import {
52
52
  isWithin,
53
53
  } from './fs-util';
54
54
 
55
- export { setupSkills, resolveSkillVersion, extractSkillArchive } from './skills';
55
+ export { setupSkills, extractSkillArchive } from './skills';
56
56
  export {
57
57
  SessionMemoryStores,
58
58
  SessionMemoryError,
@@ -70,6 +70,8 @@ const BASH_DEFAULT_TIMEOUT_MS = 120_000;
70
70
  // memory) when AgentToolContext.maxFileBytes is unset. The reject-vs-truncate
71
71
  // behaviour remains a separate question pending CMA validation.
72
72
  const DEFAULT_MAX_FILE_BYTES = 256 * 1024;
73
+ const READ_STREAM_CHUNK_BYTES = 64 * 1024;
74
+ const NEWLINE = Buffer.from('\n');
73
75
  const GREP_OUTPUT_LIMIT = 100 * 1024;
74
76
  const GREP_MAX_LINE_LENGTH = 2000;
75
77
  const GLOB_RESULT_LIMIT = 200;
@@ -194,12 +196,14 @@ export interface AgentToolContext {
194
196
  */
195
197
  env?: Record<string, string | undefined>;
196
198
  /**
197
- * Size cap for the `read` and `edit` tools, which both load the whole file into
198
- * memory. `undefined` (default) uses the built-in 256 KiB cap; a positive number
199
- * sets a custom cap; `null` disables the cap entirely. Disabling it reintroduces
200
- * the OOM risk on a model-controlled path, so pass `null` only when the sandbox
201
- * can absorb arbitrarily large files. The non-regular-file (FIFO/device) guard
202
- * always applies regardless of this value.
199
+ * Size cap for a whole-file `read` and for `edit`, which both load the whole
200
+ * file into memory. A `read` with `view_range` on a larger file streams it and
201
+ * applies the cap to the selected lines instead. `undefined` (default) uses the
202
+ * built-in 256 KiB cap; a positive number sets a custom cap; `null` disables the
203
+ * cap entirely. Disabling it reintroduces the OOM risk on a model-controlled
204
+ * path, so pass `null` only when the sandbox can absorb arbitrarily large files.
205
+ * The non-regular-file (FIFO/device) guard always applies regardless of this
206
+ * value.
203
207
  */
204
208
  maxFileBytes?: number | null;
205
209
  }
@@ -525,6 +529,9 @@ export function betaReadTool(ctx: AgentToolContext): BetaRunnableTool {
525
529
  run: async ({ file_path, view_range }) => {
526
530
  if (!file_path) throw new ToolError('read: file_path is required');
527
531
  const abs = await resolvePath(ctx, file_path);
532
+ if (view_range?.length && view_range.length !== 2) {
533
+ throw new ToolError('read: view_range must be [start_line, end_line]');
534
+ }
528
535
  let data: string;
529
536
  try {
530
537
  // stat() before any open(): the size cap stops a multi-GB file from
@@ -536,10 +543,14 @@ export function betaReadTool(ctx: AgentToolContext): BetaRunnableTool {
536
543
  }
537
544
  const limit = resolveMaxBytes(ctx.maxFileBytes);
538
545
  if (limit !== null && st.size > limit) {
539
- throw new ToolError(
540
- `read: ${file_path} is ${st.size} bytes, exceeds ${limit}-byte limit. ` +
541
- 'Use bash (head/tail/sed) to read a slice.',
542
- );
546
+ if (!view_range?.length) {
547
+ throw new ToolError(
548
+ `read: ${file_path} is ${st.size} bytes, exceeds ${limit}-byte limit. ` +
549
+ 'Use the view_range parameter to read specific line ranges, e.g. view_range: [1, 500].',
550
+ );
551
+ }
552
+ const [startLine, endLine] = view_range as [number, number];
553
+ return await readRangeStreaming(abs, file_path, startLine, endLine, limit);
543
554
  }
544
555
  data = await fs.readFile(abs, 'utf8');
545
556
  } catch (e) {
@@ -547,7 +558,6 @@ export function betaReadTool(ctx: AgentToolContext): BetaRunnableTool {
547
558
  throw new ToolError(`read: ${fsErrorMessage(e, file_path)}`);
548
559
  }
549
560
  if (!view_range?.length) return data;
550
- if (view_range.length !== 2) throw new ToolError('read: view_range must be [start_line, end_line]');
551
561
  const [startLine, endLine] = view_range as [number, number];
552
562
  const lines = data.split('\n');
553
563
  const start = Math.max(0, startLine - 1);
@@ -557,6 +567,101 @@ export function betaReadTool(ctx: AgentToolContext): BetaRunnableTool {
557
567
  });
558
568
  }
559
569
 
570
+ /** Returns lines `[startLine, endLine]` of the file at `abs`, capping the selected bytes at `limit`. */
571
+ async function readRangeStreaming(
572
+ abs: string,
573
+ filePath: string,
574
+ startLine: number,
575
+ endLine: number,
576
+ limit: number,
577
+ ): Promise<string> {
578
+ const lines = new LineRangeCollector(filePath, startLine, endLine, limit);
579
+ if (lines.rangeIsEmpty()) return '';
580
+ // Byte chunks rather than readline: a single huge line must never be buffered
581
+ // whole, so memory stays bounded by `limit` plus one chunk.
582
+ const stream = fssync.createReadStream(abs, { highWaterMark: READ_STREAM_CHUNK_BYTES });
583
+ try {
584
+ for await (const chunk of stream as AsyncIterable<Buffer>) {
585
+ lines.collectFrom(chunk);
586
+ if (lines.rangeIsCollected()) break;
587
+ }
588
+ } finally {
589
+ stream.destroy();
590
+ }
591
+ return lines.text();
592
+ }
593
+
594
+ /** Collects the bytes of lines `[startLine, endLine]` from consecutive file chunks, capped at `limit`. */
595
+ class LineRangeCollector {
596
+ readonly #filePath: string;
597
+ readonly #startLine: number;
598
+ readonly #endLine: number;
599
+ readonly #start: number;
600
+ readonly #end: number;
601
+ readonly #limit: number;
602
+ #line = 0;
603
+ #collected: Buffer[] = [];
604
+ #collectedBytes = 0;
605
+
606
+ constructor(filePath: string, startLine: number, endLine: number, limit: number) {
607
+ this.#filePath = filePath;
608
+ this.#startLine = startLine;
609
+ this.#endLine = endLine;
610
+ this.#start = Math.max(0, startLine - 1);
611
+ this.#end = endLine > 0 ? endLine : Infinity;
612
+ this.#limit = limit;
613
+ }
614
+
615
+ rangeIsEmpty(): boolean {
616
+ return this.#end <= this.#start;
617
+ }
618
+
619
+ rangeIsCollected(): boolean {
620
+ return this.#line >= this.#end;
621
+ }
622
+
623
+ collectFrom(chunk: Buffer): void {
624
+ let lineStart = 0;
625
+ while (lineStart < chunk.length && !this.rangeIsCollected()) {
626
+ const newline = chunk.indexOf(0x0a, lineStart);
627
+ const lineEnd = newline < 0 ? chunk.length : newline;
628
+ if (this.#line >= this.#start) {
629
+ this.#collect(chunk.subarray(lineStart, lineEnd), newline >= 0);
630
+ }
631
+ if (newline < 0) break;
632
+ this.#line++;
633
+ lineStart = newline + 1;
634
+ }
635
+ }
636
+
637
+ #collect(lineBytes: Buffer, newlineTerminated: boolean): void {
638
+ this.#collected.push(lineBytes);
639
+ this.#collectedBytes += lineBytes.length;
640
+ if (newlineTerminated && this.#line + 1 < this.#end) {
641
+ this.#collected.push(NEWLINE);
642
+ this.#collectedBytes += NEWLINE.length;
643
+ }
644
+ if (this.#collectedBytes > this.#limit) throw this.#overLimitError();
645
+ }
646
+
647
+ #overLimitError(): ToolError {
648
+ if (this.#end - this.#start === 1) {
649
+ return new ToolError(
650
+ `read: line ${this.#start + 1} of ${this.#filePath} alone exceeds ${this.#limit}-byte limit. ` +
651
+ 'The read tool cannot return part of a line, so view_range cannot narrow this further.',
652
+ );
653
+ }
654
+ return new ToolError(
655
+ `read: view_range [${this.#startLine}, ${this.#endLine}] of ${this.#filePath} exceeds ${this.#limit}-byte limit. ` +
656
+ 'Narrow the view_range to read a smaller portion.',
657
+ );
658
+ }
659
+
660
+ text(): string {
661
+ return Buffer.concat(this.#collected, this.#collectedBytes).toString('utf8');
662
+ }
663
+ }
664
+
560
665
  export function betaWriteTool(ctx: AgentToolContext): BetaRunnableTool {
561
666
  rejectUnrestrictedPaths(ctx.unrestrictedPaths);
562
667
  return betaTool({
@@ -624,7 +729,7 @@ export function betaEditTool(ctx: AgentToolContext): BetaRunnableTool {
624
729
  if (limit !== null && st.size > limit) {
625
730
  throw new ToolError(
626
731
  `edit: ${file_path} is ${st.size} bytes, exceeds ${limit}-byte limit. ` +
627
- 'Use bash (sed/awk) to edit a large file.',
732
+ 'The edit tool loads the whole file and cannot modify a file this large.',
628
733
  );
629
734
  }
630
735
  data = await fs.readFile(abs, 'utf8');
@@ -12,7 +12,6 @@ import { execFile } from 'node:child_process';
12
12
  import { promisify } from 'node:util';
13
13
  import { Readable } from 'node:stream';
14
14
  import { pipeline } from 'node:stream/promises';
15
- import type { Anthropic } from '../../client';
16
15
  import { AnthropicError } from '../../core/error';
17
16
  import { loggerFor } from '../../internal/utils/log';
18
17
  import { DIR_CREATE_MODE, errnoCode } from './fs-util';
@@ -64,8 +63,7 @@ export async function setupSkills(ctx: AgentToolContext): Promise<() => Promise<
64
63
  const created: string[] = [];
65
64
  for (const skill of session.agent.skills) {
66
65
  try {
67
- const versionId = await resolveSkillVersion(client, skill.skill_id, skill.version);
68
- const version = await client.beta.skills.versions.retrieve(versionId, { skill_id: skill.skill_id });
66
+ const version = await client.beta.skills.versions.retrieve(skill.version, { skill_id: skill.skill_id });
69
67
  // The directory is the skill's name, reduced to a single safe path
70
68
  // component so a hostile name can't escape `skillsRoot`.
71
69
  let dirname = path.basename(version.name.trim());
@@ -78,7 +76,9 @@ export async function setupSkills(ctx: AgentToolContext): Promise<() => Promise<
78
76
  });
79
77
  continue;
80
78
  }
81
- const resp = await client.beta.skills.versions.download(versionId, { skill_id: skill.skill_id });
79
+ // `skill.version` may be the alias `"latest"`, which only the retrieve
80
+ // endpoint resolves; download by the concrete id it returned.
81
+ const resp = await client.beta.skills.versions.download(version.id, { skill_id: skill.skill_id });
82
82
  await fs.rm(dest, { recursive: true, force: true });
83
83
  await fs.mkdir(dest, { recursive: true, mode: DIR_CREATE_MODE });
84
84
  created.push(dest);
@@ -86,7 +86,7 @@ export async function setupSkills(ctx: AgentToolContext): Promise<() => Promise<
86
86
  log.info('downloaded skill', {
87
87
  component: 'agent-tool-context',
88
88
  skill_id: skill.skill_id,
89
- version: versionId,
89
+ version: version.id,
90
90
  dest,
91
91
  });
92
92
  } catch (e) {
@@ -106,34 +106,6 @@ export async function setupSkills(ctx: AgentToolContext): Promise<() => Promise<
106
106
  };
107
107
  }
108
108
 
109
- /**
110
- * Resolve `version` to the concrete numeric timestamp the
111
- * `/v1/skills/{id}/versions/{version}` endpoints require — `session.agent.skills[].version`
112
- * can be an alias such as `"latest"`, which those endpoints reject. Numeric
113
- * versions pass through unchanged.
114
- */
115
- export async function resolveSkillVersion(
116
- client: Anthropic,
117
- skillId: string,
118
- version: string,
119
- ): Promise<string> {
120
- if (/^\d+$/.test(version)) return version;
121
- let newest: string | undefined;
122
- for await (const v of client.beta.skills.versions.list(skillId)) {
123
- if (/^\d+$/.test(v.version) && (newest === undefined || BigInt(v.version) > BigInt(newest))) {
124
- newest = v.version;
125
- }
126
- }
127
- if (newest === undefined) {
128
- throw new AnthropicError(
129
- `skill ${JSON.stringify(skillId)} has no concrete version to resolve ${JSON.stringify(
130
- version,
131
- )} against`,
132
- );
133
- }
134
- return newest;
135
- }
136
-
137
109
  /** Reject archive members that are absolute or contain a `..` component. */
138
110
  function assertSafeMemberNames(names: string[]): void {
139
111
  for (const raw of names) {
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.121.0'; // x-release-please-version
1
+ export const VERSION = '0.122.0'; // x-release-please-version