langsmith 0.7.10 → 0.7.12

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 (103) hide show
  1. package/README.md +46 -15
  2. package/dist/_openapi_client/client.cjs +0 -80
  3. package/dist/_openapi_client/client.d.ts +2 -32
  4. package/dist/_openapi_client/client.js +0 -80
  5. package/dist/_openapi_client/core/pagination.cjs +2 -2
  6. package/dist/_openapi_client/core/pagination.js +2 -2
  7. package/dist/_openapi_client/internal/utils/query.cjs +1 -1
  8. package/dist/_openapi_client/internal/utils/query.js +1 -1
  9. package/dist/_openapi_client/resources/datasets/datasets.d.ts +52 -4
  10. package/dist/_openapi_client/resources/index.cjs +1 -21
  11. package/dist/_openapi_client/resources/index.d.ts +1 -11
  12. package/dist/_openapi_client/resources/index.js +0 -10
  13. package/dist/_openapi_client/resources/runs/index.cjs +9 -0
  14. package/dist/_openapi_client/resources/runs/index.d.ts +2 -0
  15. package/dist/_openapi_client/resources/runs/index.js +4 -0
  16. package/dist/_openapi_client/resources/runs/runs.cjs +54 -41
  17. package/dist/_openapi_client/resources/runs/runs.d.ts +571 -313
  18. package/dist/_openapi_client/resources/runs/runs.js +54 -41
  19. package/dist/_openapi_client/resources/runs.cjs +19 -0
  20. package/dist/_openapi_client/resources/runs.d.ts +1 -0
  21. package/dist/_openapi_client/resources/runs.js +3 -0
  22. package/dist/anonymizer/index.cjs +142 -0
  23. package/dist/anonymizer/index.d.ts +45 -0
  24. package/dist/anonymizer/index.js +140 -0
  25. package/dist/client.cjs +29 -1
  26. package/dist/client.d.ts +6 -1
  27. package/dist/client.js +28 -1
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/sandbox/client.cjs +5 -8
  32. package/dist/sandbox/client.js +5 -8
  33. package/dist/sandbox/index.cjs +2 -1
  34. package/dist/sandbox/index.d.ts +2 -2
  35. package/dist/sandbox/index.js +1 -1
  36. package/dist/sandbox/mounts.cjs +157 -18
  37. package/dist/sandbox/mounts.d.ts +10 -2
  38. package/dist/sandbox/mounts.js +155 -18
  39. package/dist/sandbox/proxy_config.cjs +19 -44
  40. package/dist/sandbox/proxy_config.d.ts +2 -4
  41. package/dist/sandbox/proxy_config.js +19 -43
  42. package/dist/sandbox/types.d.ts +56 -15
  43. package/dist/utils/constants.cjs +4 -0
  44. package/dist/utils/constants.d.ts +1 -0
  45. package/dist/utils/constants.js +1 -0
  46. package/package.json +5 -5
  47. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.cjs +0 -138
  48. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.d.ts +0 -367
  49. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.js +0 -101
  50. package/dist/_openapi_client/resources/annotation-queues/runs.cjs +0 -46
  51. package/dist/_openapi_client/resources/annotation-queues/runs.d.ts +0 -128
  52. package/dist/_openapi_client/resources/annotation-queues/runs.js +0 -42
  53. package/dist/_openapi_client/resources/commits.cjs +0 -44
  54. package/dist/_openapi_client/resources/commits.d.ts +0 -204
  55. package/dist/_openapi_client/resources/commits.js +0 -40
  56. package/dist/_openapi_client/resources/evaluators.cjs +0 -15
  57. package/dist/_openapi_client/resources/evaluators.d.ts +0 -125
  58. package/dist/_openapi_client/resources/evaluators.js +0 -11
  59. package/dist/_openapi_client/resources/examples/bulk.cjs +0 -24
  60. package/dist/_openapi_client/resources/examples/bulk.d.ts +0 -78
  61. package/dist/_openapi_client/resources/examples/bulk.js +0 -20
  62. package/dist/_openapi_client/resources/examples/examples.cjs +0 -124
  63. package/dist/_openapi_client/resources/examples/examples.d.ts +0 -182
  64. package/dist/_openapi_client/resources/examples/examples.js +0 -87
  65. package/dist/_openapi_client/resources/examples/validate.cjs +0 -21
  66. package/dist/_openapi_client/resources/examples/validate.d.ts +0 -38
  67. package/dist/_openapi_client/resources/examples/validate.js +0 -17
  68. package/dist/_openapi_client/resources/feedback/configs.cjs +0 -24
  69. package/dist/_openapi_client/resources/feedback/configs.d.ts +0 -18
  70. package/dist/_openapi_client/resources/feedback/configs.js +0 -20
  71. package/dist/_openapi_client/resources/feedback/feedback.cjs +0 -98
  72. package/dist/_openapi_client/resources/feedback/feedback.d.ts +0 -275
  73. package/dist/_openapi_client/resources/feedback/feedback.js +0 -61
  74. package/dist/_openapi_client/resources/feedback/tokens.cjs +0 -35
  75. package/dist/_openapi_client/resources/feedback/tokens.d.ts +0 -130
  76. package/dist/_openapi_client/resources/feedback/tokens.js +0 -31
  77. package/dist/_openapi_client/resources/public/datasets.cjs +0 -47
  78. package/dist/_openapi_client/resources/public/datasets.d.ts +0 -152
  79. package/dist/_openapi_client/resources/public/datasets.js +0 -43
  80. package/dist/_openapi_client/resources/public/public.cjs +0 -62
  81. package/dist/_openapi_client/resources/public/public.d.ts +0 -32
  82. package/dist/_openapi_client/resources/public/public.js +0 -25
  83. package/dist/_openapi_client/resources/repos/directories.cjs +0 -40
  84. package/dist/_openapi_client/resources/repos/directories.d.ts +0 -72
  85. package/dist/_openapi_client/resources/repos/directories.js +0 -36
  86. package/dist/_openapi_client/resources/repos/repos.cjs +0 -93
  87. package/dist/_openapi_client/resources/repos/repos.d.ts +0 -188
  88. package/dist/_openapi_client/resources/repos/repos.js +0 -56
  89. package/dist/_openapi_client/resources/sandboxes/boxes.cjs +0 -86
  90. package/dist/_openapi_client/resources/sandboxes/boxes.d.ts +0 -1121
  91. package/dist/_openapi_client/resources/sandboxes/boxes.js +0 -82
  92. package/dist/_openapi_client/resources/sandboxes/sandboxes.cjs +0 -63
  93. package/dist/_openapi_client/resources/sandboxes/sandboxes.d.ts +0 -13
  94. package/dist/_openapi_client/resources/sandboxes/sandboxes.js +0 -26
  95. package/dist/_openapi_client/resources/sandboxes/snapshots.cjs +0 -39
  96. package/dist/_openapi_client/resources/sandboxes/snapshots.d.ts +0 -130
  97. package/dist/_openapi_client/resources/sandboxes/snapshots.js +0 -35
  98. package/dist/_openapi_client/resources/settings.cjs +0 -15
  99. package/dist/_openapi_client/resources/settings.d.ts +0 -18
  100. package/dist/_openapi_client/resources/settings.js +0 -11
  101. package/dist/_openapi_client/resources/workspaces.cjs +0 -35
  102. package/dist/_openapi_client/resources/workspaces.d.ts +0 -84
  103. package/dist/_openapi_client/resources/workspaces.js +0 -31
