langsmith 0.8.9 → 0.8.11

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 (41) hide show
  1. package/dist/client.cjs +33 -4
  2. package/dist/client.d.ts +10 -6
  3. package/dist/client.js +33 -4
  4. package/dist/experimental/vercel/_agent_type.cjs +30 -0
  5. package/dist/experimental/vercel/_agent_type.d.ts +5 -0
  6. package/dist/experimental/vercel/_agent_type.js +26 -0
  7. package/dist/experimental/vercel/telemetry.cjs +2 -7
  8. package/dist/experimental/vercel/telemetry.js +2 -7
  9. package/dist/experimental/vercel/wrap.cjs +5 -11
  10. package/dist/experimental/vercel/wrap.js +6 -12
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/sandbox/client.cjs +11 -5
  15. package/dist/sandbox/client.d.ts +4 -4
  16. package/dist/sandbox/client.js +11 -5
  17. package/dist/sandbox/index.cjs +2 -1
  18. package/dist/sandbox/index.d.ts +2 -2
  19. package/dist/sandbox/index.js +1 -1
  20. package/dist/sandbox/mounts.cjs +25 -2
  21. package/dist/sandbox/mounts.d.ts +12 -1
  22. package/dist/sandbox/mounts.js +24 -2
  23. package/dist/sandbox/proxy_config.cjs +28 -4
  24. package/dist/sandbox/proxy_config.d.ts +4 -2
  25. package/dist/sandbox/proxy_config.js +28 -4
  26. package/dist/sandbox/types.d.ts +36 -1
  27. package/dist/utils/_git.cjs +28 -1
  28. package/dist/utils/_git.js +28 -1
  29. package/dist/utils/ls_agent_type.cjs +31 -0
  30. package/dist/utils/ls_agent_type.d.ts +9 -0
  31. package/dist/utils/ls_agent_type.js +25 -0
  32. package/dist/utils/profiles.cjs +125 -1
  33. package/dist/utils/profiles.d.ts +6 -0
  34. package/dist/utils/profiles.js +124 -1
  35. package/dist/wrappers/anthropic.cjs +39 -6
  36. package/dist/wrappers/anthropic.js +39 -6
  37. package/dist/wrappers/openai.cjs +4 -0
  38. package/dist/wrappers/openai.js +4 -0
  39. package/dist/wrappers/openai_agents.cjs +15 -12
  40. package/dist/wrappers/openai_agents.js +15 -12
  41. package/package.json +12 -2
@@ -5,6 +5,8 @@ export const DEFAULT_API_URL = "https://api.smith.langchain.com";
5
5
  const OAUTH_CLIENT_ID = "langsmith-cli";
6
6
  const TOKEN_REFRESH_LEEWAY_MS = 60_000;
7
7
  const TOKEN_REFRESH_TIMEOUT_MS = 10_000;
8
+ const OAUTH_DISCOVERY_TIMEOUT_MS = 5_000;
9
+ const WELL_KNOWN_OAUTH_PATH = "/.well-known/oauth-authorization-server";
8
10
  function isBrowserLikeRuntime() {
9
11
  const env = getEnv();
10
12
  return env === "browser" || env === "webworker";
@@ -87,6 +89,126 @@ function normalizeConfigUrl(apiUrl) {
87
89
  ? normalized.slice(0, -apiV1Suffix.length)
88
90
  : normalized;
89
91
  }
92
+ /**
93
+ * Metadata base URLs to probe, most specific first: the configured mount point,
94
+ * then the self-hosted and SaaS locations, so both a bare origin and an
95
+ * explicit `/api` resolve.
96
+ */
97
+ function oauthDiscoveryCandidates(apiUrl) {
98
+ const given = normalizeConfigUrl(apiUrl);
99
+ const origin = given.endsWith("/api")
100
+ ? given.slice(0, -"/api".length)
101
+ : given;
102
+ const candidates = [];
103
+ for (const candidate of [given, `${origin}/api`, origin]) {
104
+ if (candidate && candidate !== "/api" && !candidates.includes(candidate)) {
105
+ candidates.push(candidate);
106
+ }
107
+ }
108
+ return candidates;
109
+ }
110
+ /**
111
+ * Check the document describes the deployment we probed. RFC 8414 requires the
112
+ * issuer to match the URL the well-known path was built from, and every
113
+ * endpoint must share the issuer's origin. Refresh tokens are posted to these
114
+ * endpoints, so an unvalidated document could redirect credentials elsewhere.
115
+ */
116
+ function isTrustedOAuthMetadata(doc, base) {
117
+ const { issuer } = doc;
118
+ if (typeof issuer !== "string" ||
119
+ issuer.replace(/\/+$/, "") !== base.replace(/\/+$/, "")) {
120
+ return false;
121
+ }
122
+ let issuerUrl;
123
+ try {
124
+ issuerUrl = new URL(issuer);
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ for (const endpoint of [
130
+ doc.device_authorization_endpoint,
131
+ doc.token_endpoint,
132
+ ]) {
133
+ if (typeof endpoint !== "string" || !endpoint) {
134
+ return false;
135
+ }
136
+ try {
137
+ const url = new URL(endpoint);
138
+ if (url.protocol !== issuerUrl.protocol || url.host !== issuerUrl.host) {
139
+ return false;
140
+ }
141
+ }
142
+ catch {
143
+ return false;
144
+ }
145
+ }
146
+ return true;
147
+ }
148
+ /**
149
+ * Metadata URLs for an issuer, RFC 8414 form first. RFC 8414 inserts the
150
+ * well-known segment between the origin and the issuer path, so
151
+ * `https://host/api` is described at
152
+ * `https://host/.well-known/oauth-authorization-server/api`. Deployments
153
+ * commonly also serve the appended form, and for a path-less issuer the two are
154
+ * identical.
155
+ */
156
+ function oauthMetadataUrls(base) {
157
+ const appended = `${base}${WELL_KNOWN_OAUTH_PATH}`;
158
+ let inserted;
159
+ try {
160
+ const url = new URL(base);
161
+ inserted = `${url.origin}${WELL_KNOWN_OAUTH_PATH}${url.pathname === "/" ? "" : url.pathname}`;
162
+ }
163
+ catch {
164
+ return [appended];
165
+ }
166
+ return inserted === appended ? [inserted] : [inserted, appended];
167
+ }
168
+ async function fetchOAuthMetadata(url, base, fetchImplementation) {
169
+ let response;
170
+ try {
171
+ response = await fetchImplementation(url, {
172
+ method: "GET",
173
+ headers: { Accept: "application/json" },
174
+ signal: AbortSignal.timeout(OAUTH_DISCOVERY_TIMEOUT_MS),
175
+ });
176
+ }
177
+ catch {
178
+ return undefined;
179
+ }
180
+ if (!response.ok) {
181
+ return undefined;
182
+ }
183
+ let doc;
184
+ try {
185
+ // Self-hosted answers unknown paths with an HTML 200 from the SPA.
186
+ doc = (await response.json());
187
+ }
188
+ catch {
189
+ return undefined;
190
+ }
191
+ if (!doc || typeof doc !== "object" || !isTrustedOAuthMetadata(doc, base)) {
192
+ return undefined;
193
+ }
194
+ return doc;
195
+ }
196
+ /**
197
+ * Resolve the token endpoint, preferring the deployment's metadata document.
198
+ * Falls back to `<mount>/oauth/token`, keeping a trailing `/api` because
199
+ * self-hosted mounts the authorization server under it.
200
+ */
201
+ export async function resolveTokenEndpoint(apiUrl, fetchImplementation) {
202
+ for (const base of oauthDiscoveryCandidates(apiUrl)) {
203
+ for (const url of oauthMetadataUrls(base)) {
204
+ const doc = await fetchOAuthMetadata(url, base, fetchImplementation);
205
+ if (doc) {
206
+ return doc.token_endpoint;
207
+ }
208
+ }
209
+ }
210
+ return `${normalizeConfigUrl(apiUrl)}/oauth/token`;
211
+ }
90
212
  function applyTokenResponse(profile, token) {
91
213
  profile.oauth ??= {};
92
214
  if (token.access_token) {
@@ -224,7 +346,8 @@ export class ProfileAuth {
224
346
  client_id: OAUTH_CLIENT_ID,
225
347
  refresh_token: this.state.profile.oauth?.refresh_token ?? refreshToken,
226
348
  });
227
- const response = await fetchImplementation(`${normalizeConfigUrl(refreshApiUrl)}/oauth/token`, {
349
+ const tokenEndpoint = await resolveTokenEndpoint(refreshApiUrl, fetchImplementation);
350
+ const response = await fetchImplementation(tokenEndpoint, {
228
351
  method: "POST",
229
352
  headers: {
230
353
  "Content-Type": "application/x-www-form-urlencoded",
@@ -5,7 +5,36 @@ exports.createUsageMetadata = createUsageMetadata;
5
5
  const anthropic_managed_js_1 = require("./utils/anthropic_managed.cjs");
6
6
  const traceable_js_1 = require("../traceable.cjs");
7
7
  const usage_js_1 = require("../utils/usage.cjs");
8
+ const index_js_1 = require("../anonymizer/index.cjs");
8
9
  const TRACED_INVOCATION_KEYS = ["top_k", "top_p", "stream", "thinking"];
10
+ /** Keys of an MCP server definition that are safe to trace. */
11
+ const MCP_SERVER_SAFE_KEYS = new Set([
12
+ "name",
13
+ "type",
14
+ "url",
15
+ "tool_configuration",
16
+ ]);
17
+ /**
18
+ * Mask credentials in `mcp_servers` before tracing. Anything outside
19
+ * {@link MCP_SERVER_SAFE_KEYS} keeps its name but gets
20
+ * {@link SECRET_PLACEHOLDER} as its value, so the trace shows the field was set
21
+ * without exposing it, and fields the API adds later are masked by default.
22
+ *
23
+ * Returns new objects; the caller's are also sent to Anthropic.
24
+ */
25
+ function redactMcpServers(servers) {
26
+ if (!Array.isArray(servers))
27
+ return servers;
28
+ return servers.map((server) => {
29
+ if (typeof server !== "object" || server == null || Array.isArray(server)) {
30
+ return server;
31
+ }
32
+ return Object.fromEntries(Object.entries(server).map(([key, value]) => [
33
+ key,
34
+ MCP_SERVER_SAFE_KEYS.has(key) ? value : index_js_1.SECRET_PLACEHOLDER,
35
+ ]));
36
+ });
37
+ }
9
38
  /**
10
39
  * Create usage metadata from Anthropic's token usage format.
11
40
  */
@@ -222,11 +251,15 @@ const wrapAnthropic = (anthropic, options) => {
222
251
  * This provides parity with the Python SDK behavior and enables system prompts
223
252
  * to be viewed and edited in the LangSmith playground.
224
253
  */
225
- function processSystemMessage(params) {
254
+ function processTracedInputs(params) {
255
+ // Copy first: `params` also goes to the API and must keep its real values.
256
+ const processed = { ...params };
257
+ if (processed.mcp_servers != null) {
258
+ processed.mcp_servers = redactMcpServers(processed.mcp_servers);
259
+ }
226
260
  if (!params.system) {
227
- return params;
261
+ return processed;
228
262
  }
229
- const processed = { ...params };
230
263
  // Handle both string and ContentBlock[] formats
231
264
  const systemContent = Array.isArray(params.system)
232
265
  ? params.system
@@ -248,7 +281,7 @@ const wrapAnthropic = (anthropic, options) => {
248
281
  run_type: "llm",
249
282
  aggregator: messageAggregator,
250
283
  argsConfigPath: [1, "langsmithExtra"],
251
- processInputs: processSystemMessage,
284
+ processInputs: processTracedInputs,
252
285
  getInvocationParams: (payload) => {
253
286
  if (typeof payload !== "object" || payload == null)
254
287
  return undefined;
@@ -315,7 +348,7 @@ const wrapAnthropic = (anthropic, options) => {
315
348
  run_type: "llm",
316
349
  aggregator: messageAggregator,
317
350
  argsConfigPath: [1, "langsmithExtra"],
318
- processInputs: processSystemMessage,
351
+ processInputs: processTracedInputs,
319
352
  getInvocationParams: messagesCreateConfig.getInvocationParams,
320
353
  processOutputs: processMessageOutput,
321
354
  ...cleanedOptions,
@@ -341,7 +374,7 @@ const wrapAnthropic = (anthropic, options) => {
341
374
  run_type: "llm",
342
375
  aggregator: messageAggregator,
343
376
  argsConfigPath: [1, "langsmithExtra"],
344
- processInputs: processSystemMessage,
377
+ processInputs: processTracedInputs,
345
378
  getInvocationParams: messagesCreateConfig.getInvocationParams,
346
379
  processOutputs: processMessageOutput,
347
380
  ...cleanedOptions,
@@ -1,7 +1,36 @@
1
1
  import { wrapManagedAgentSessionEvents } from "./utils/anthropic_managed.js";
2
2
  import { isTraceableFunction, traceable, } from "../traceable.js";
3
3
  import { convertAnthropicUsageToInputTokenDetails } from "../utils/usage.js";
4
+ import { SECRET_PLACEHOLDER } from "../anonymizer/index.js";
4
5
  const TRACED_INVOCATION_KEYS = ["top_k", "top_p", "stream", "thinking"];
6
+ /** Keys of an MCP server definition that are safe to trace. */
7
+ const MCP_SERVER_SAFE_KEYS = new Set([
8
+ "name",
9
+ "type",
10
+ "url",
11
+ "tool_configuration",
12
+ ]);
13
+ /**
14
+ * Mask credentials in `mcp_servers` before tracing. Anything outside
15
+ * {@link MCP_SERVER_SAFE_KEYS} keeps its name but gets
16
+ * {@link SECRET_PLACEHOLDER} as its value, so the trace shows the field was set
17
+ * without exposing it, and fields the API adds later are masked by default.
18
+ *
19
+ * Returns new objects; the caller's are also sent to Anthropic.
20
+ */
21
+ function redactMcpServers(servers) {
22
+ if (!Array.isArray(servers))
23
+ return servers;
24
+ return servers.map((server) => {
25
+ if (typeof server !== "object" || server == null || Array.isArray(server)) {
26
+ return server;
27
+ }
28
+ return Object.fromEntries(Object.entries(server).map(([key, value]) => [
29
+ key,
30
+ MCP_SERVER_SAFE_KEYS.has(key) ? value : SECRET_PLACEHOLDER,
31
+ ]));
32
+ });
33
+ }
5
34
  /**
6
35
  * Create usage metadata from Anthropic's token usage format.
7
36
  */
@@ -218,11 +247,15 @@ export const wrapAnthropic = (anthropic, options) => {
218
247
  * This provides parity with the Python SDK behavior and enables system prompts
219
248
  * to be viewed and edited in the LangSmith playground.
220
249
  */
221
- function processSystemMessage(params) {
250
+ function processTracedInputs(params) {
251
+ // Copy first: `params` also goes to the API and must keep its real values.
252
+ const processed = { ...params };
253
+ if (processed.mcp_servers != null) {
254
+ processed.mcp_servers = redactMcpServers(processed.mcp_servers);
255
+ }
222
256
  if (!params.system) {
223
- return params;
257
+ return processed;
224
258
  }
225
- const processed = { ...params };
226
259
  // Handle both string and ContentBlock[] formats
227
260
  const systemContent = Array.isArray(params.system)
228
261
  ? params.system
@@ -244,7 +277,7 @@ export const wrapAnthropic = (anthropic, options) => {
244
277
  run_type: "llm",
245
278
  aggregator: messageAggregator,
246
279
  argsConfigPath: [1, "langsmithExtra"],
247
- processInputs: processSystemMessage,
280
+ processInputs: processTracedInputs,
248
281
  getInvocationParams: (payload) => {
249
282
  if (typeof payload !== "object" || payload == null)
250
283
  return undefined;
@@ -311,7 +344,7 @@ export const wrapAnthropic = (anthropic, options) => {
311
344
  run_type: "llm",
312
345
  aggregator: messageAggregator,
313
346
  argsConfigPath: [1, "langsmithExtra"],
314
- processInputs: processSystemMessage,
347
+ processInputs: processTracedInputs,
315
348
  getInvocationParams: messagesCreateConfig.getInvocationParams,
316
349
  processOutputs: processMessageOutput,
317
350
  ...cleanedOptions,
@@ -337,7 +370,7 @@ export const wrapAnthropic = (anthropic, options) => {
337
370
  run_type: "llm",
338
371
  aggregator: messageAggregator,
339
372
  argsConfigPath: [1, "langsmithExtra"],
340
- processInputs: processSystemMessage,
373
+ processInputs: processTracedInputs,
341
374
  getInvocationParams: messagesCreateConfig.getInvocationParams,
342
375
  processOutputs: processMessageOutput,
343
376
  ...cleanedOptions,
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.wrapOpenAI = void 0;
4
4
  const traceable_js_1 = require("../traceable.cjs");
5
+ const traceable_js_2 = require("../singletons/traceable.cjs");
6
+ const ls_agent_type_js_1 = require("../utils/ls_agent_type.cjs");
5
7
  const TRACED_INVOCATION_KEYS = [
6
8
  "frequency_penalty",
7
9
  "n",
@@ -261,6 +263,7 @@ const getChatModelInvocationParamsFn = (provider, prepopulatedInvocationParams,
261
263
  ...prepopulatedInvocationParams,
262
264
  ...ls_invocation_params,
263
265
  },
266
+ ...(0, ls_agent_type_js_1.defaultLsAgentTypeMetadata)(requestMetadata, (0, traceable_js_2.getCurrentRunTree)(true)),
264
267
  };
265
268
  };
266
269
  };
@@ -416,6 +419,7 @@ const wrapOpenAI = (openai, options) => {
416
419
  ...prepopulatedInvocationParams,
417
420
  ...ls_invocation_params,
418
421
  },
422
+ ...(0, ls_agent_type_js_1.defaultLsAgentTypeMetadata)({}, (0, traceable_js_2.getCurrentRunTree)(true)),
419
423
  };
420
424
  },
421
425
  ...cleanedOptions,
@@ -1,4 +1,6 @@
1
1
  import { isTraceableFunction, traceable, } from "../traceable.js";
2
+ import { getCurrentRunTree } from "../singletons/traceable.js";
3
+ import { defaultLsAgentTypeMetadata } from "../utils/ls_agent_type.js";
2
4
  const TRACED_INVOCATION_KEYS = [
3
5
  "frequency_penalty",
4
6
  "n",
@@ -258,6 +260,7 @@ const getChatModelInvocationParamsFn = (provider, prepopulatedInvocationParams,
258
260
  ...prepopulatedInvocationParams,
259
261
  ...ls_invocation_params,
260
262
  },
263
+ ...defaultLsAgentTypeMetadata(requestMetadata, getCurrentRunTree(true)),
261
264
  };
262
265
  };
263
266
  };
@@ -413,6 +416,7 @@ export const wrapOpenAI = (openai, options) => {
413
416
  ...prepopulatedInvocationParams,
414
417
  ...ls_invocation_params,
415
418
  },
419
+ ...defaultLsAgentTypeMetadata({}, getCurrentRunTree(true)),
416
420
  };
417
421
  },
418
422
  ...cleanedOptions,
@@ -10,6 +10,7 @@ const node_async_hooks_1 = require("node:async_hooks");
10
10
  const run_trees_js_1 = require("../run_trees.cjs");
11
11
  const client_js_1 = require("../client.cjs");
12
12
  const traceable_js_1 = require("../singletons/traceable.cjs");
13
+ const ls_agent_type_js_1 = require("../utils/ls_agent_type.cjs");
13
14
  traceable_js_1.AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new node_async_hooks_1.AsyncLocalStorage());
14
15
  /**
15
16
  * Set the current AsyncLocalStorage store to the given RunTree without a
@@ -521,14 +522,15 @@ class OpenAIAgentsTracingProcessor {
521
522
  else {
522
523
  runName = "Agent workflow";
523
524
  }
524
- // Build metadata
525
- const runExtra = {
526
- metadata: {
527
- ...this._metadata,
528
- ls_integration: "openai-agents-sdk",
529
- ls_agent_type: "root",
530
- },
525
+ // Merge three sources: processor defaults, trace.metadata (user's per-run
526
+ // ls_agent_type), and force-set ls_integration.
527
+ const mergedMetadata = {
528
+ ...this._metadata,
529
+ ...(trace.metadata ?? {}),
530
+ ls_integration: "openai-agents-sdk",
531
531
  };
532
+ Object.assign(mergedMetadata, (0, ls_agent_type_js_1.defaultLsAgentTypeMetadata)(mergedMetadata, currentRunTree));
533
+ const runExtra = { metadata: mergedMetadata };
532
534
  const traceDict = trace.toJSON() ?? {};
533
535
  const groupId = trace.groupId ??
534
536
  traceDict.groupId ??
@@ -589,9 +591,10 @@ class OpenAIAgentsTracingProcessor {
589
591
  }
590
592
  this._runs.delete(trace.traceId);
591
593
  const traceDict = trace.toJSON() ?? {};
594
+ // trace.metadata may have new keys since onTraceStart; pull them in.
592
595
  const metadata = {
593
- ...traceDict.metadata,
594
596
  ...this._metadata,
597
+ ...traceDict.metadata,
595
598
  };
596
599
  try {
597
600
  // Update run with final inputs/outputs
@@ -695,10 +698,10 @@ class OpenAIAgentsTracingProcessor {
695
698
  if (!childRun.extra.metadata) {
696
699
  childRun.extra.metadata = {};
697
700
  }
698
- childRun.extra.metadata = {
699
- ...childRun.extra.metadata,
700
- ls_agent_type: "subagent",
701
- };
701
+ const meta = childRun.extra.metadata;
702
+ if (!ls_agent_type_js_1.NON_ROOT_LS_AGENT_TYPES.has(meta.ls_agent_type)) {
703
+ meta.ls_agent_type = "subagent";
704
+ }
702
705
  }
703
706
  }
704
707
  // Track span data type for parent lookups
@@ -7,6 +7,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { RunTree } from "../run_trees.js";
8
8
  import { Client } from "../client.js";
9
9
  import { AsyncLocalStorageProviderSingleton, getCurrentRunTree, } from "../singletons/traceable.js";
10
+ import { NON_ROOT_LS_AGENT_TYPES, defaultLsAgentTypeMetadata, } from "../utils/ls_agent_type.js";
10
11
  AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new AsyncLocalStorage());
11
12
  /**
12
13
  * Set the current AsyncLocalStorage store to the given RunTree without a
@@ -518,14 +519,15 @@ export class OpenAIAgentsTracingProcessor {
518
519
  else {
519
520
  runName = "Agent workflow";
520
521
  }
521
- // Build metadata
522
- const runExtra = {
523
- metadata: {
524
- ...this._metadata,
525
- ls_integration: "openai-agents-sdk",
526
- ls_agent_type: "root",
527
- },
522
+ // Merge three sources: processor defaults, trace.metadata (user's per-run
523
+ // ls_agent_type), and force-set ls_integration.
524
+ const mergedMetadata = {
525
+ ...this._metadata,
526
+ ...(trace.metadata ?? {}),
527
+ ls_integration: "openai-agents-sdk",
528
528
  };
529
+ Object.assign(mergedMetadata, defaultLsAgentTypeMetadata(mergedMetadata, currentRunTree));
530
+ const runExtra = { metadata: mergedMetadata };
529
531
  const traceDict = trace.toJSON() ?? {};
530
532
  const groupId = trace.groupId ??
531
533
  traceDict.groupId ??
@@ -586,9 +588,10 @@ export class OpenAIAgentsTracingProcessor {
586
588
  }
587
589
  this._runs.delete(trace.traceId);
588
590
  const traceDict = trace.toJSON() ?? {};
591
+ // trace.metadata may have new keys since onTraceStart; pull them in.
589
592
  const metadata = {
590
- ...traceDict.metadata,
591
593
  ...this._metadata,
594
+ ...traceDict.metadata,
592
595
  };
593
596
  try {
594
597
  // Update run with final inputs/outputs
@@ -692,10 +695,10 @@ export class OpenAIAgentsTracingProcessor {
692
695
  if (!childRun.extra.metadata) {
693
696
  childRun.extra.metadata = {};
694
697
  }
695
- childRun.extra.metadata = {
696
- ...childRun.extra.metadata,
697
- ls_agent_type: "subagent",
698
- };
698
+ const meta = childRun.extra.metadata;
699
+ if (!NON_ROOT_LS_AGENT_TYPES.has(meta.ls_agent_type)) {
700
+ meta.ls_agent_type = "subagent";
701
+ }
699
702
  }
700
703
  }
701
704
  // Track span data type for parent lookups
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.8.9",
3
+ "version": "0.8.11",
4
4
  "description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
5
5
  "packageManager": "pnpm@10.33.0",
6
6
  "files": [
@@ -481,7 +481,17 @@
481
481
  },
482
482
  "pnpm": {
483
483
  "overrides": {
484
- "js-yaml": "^4.1.1",
484
+ "@hono/node-server": "2.0.10",
485
+ "brace-expansion@<1.1.16": "1.1.16",
486
+ "brace-expansion@>=2.0.0 <2.1.2": "2.1.2",
487
+ "brace-expansion@>=3.0.0 <5.0.7": "5.0.7",
488
+ "fast-uri": "3.1.5",
489
+ "postcss": "8.5.23",
490
+ "js-yaml": "4.3.1",
491
+ "hono": "4.12.34",
492
+ "ip-address": "10.3.1",
493
+ "undici@>=7.0.0 <8.0.0": "7.29.0",
494
+ "undici@>=8.0.0 <9.0.0": "8.9.0",
485
495
  "vite": "^7.3.2",
486
496
  "form-data": "4.0.6",
487
497
  "langsmith": "0.6.0",