@@ -3,6 +3,8 @@
3
3
  import { APIResource } from '../../core/resource.js';
4
4
  import * as RulesAPI from './rules.js';
5
5
  import { Rules } from './rules.js';
6
+ import { ItemsCursorPostPagination, } from '../../core/pagination.js';
7
+ import { buildHeaders } from '../../internal/headers.js';
6
8
  import { path } from '../../internal/utils/path.js';
7
9
  export class Runs extends APIResource {
8
10
  constructor() {
@@ -13,53 +15,64 @@ export class Runs extends APIResource {
13
15
  writable: true,
14
16
  value: new RulesAPI.Rules(this._client)
15
17
  });
18
+ Object.defineProperty(this, "retrieve", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: this.retrieveV2
23
+ });
24
+ Object.defineProperty(this, "query", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: this.queryV2
29
+ });
16
30
  }
17
31
  /**
18
- * Queues a single run for ingestion. The request body must be a JSON-encoded run
19
- * object that follows the Run schema.
20
- */
21
- create(body, options) {
22
- return this._client.post('/runs', { body, ...options });
23
- }
24
- /**
25
- * Get a specific run.
26
- */
27
- retrieve(runID, query = {}, options) {
28
- return this._client.get(path `/api/v1/runs/${runID}`, { query, ...options });
29
- }
30
- /**
31
- * Updates a run identified by its ID. The body should contain only the fields to
32
- * be changed; unknown fields are ignored.
33
- */
34
- update(runID, body, options) {
35
- return this._client.patch(path `/runs/${runID}`, { body, ...options });
36
- }
37
- /**
38
- * Ingests a batch of runs in a single JSON payload. The payload must have `post`
39
- * and/or `patch` arrays containing run objects. Prefer this endpoint over
40
- * single‑run ingestion when submitting hundreds of runs, but `/runs/multipart`
41
- * offers better handling for very large fields and attachments.
42
- */
43
- ingestBatch(body, options) {
44
- return this._client.post('/runs/batch', { body, ...options });
45
- }
46
- /**
47
- * Query Runs
48
- */
49
- query(body, options) {
50
- return this._client.post('/api/v1/runs/query', { body, ...options });
51
- }
52
- /**
53
- * Get all runs by query in body payload.
32
+ * **Alpha:** The request and response contract may change; Returns a paginated
33
+ * list of runs for the given projects within min/max start_time. Supports filters,
34
+ * cursor pagination, and `selects` to select fields to return.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * // Automatically fetches more pages as needed.
39
+ * for await (const queryRunResponse of client.runs.queryV2()) {
40
+ * // ...
41
+ * }
42
+ * ```
54
43
  */
55
- stats(body, options) {
56
- return this._client.post('/api/v1/runs/stats', { body, ...options });
44
+ queryV2(params, options) {
45
+ const { Accept, ...body } = params;
46
+ return this._client.getAPIList('/v2/runs/query', (ItemsCursorPostPagination), {
47
+ body,
48
+ method: 'post',
49
+ ...options,
50
+ headers: buildHeaders([{ ...(Accept != null ? { Accept: Accept } : undefined) }, options?.headers]),
51
+ });
57
52
  }
58
53
  /**
59
- * Update a run.
54
+ * **Alpha:** The request and response contract may change; Returns one run by ID
55
+ * for the given session and start_time. Use the `selects` query parameter
56
+ * (repeatable) to select fields to return.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const queryRunResponse = await client.runs.retrieveV2(
61
+ * '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
62
+ * {
63
+ * project_id: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
64
+ * start_time: '2019-12-27T18:11:19.117Z',
65
+ * },
66
+ * );
67
+ * ```
60
68
  */
61
- update2(runID, options) {
62
- return this._client.patch(path `/api/v1/runs/${runID}`, options);
69
+ retrieveV2(runID, params, options) {
70
+ const { Accept, ...query } = params;
71
+ return this._client.get(path `/v2/runs/${runID}`, {
72
+ query,
73
+ ...options,
74
+ headers: buildHeaders([{ ...(Accept != null ? { Accept: Accept } : undefined) }, options?.headers]),
75
+ });
63
76
  }
64
77
  }
65
78
  Runs.Rules = Rules;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ __exportStar(require("./runs/index.cjs"), exports);
@@ -0,0 +1 @@
1
+ export * from './runs/index.js';
@@ -0,0 +1,3 @@
1
+ // @ts-nocheck
2
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
3
+ export * from './runs/index.js';
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SECRET_RULES = exports.SECRET_PLACEHOLDER = void 0;
3
4
  exports.createAnonymizer = createAnonymizer;
5
+ exports.createSecretAnonymizer = createSecretAnonymizer;
4
6
  function extractStringNodes(data, options) {
5
7
  const parsedOptions = { ...options, maxDepth: options.maxDepth ?? 10 };
6
8
  const queue = [[data, 0, "", null, ""]];
@@ -125,3 +127,143 @@ function createAnonymizer(replacer, options) {
125
127
  return mutateValue;
126
128
  };
127
129
  }
130
+ /**
131
+ * Replacement token written in place of detected secrets by
132
+ * {@link DEFAULT_SECRET_RULES} / {@link createSecretAnonymizer}.
133
+ */
134
+ exports.SECRET_PLACEHOLDER = "[SECRET_DETECTED]";
135
+ /**
136
+ * A curated, high-precision rule set for detecting common credentials in
137
+ * traced data (prompts, tool inputs/outputs, file contents, shell commands).
138
+ *
139
+ * Designed to favor *low false positives* over exhaustive coverage:
140
+ * - Provider rules are anchored to well-known key prefixes.
141
+ * - Structural rules only fire when a sensitive *name* (api_key, token,
142
+ * password, …) is paired with an assignment/separator, so ordinary code,
143
+ * UUIDs, and hashes are left intact.
144
+ *
145
+ * This is NOT a port of gitleaks/secretlint; pattern shapes are drawn from
146
+ * those projects (and provider docs) as a reference only. Every rule sets an
147
+ * explicit `replace` because {@link createAnonymizer}'s default is
148
+ * `[redacted]`, whereas the shared token here is {@link SECRET_PLACEHOLDER}.
149
+ *
150
+ * Patterns are written to port 1:1 to the Python SDK preset (no lookbehind).
151
+ */
152
+ exports.DEFAULT_SECRET_RULES = [
153
+ // ── Provider API keys (prefix-anchored) ─────────────────────────────────
154
+ // Anthropic
155
+ { pattern: /sk-ant-[A-Za-z0-9_-]{20,}/g, replace: exports.SECRET_PLACEHOLDER },
156
+ // OpenAI: project / service-account / admin keys, then legacy `sk-...`
157
+ {
158
+ pattern: /sk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}/g,
159
+ replace: exports.SECRET_PLACEHOLDER,
160
+ },
161
+ { pattern: /sk-[A-Za-z0-9]{32,}/g, replace: exports.SECRET_PLACEHOLDER },
162
+ // LangSmith (keys are multi-segment: lsv2_pt_<key>_<tail> — match the
163
+ // full underscore-delimited tail so none of it leaks past the placeholder)
164
+ {
165
+ pattern: /lsv2_(?:pt|sk)_[A-Za-z0-9]{32,}(?:_[A-Za-z0-9]+)*/g,
166
+ replace: exports.SECRET_PLACEHOLDER,
167
+ },
168
+ { pattern: /ls__[A-Za-z0-9]{16,}/g, replace: exports.SECRET_PLACEHOLDER },
169
+ // GitHub personal access / app tokens
170
+ { pattern: /gh[pousr]_[A-Za-z0-9]{36,}/g, replace: exports.SECRET_PLACEHOLDER },
171
+ { pattern: /github_pat_[A-Za-z0-9_]{82}/g, replace: exports.SECRET_PLACEHOLDER },
172
+ // GitLab personal access token
173
+ { pattern: /glpat-[A-Za-z0-9_-]{20,}/g, replace: exports.SECRET_PLACEHOLDER },
174
+ // AWS access key id (covers AKIA/ASIA/ABIA/ACCA/A3T* prefixes)
175
+ {
176
+ pattern: /\b(?:AKIA|ASIA|ABIA|ACCA|A3T[A-Z0-9])[0-9A-Z]{16}\b/g,
177
+ replace: exports.SECRET_PLACEHOLDER,
178
+ },
179
+ // Google API key + OAuth access token
180
+ { pattern: /AIza[0-9A-Za-z_-]{35}/g, replace: exports.SECRET_PLACEHOLDER },
181
+ { pattern: /ya29\.[0-9A-Za-z_-]+/g, replace: exports.SECRET_PLACEHOLDER },
182
+ // Slack tokens (bot/user + app-level) + incoming webhooks
183
+ { pattern: /xox[baprs]-[A-Za-z0-9-]{10,}/g, replace: exports.SECRET_PLACEHOLDER },
184
+ { pattern: /xapp-\d-[A-Za-z0-9-]{10,}/g, replace: exports.SECRET_PLACEHOLDER },
185
+ {
186
+ pattern: /https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/]+/g,
187
+ replace: exports.SECRET_PLACEHOLDER,
188
+ },
189
+ // Stripe
190
+ {
191
+ pattern: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{20,}\b/g,
192
+ replace: exports.SECRET_PLACEHOLDER,
193
+ },
194
+ // npm
195
+ { pattern: /npm_[A-Za-z0-9]{36}/g, replace: exports.SECRET_PLACEHOLDER },
196
+ // PyPI upload token
197
+ {
198
+ pattern: /pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,}/g,
199
+ replace: exports.SECRET_PLACEHOLDER,
200
+ },
201
+ // SendGrid
202
+ {
203
+ pattern: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g,
204
+ replace: exports.SECRET_PLACEHOLDER,
205
+ },
206
+ // ── Structured tokens ────────────────────────────────────────────────────
207
+ // JWT (header.payload.signature)
208
+ {
209
+ pattern: /eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
210
+ replace: exports.SECRET_PLACEHOLDER,
211
+ },
212
+ // PEM private key blocks (RSA/EC/OPENSSH/DSA/plain + PGP "...KEY BLOCK")
213
+ {
214
+ pattern: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY(?: BLOCK)?-----[\s\S]+?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY(?: BLOCK)?-----/g,
215
+ replace: exports.SECRET_PLACEHOLDER,
216
+ },
217
+ // ── Structural / contextual (sensitive NAME + assignment) ─────────────────
218
+ // KEY=value or "key": "value" where the name looks sensitive. Keep the name
219
+ // and separator ($1), redact the value. Notes:
220
+ // - (?![A-Za-z0-9]) after the keyword requires a component boundary, so
221
+ // `token` matches `api_token`/`mytoken` but NOT `tokenizer`/`tokens`.
222
+ // - the value may start with an auth scheme word (Bearer/Token/Basic) so a
223
+ // `X-Api-Key: Bearer <tok>` shape redacts the credential, not just "Bearer".
224
+ // - value excludes & and ; so query-string params past the secret survive.
225
+ // - requires a 6+ char value so short non-secret values are not touched.
226
+ {
227
+ pattern: /\b([A-Za-z0-9_.-]*(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|ACCESS[_-]?KEY|AUTH[_-]?TOKEN|CLIENT[_-]?SECRET)(?![A-Za-z0-9])(?:[_.-][A-Za-z0-9]+)*["']?\s*[:=]\s*["']?)(?:(?:bearer|token|basic)\s+)?[^\s"'&;]{6,}/gi,
228
+ replace: `$1${exports.SECRET_PLACEHOLDER}`,
229
+ },
230
+ // Authorization / API-key headers. Keep the header name + separator ($1$2)
231
+ // and an optional scheme ($3); redact the credential.
232
+ {
233
+ pattern: /\b(authorization|x-api-key|x-auth-token)(["']?\s*[:=]\s*["']?)(bearer\s+|token\s+|basic\s+)?[A-Za-z0-9._~+/-]{8,}=*/gi,
234
+ replace: `$1$2$3${exports.SECRET_PLACEHOLDER}`,
235
+ },
236
+ // Bare "Bearer <token>" (any case; the scheme word is preserved via $1).
237
+ {
238
+ pattern: /\b(Bearer\s+)[A-Za-z0-9._~+/-]{10,}=*/gi,
239
+ replace: `$1${exports.SECRET_PLACEHOLDER}`,
240
+ },
241
+ // Credentials embedded in URLs: proto://user:PASS@host -> redact PASS only.
242
+ // Username is optional so proto://:PASS@host (empty user) is still covered.
243
+ {
244
+ pattern: /\b([a-z][a-z0-9+.-]*:\/\/[^:@/\s]*:)[^@/\s]+(@)/gi,
245
+ replace: `$1${exports.SECRET_PLACEHOLDER}$2`,
246
+ },
247
+ ];
248
+ /**
249
+ * Build an anonymizer pre-loaded with {@link DEFAULT_SECRET_RULES} suitable for
250
+ * passing to `new Client({ anonymizer })`. It redacts detected secrets from run
251
+ * inputs, outputs, and metadata client-side, before they are uploaded.
252
+ *
253
+ * @param options.extraRules - Additional rules appended after the defaults.
254
+ * @param options.maxDepth - Max recursion depth (default 24; higher than
255
+ * `createAnonymizer`'s default of 10 because traced payloads nest deeply,
256
+ * e.g. `messages[].content[].args`).
257
+ *
258
+ * @example
259
+ * ```ts
260
+ * import { Client } from "langsmith";
261
+ * import { createSecretAnonymizer } from "langsmith/anonymizer";
262
+ *
263
+ * const client = new Client({ anonymizer: createSecretAnonymizer() });
264
+ * ```
265
+ */
266
+ function createSecretAnonymizer(options) {
267
+ const rules = [...exports.DEFAULT_SECRET_RULES, ...(options?.extraRules ?? [])];
268
+ return createAnonymizer(rules, { maxDepth: options?.maxDepth ?? 24 });
269
+ }
@@ -14,3 +14,48 @@ export type ReplacerType = ((value: string, path?: string) => string) | StringNo
14
14
  export declare function createAnonymizer(replacer: ReplacerType, options?: {
15
15
  maxDepth?: number;
16
16
  }): <T>(data: T) => T;
17
+ /**
18
+ * Replacement token written in place of detected secrets by
19
+ * {@link DEFAULT_SECRET_RULES} / {@link createSecretAnonymizer}.
20
+ */
21
+ export declare const SECRET_PLACEHOLDER = "[SECRET_DETECTED]";
22
+ /**
23
+ * A curated, high-precision rule set for detecting common credentials in
24
+ * traced data (prompts, tool inputs/outputs, file contents, shell commands).
25
+ *
26
+ * Designed to favor *low false positives* over exhaustive coverage:
27
+ * - Provider rules are anchored to well-known key prefixes.
28
+ * - Structural rules only fire when a sensitive *name* (api_key, token,
29
+ * password, …) is paired with an assignment/separator, so ordinary code,
30
+ * UUIDs, and hashes are left intact.
31
+ *
32
+ * This is NOT a port of gitleaks/secretlint; pattern shapes are drawn from
33
+ * those projects (and provider docs) as a reference only. Every rule sets an
34
+ * explicit `replace` because {@link createAnonymizer}'s default is
35
+ * `[redacted]`, whereas the shared token here is {@link SECRET_PLACEHOLDER}.
36
+ *
37
+ * Patterns are written to port 1:1 to the Python SDK preset (no lookbehind).
38
+ */
39
+ export declare const DEFAULT_SECRET_RULES: StringNodeRule[];
40
+ /**
41
+ * Build an anonymizer pre-loaded with {@link DEFAULT_SECRET_RULES} suitable for
42
+ * passing to `new Client({ anonymizer })`. It redacts detected secrets from run
43
+ * inputs, outputs, and metadata client-side, before they are uploaded.
44
+ *
45
+ * @param options.extraRules - Additional rules appended after the defaults.
46
+ * @param options.maxDepth - Max recursion depth (default 24; higher than
47
+ * `createAnonymizer`'s default of 10 because traced payloads nest deeply,
48
+ * e.g. `messages[].content[].args`).
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { Client } from "langsmith";
53
+ * import { createSecretAnonymizer } from "langsmith/anonymizer";
54
+ *
55
+ * const client = new Client({ anonymizer: createSecretAnonymizer() });
56
+ * ```
57
+ */
58
+ export declare function createSecretAnonymizer(options?: {
59
+ extraRules?: StringNodeRule[];
60
+ maxDepth?: number;
61
+ }): <T>(data: T) => T;
@@ -122,3 +122,143 @@ export function createAnonymizer(replacer, options) {
122
122
  return mutateValue;
123
123
  };
124
124
  }
125
+ /**
126
+ * Replacement token written in place of detected secrets by
127
+ * {@link DEFAULT_SECRET_RULES} / {@link createSecretAnonymizer}.
128
+ */
129
+ export const SECRET_PLACEHOLDER = "[SECRET_DETECTED]";
130
+ /**
131
+ * A curated, high-precision rule set for detecting common credentials in
132
+ * traced data (prompts, tool inputs/outputs, file contents, shell commands).
133
+ *
134
+ * Designed to favor *low false positives* over exhaustive coverage:
135
+ * - Provider rules are anchored to well-known key prefixes.
136
+ * - Structural rules only fire when a sensitive *name* (api_key, token,
137
+ * password, …) is paired with an assignment/separator, so ordinary code,
138
+ * UUIDs, and hashes are left intact.
139
+ *
140
+ * This is NOT a port of gitleaks/secretlint; pattern shapes are drawn from
141
+ * those projects (and provider docs) as a reference only. Every rule sets an
142
+ * explicit `replace` because {@link createAnonymizer}'s default is
143
+ * `[redacted]`, whereas the shared token here is {@link SECRET_PLACEHOLDER}.
144
+ *
145
+ * Patterns are written to port 1:1 to the Python SDK preset (no lookbehind).
146
+ */
147
+ export const DEFAULT_SECRET_RULES = [
148
+ // ── Provider API keys (prefix-anchored) ─────────────────────────────────
149
+ // Anthropic
150
+ { pattern: /sk-ant-[A-Za-z0-9_-]{20,}/g, replace: SECRET_PLACEHOLDER },
151
+ // OpenAI: project / service-account / admin keys, then legacy `sk-...`
152
+ {
153
+ pattern: /sk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}/g,
154
+ replace: SECRET_PLACEHOLDER,
155
+ },
156
+ { pattern: /sk-[A-Za-z0-9]{32,}/g, replace: SECRET_PLACEHOLDER },
157
+ // LangSmith (keys are multi-segment: lsv2_pt_<key>_<tail> — match the
158
+ // full underscore-delimited tail so none of it leaks past the placeholder)
159
+ {
160
+ pattern: /lsv2_(?:pt|sk)_[A-Za-z0-9]{32,}(?:_[A-Za-z0-9]+)*/g,
161
+ replace: SECRET_PLACEHOLDER,
162
+ },
163
+ { pattern: /ls__[A-Za-z0-9]{16,}/g, replace: SECRET_PLACEHOLDER },
164
+ // GitHub personal access / app tokens
165
+ { pattern: /gh[pousr]_[A-Za-z0-9]{36,}/g, replace: SECRET_PLACEHOLDER },
166
+ { pattern: /github_pat_[A-Za-z0-9_]{82}/g, replace: SECRET_PLACEHOLDER },
167
+ // GitLab personal access token
168
+ { pattern: /glpat-[A-Za-z0-9_-]{20,}/g, replace: SECRET_PLACEHOLDER },
169
+ // AWS access key id (covers AKIA/ASIA/ABIA/ACCA/A3T* prefixes)
170
+ {
171
+ pattern: /\b(?:AKIA|ASIA|ABIA|ACCA|A3T[A-Z0-9])[0-9A-Z]{16}\b/g,
172
+ replace: SECRET_PLACEHOLDER,
173
+ },
174
+ // Google API key + OAuth access token
175
+ { pattern: /AIza[0-9A-Za-z_-]{35}/g, replace: SECRET_PLACEHOLDER },
176
+ { pattern: /ya29\.[0-9A-Za-z_-]+/g, replace: SECRET_PLACEHOLDER },
177
+ // Slack tokens (bot/user + app-level) + incoming webhooks
178
+ { pattern: /xox[baprs]-[A-Za-z0-9-]{10,}/g, replace: SECRET_PLACEHOLDER },
179
+ { pattern: /xapp-\d-[A-Za-z0-9-]{10,}/g, replace: SECRET_PLACEHOLDER },
180
+ {
181
+ pattern: /https:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/]+/g,
182
+ replace: SECRET_PLACEHOLDER,
183
+ },
184
+ // Stripe
185
+ {
186
+ pattern: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{20,}\b/g,
187
+ replace: SECRET_PLACEHOLDER,
188
+ },
189
+ // npm
190
+ { pattern: /npm_[A-Za-z0-9]{36}/g, replace: SECRET_PLACEHOLDER },
191
+ // PyPI upload token
192
+ {
193
+ pattern: /pypi-AgEIcHlwaS[A-Za-z0-9_-]{50,}/g,
194
+ replace: SECRET_PLACEHOLDER,
195
+ },
196
+ // SendGrid
197
+ {
198
+ pattern: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g,
199
+ replace: SECRET_PLACEHOLDER,
200
+ },
201
+ // ── Structured tokens ────────────────────────────────────────────────────
202
+ // JWT (header.payload.signature)
203
+ {
204
+ pattern: /eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
205
+ replace: SECRET_PLACEHOLDER,
206
+ },
207
+ // PEM private key blocks (RSA/EC/OPENSSH/DSA/plain + PGP "...KEY BLOCK")
208
+ {
209
+ pattern: /-----BEGIN (?:[A-Z0-9 ]+ )?PRIVATE KEY(?: BLOCK)?-----[\s\S]+?-----END (?:[A-Z0-9 ]+ )?PRIVATE KEY(?: BLOCK)?-----/g,
210
+ replace: SECRET_PLACEHOLDER,
211
+ },
212
+ // ── Structural / contextual (sensitive NAME + assignment) ─────────────────
213
+ // KEY=value or "key": "value" where the name looks sensitive. Keep the name
214
+ // and separator ($1), redact the value. Notes:
215
+ // - (?![A-Za-z0-9]) after the keyword requires a component boundary, so
216
+ // `token` matches `api_token`/`mytoken` but NOT `tokenizer`/`tokens`.
217
+ // - the value may start with an auth scheme word (Bearer/Token/Basic) so a
218
+ // `X-Api-Key: Bearer <tok>` shape redacts the credential, not just "Bearer".
219
+ // - value excludes & and ; so query-string params past the secret survive.
220
+ // - requires a 6+ char value so short non-secret values are not touched.
221
+ {
222
+ pattern: /\b([A-Za-z0-9_.-]*(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE[_-]?KEY|ACCESS[_-]?KEY|AUTH[_-]?TOKEN|CLIENT[_-]?SECRET)(?![A-Za-z0-9])(?:[_.-][A-Za-z0-9]+)*["']?\s*[:=]\s*["']?)(?:(?:bearer|token|basic)\s+)?[^\s"'&;]{6,}/gi,
223
+ replace: `$1${SECRET_PLACEHOLDER}`,
224
+ },
225
+ // Authorization / API-key headers. Keep the header name + separator ($1$2)
226
+ // and an optional scheme ($3); redact the credential.
227
+ {
228
+ pattern: /\b(authorization|x-api-key|x-auth-token)(["']?\s*[:=]\s*["']?)(bearer\s+|token\s+|basic\s+)?[A-Za-z0-9._~+/-]{8,}=*/gi,
229
+ replace: `$1$2$3${SECRET_PLACEHOLDER}`,
230
+ },
231
+ // Bare "Bearer <token>" (any case; the scheme word is preserved via $1).
232
+ {
233
+ pattern: /\b(Bearer\s+)[A-Za-z0-9._~+/-]{10,}=*/gi,
234
+ replace: `$1${SECRET_PLACEHOLDER}`,
235
+ },
236
+ // Credentials embedded in URLs: proto://user:PASS@host -> redact PASS only.
237
+ // Username is optional so proto://:PASS@host (empty user) is still covered.
238
+ {
239
+ pattern: /\b([a-z][a-z0-9+.-]*:\/\/[^:@/\s]*:)[^@/\s]+(@)/gi,
240
+ replace: `$1${SECRET_PLACEHOLDER}$2`,
241
+ },
242
+ ];
243
+ /**
244
+ * Build an anonymizer pre-loaded with {@link DEFAULT_SECRET_RULES} suitable for
245
+ * passing to `new Client({ anonymizer })`. It redacts detected secrets from run
246
+ * inputs, outputs, and metadata client-side, before they are uploaded.
247
+ *
248
+ * @param options.extraRules - Additional rules appended after the defaults.
249
+ * @param options.maxDepth - Max recursion depth (default 24; higher than
250
+ * `createAnonymizer`'s default of 10 because traced payloads nest deeply,
251
+ * e.g. `messages[].content[].args`).
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * import { Client } from "langsmith";
256
+ * import { createSecretAnonymizer } from "langsmith/anonymizer";
257
+ *
258
+ * const client = new Client({ anonymizer: createSecretAnonymizer() });
259
+ * ```
260
+ */
261
+ export function createSecretAnonymizer(options) {
262
+ const rules = [...DEFAULT_SECRET_RULES, ...(options?.extraRules ?? [])];
263
+ return createAnonymizer(rules, { maxDepth: options?.maxDepth ?? 24 });
264
+ }
package/dist/client.cjs CHANGED
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.Client = exports.AutoBatchQueue = exports.DEFAULT_MAX_SIZE_BYTES = exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = void 0;
37
37
  exports.mergeRuntimeEnvIntoRun = mergeRuntimeEnvIntoRun;
38
+ exports._checkBackendVersion = _checkBackendVersion;
38
39
  const uuid = __importStar(require("./utils/uuid/src/index.cjs"));
39
40
  const translator_js_1 = require("./experimental/otel/translator.cjs");
40
41
  const otel_js_1 = require("./singletons/otel.cjs");
@@ -45,6 +46,7 @@ const index_js_1 = require("./index.cjs");
45
46
  const index_js_2 = require("./_openapi_client/index.cjs");
46
47
  const _uuid_js_1 = require("./utils/_uuid.cjs");
47
48
  const warn_js_1 = require("./utils/warn.cjs");
49
+ const constants_js_1 = require("./utils/constants.cjs");
48
50
  const prompts_js_1 = require("./utils/prompts.cjs");
49
51
  const error_js_1 = require("./utils/error.cjs");
50
52
  const index_js_3 = require("./utils/prompt_cache/index.cjs");
@@ -159,6 +161,25 @@ function _formatFeedbackScore(score) {
159
161
  }
160
162
  return score;
161
163
  }
164
+ function _checkBackendVersion(version, minVersion = constants_js_1._MIN_BACKEND_VERSION) {
165
+ const parse = (v) => v.split(".").map((s) => parseInt(s, 10));
166
+ const [maj, min, pat] = parse(version);
167
+ const [rMaj, rMin, rPat] = parse(minVersion);
168
+ if (isNaN(maj) ||
169
+ isNaN(min) ||
170
+ isNaN(pat) ||
171
+ isNaN(rMaj) ||
172
+ isNaN(rMin) ||
173
+ isNaN(rPat)) {
174
+ console.warn(`[LANGSMITH]: Could not parse backend version ${JSON.stringify(version)} for compatibility check.`);
175
+ return;
176
+ }
177
+ if (maj < rMaj ||
178
+ (maj === rMaj && min < rMin) ||
179
+ (maj === rMaj && min === rMin && pat < rPat)) {
180
+ console.warn(`[LANGSMITH]: Backend version ${JSON.stringify(version)} is older than the minimum version required by this SDK (${JSON.stringify(minVersion)}). Some features may not work as expected.`);
181
+ }
182
+ }
162
183
  exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = 24 * 1024 * 1024;
163
184
  /** Default maximum memory (1GB) for queue size limits. */
164
185
  exports.DEFAULT_MAX_SIZE_BYTES = 1024 * 1024 * 1024; // 1GB
@@ -947,6 +968,9 @@ class Client {
947
968
  get onlineEvaluators() {
948
969
  return this.openAPIClient.onlineEvaluators;
949
970
  }
971
+ get runs() {
972
+ return this.openAPIClient.runs;
973
+ }
950
974
  async processInputs(inputs) {
951
975
  if (this.hideInputs === false) {
952
976
  return inputs;
@@ -1408,6 +1432,9 @@ class Client {
1408
1432
  if (this._serverInfo === undefined) {
1409
1433
  try {
1410
1434
  this._serverInfo = await this._getServerInfo();
1435
+ if (this._serverInfo?.version) {
1436
+ _checkBackendVersion(this._serverInfo.version);
1437
+ }
1411
1438
  }
1412
1439
  catch (e) {
1413
1440
  console.warn(`[LANGSMITH]: Failed to fetch info on supported operations. Falling back to batch operations and default limits. Info: ${e.status ?? "Unspecified status code"} ${e.message}`);
@@ -3502,7 +3529,7 @@ class Client {
3502
3529
  return res;
3503
3530
  });
3504
3531
  }
3505
- async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, }) {
3532
+ async createFeedback(runId, key, { score, value, correction, comment, sourceInfo, feedbackSourceType = "api", sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, extendTraceRetention, }) {
3506
3533
  if (!runId && !projectId) {
3507
3534
  throw new Error("One of runId or projectId must be provided");
3508
3535
  }
@@ -3535,6 +3562,7 @@ class Client {
3535
3562
  feedbackConfig,
3536
3563
  session_id: sessionId ?? projectId,
3537
3564
  start_time: startTime,
3565
+ extend_trace_retention: extendTraceRetention,
3538
3566
  };
3539
3567
  const body = JSON.stringify(feedback);
3540
3568
  const url = `${this.apiUrl}/feedback`;
package/dist/client.d.ts CHANGED
@@ -4,6 +4,7 @@ import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShare
4
4
  import { type TracingMode } from "./utils/env.js";
5
5
  import { EvaluationResult, EvaluationResults } from "./evaluation/evaluator.js";
6
6
  import { OnlineEvaluators } from "./_openapi_client/resources/online-evaluators.js";
7
+ import { Runs as OpenAPIRuns } from "./_openapi_client/resources/runs.js";
7
8
  import { PromptCache } from "./utils/prompt_cache/index.js";
8
9
  import { ProfileAuth } from "./utils/profiles.js";
9
10
  export interface ClientConfig {
@@ -382,6 +383,7 @@ export interface ListThreadsItem extends Thread {
382
383
  runs: Run[];
383
384
  }
384
385
  export declare function mergeRuntimeEnvIntoRun<T extends RunCreate | RunUpdate>(run: T, cachedEnvVars?: Record<string, string>, omitTracedRuntimeInfo?: boolean): T;
386
+ export declare function _checkBackendVersion(version: string, minVersion?: string): void;
385
387
  export declare const DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES: number;
386
388
  /** Default maximum memory (1GB) for queue size limits. */
387
389
  export declare const DEFAULT_MAX_SIZE_BYTES: number;
@@ -501,6 +503,7 @@ export declare class Client implements LangSmithTracingClientInterface {
501
503
  private _newOpenAPIClient;
502
504
  private _getPlatformEndpointPath;
503
505
  get onlineEvaluators(): OnlineEvaluators;
506
+ get runs(): OpenAPIRuns;
504
507
  private processInputs;
505
508
  private processOutputs;
506
509
  private processMetadata;
@@ -915,7 +918,7 @@ export declare class Client implements LangSmithTracingClientInterface {
915
918
  exampleIds: string[];
916
919
  remove?: boolean;
917
920
  }): Promise<void>;
918
- createFeedback(runId: string | null, key: string, { score, value, correction, comment, sourceInfo, feedbackSourceType, sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, }: {
921
+ createFeedback(runId: string | null, key: string, { score, value, correction, comment, sourceInfo, feedbackSourceType, sourceRunId, feedbackId, feedbackConfig, projectId, comparativeExperimentId, sessionId, startTime, extendTraceRetention, }: {
919
922
  score?: ScoreType;
920
923
  value?: ValueType;
921
924
  correction?: object;
@@ -932,6 +935,8 @@ export declare class Client implements LangSmithTracingClientInterface {
932
935
  sessionId?: string;
933
936
  /** The start time of the run this feedback is for. Accepts ISO string or epoch ms. */
934
937
  startTime?: number | string;
938
+ /** If false, create feedback without extending the trace's retention tier. */
939
+ extendTraceRetention?: boolean;
935
940
  }): Promise<Feedback>;
936
941
  updateFeedback(feedbackId: string, { score, value, correction, comment, }: {
937
942
  score?: number | boolean | null;