@introspection-sdk/introspection-node 0.13.0 → 0.15.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @introspection-sdk/introspection-node@0.13.0 build /home/runner/work/introspection-js-sdk/introspection-js-sdk/packages/introspection-node
2
+ > @introspection-sdk/introspection-node@0.15.0 build /home/runner/work/introspection-js-sdk/introspection-js-sdk/packages/introspection-node
3
3
  > tsc
4
4
 
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAuB,KAAK,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAExE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,mBAAmB;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAE1C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,WAAW,GAAG,oBAAoB,CAAC;IAEtD;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,cAAc,GAAG,uBAAuB,CAAC;IAE/D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;gBAEjB,OAAO,GAAE,0BAA+B;IAgCpD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;WACU,kBAAkB,CAC7B,MAAM,EAAE,yBAAyB,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3D,OAAO,CAAC,mBAAmB,CAAC;IAS/B,wCAAwC;IAClC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAKhC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAuB,KAAK,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGxE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,mBAAmB;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,mFAAmF;IACnF,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAE1C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,WAAW,GAAG,oBAAoB,CAAC;IAEtD;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,cAAc,GAAG,uBAAuB,CAAC;IAE/D;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;gBAEjB,OAAO,GAAE,0BAA+B;IA0CpD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;WACU,kBAAkB,CAC7B,MAAM,EAAE,yBAAyB,GAAG;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3D,OAAO,CAAC,mBAAmB,CAAC;IAS/B,wCAAwC;IAClC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAKhC"}
package/dist/client.js CHANGED
@@ -17,6 +17,7 @@ import { HttpClient } from "./http.js";
17
17
  import { attachRuntimes, } from "./resources/runtimes.js";
18
18
  import { attachExperiments, } from "./resources/experiments.js";
19
19
  import { attachRecipes } from "./resources/recipes.js";
20
+ import { DEV_TARGET_HEADER, resolveDevTarget } from "./dev-target.js";
20
21
  /**
21
22
  * Introspection REST client.
22
23
  *
@@ -61,20 +62,29 @@ export class IntrospectionClient {
61
62
  constructor(options = {}) {
62
63
  const token = options.token || process.env.INTROSPECTION_TOKEN || "";
63
64
  const advanced = options.advanced || {};
64
- this.advancedOptions = advanced;
65
65
  const baseApiUrl = advanced.baseApiUrl ||
66
66
  process.env.INTROSPECTION_BASE_API_URL ||
67
67
  "https://api.introspection.dev";
68
68
  if (!token) {
69
69
  sdkLogger.warn("IntrospectionClient: No token provided. REST calls will fail.");
70
70
  }
71
+ // INTROSPECTION_DEV_TARGET rides every request as a header so it reaches
72
+ // the paths a runner cannot: a bare `POST /v1/tasks` with a dev API key
73
+ // mints its JWT from the key row and has no per-request claim to carry.
74
+ // Merged first, so an explicit `additionalHeaders` entry still wins, and
75
+ // stored on advancedOptions so the Runner's own DP client inherits it.
76
+ const devTarget = resolveDevTarget();
77
+ const additionalHeaders = devTarget
78
+ ? { [DEV_TARGET_HEADER]: devTarget, ...advanced.additionalHeaders }
79
+ : advanced.additionalHeaders;
80
+ this.advancedOptions = { ...advanced, additionalHeaders };
71
81
  // CP HTTP client — talks to the customer-facing API with the customer
72
82
  // API key. Runners get their own HttpClient instances pointed at the
73
83
  // `deployment.endpoint` returned by `/v1/runtimes/{id}/run`.
74
84
  this.cpHttp = new HttpClient({
75
85
  apiUrl: baseApiUrl,
76
86
  token,
77
- additionalHeaders: advanced.additionalHeaders,
87
+ additionalHeaders: this.advancedOptions.additionalHeaders,
78
88
  fetch: advanced.fetch,
79
89
  });
80
90
  this.runtimes = attachRuntimes(this, this.cpHttp);
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAkC,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EACL,cAAc,GAGf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,GAGlB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,aAAa,EAAmB,MAAM,wBAAwB,CAAC;AAExE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,mBAAmB;IAC9B,2EAA2E;IAClE,MAAM,CAAa;IAC5B,mFAAmF;IAC1E,eAAe,CAAkB;IAE1C;;;;OAIG;IACM,QAAQ,CAAqC;IAEtD;;OAEG;IACM,WAAW,CAA2C;IAE/D;;;;OAIG;IACM,OAAO,CAAa;IAE7B,YAAY,UAAsC,EAAE;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,EAAE,CAAC;QACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAChC,MAAM,UAAU,GACd,QAAQ,CAAC,UAAU;YACnB,OAAO,CAAC,GAAG,CAAC,0BAA0B;YACtC,+BAA+B,CAAC;QAElC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,SAAS,CAAC,IAAI,CACZ,+DAA+D,CAChE,CAAC;QACJ,CAAC;QAED,sEAAsE;QACtE,qEAAqE;QACrE,6DAA6D;QAC7D,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC;YAC3B,MAAM,EAAE,UAAU;YAClB,KAAK;YACL,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;YAC7C,KAAK,EAAE,QAAQ,CAAC,KAAK;SACtB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,GAAG,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,CAAC,WAAW,GAAG,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE1C,SAAS,CAAC,IAAI,CAAC,wCAAwC,UAAU,EAAE,CAAC,CAAC;IACvE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAC7B,MAA4D;QAE5D,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC3D,OAAO,IAAI,mBAAmB,CAAC;YAC7B,KAAK,EAAE,YAAY;YACnB,WAAW,EAAE,MAAM,CAAC,WAAW;YAC/B,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE;SACjE,CAAC,CAAC;IACL,CAAC;IAED,wCAAwC;IACxC,KAAK,CAAC,QAAQ;QACZ,kEAAkE;QAClE,gDAAgD;QAChD,SAAS,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;CACF"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Development-target resolution — which `introspection dev` server this
3
+ * process's tasks should reach.
4
+ *
5
+ * Two developers can run `introspection dev` against one shared Runtime. A
6
+ * task created by a shared application credential carries no developer, so the
7
+ * platform cannot tell their machines apart; the caller names one instead.
8
+ * `introspection dev` prints the value to set:
9
+ *
10
+ * ```
11
+ * serving as: roland
12
+ * for your app: INTROSPECTION_DEV_TARGET=roland
13
+ * ```
14
+ *
15
+ * Carried as a header rather than on the runner's `caller` payload: `caller`
16
+ * is descriptive metadata the platform never acts on, and a target is a
17
+ * per-request selector the platform does act on. Keeping them apart is what
18
+ * lets `caller` stay a free-form bag, and it is the only transport that
19
+ * reaches a bare `POST /v1/tasks` with a dev API key, whose JWT is minted from
20
+ * the key row with no per-request input path.
21
+ *
22
+ * Deliberately env-only, with no `os.userInfo()` fallback. Defaulting to the
23
+ * local username would be zero-config on a laptop and wrong everywhere else: a
24
+ * process running in a shared development deployment under an account like
25
+ * `node` would silently name a machine nobody is serving and fail closed,
26
+ * where today it reaches the one connected dev server. The CLI defaults to the
27
+ * username because it is naming *itself* and always runs on the developer's
28
+ * machine; this names *someone else's* machine and can run anywhere.
29
+ *
30
+ * Inert outside development: the Data Plane consults a target only on the
31
+ * development pin path, so a stray value in staging or production is ignored.
32
+ */
33
+ /** Header carrying the target on requests that have no runner to ride. */
34
+ export declare const DEV_TARGET_HEADER = "x-introspection-dev-target";
35
+ /** The env var `introspection dev` prints and both the CLI and SDK read. */
36
+ export declare const DEV_TARGET_ENV = "INTROSPECTION_DEV_TARGET";
37
+ /**
38
+ * The development target for this process, or `undefined` when unset.
39
+ *
40
+ * Percent-encoded, because the value becomes an HTTP header and a header is
41
+ * bytes: a login name like `andré` is not transmissible as-is, and a runtime
42
+ * that lets it through sends latin-1 bytes the server would read back as a
43
+ * different string. Encoding here is lossless and costs nothing for the
44
+ * ordinary ASCII name, which encodes to itself.
45
+ *
46
+ * Safe to send encoded because the Data Plane decodes before it normalizes,
47
+ * so `andré` and `andr%C3%A9` land on the same target as the `--as andré`
48
+ * the CLI advertises over protobuf, where no encoding is needed.
49
+ *
50
+ * Read through `globalThis.process` so the module stays importable from
51
+ * non-Node runtimes that share this package's build.
52
+ */
53
+ export declare function resolveDevTarget(): string | undefined;
54
+ //# sourceMappingURL=dev-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-target.d.ts","sourceRoot":"","sources":["../src/dev-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAE9D,4EAA4E;AAC5E,eAAO,MAAM,cAAc,6BAA6B,CAAC;AAkBzD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAOrD"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Development-target resolution — which `introspection dev` server this
3
+ * process's tasks should reach.
4
+ *
5
+ * Two developers can run `introspection dev` against one shared Runtime. A
6
+ * task created by a shared application credential carries no developer, so the
7
+ * platform cannot tell their machines apart; the caller names one instead.
8
+ * `introspection dev` prints the value to set:
9
+ *
10
+ * ```
11
+ * serving as: roland
12
+ * for your app: INTROSPECTION_DEV_TARGET=roland
13
+ * ```
14
+ *
15
+ * Carried as a header rather than on the runner's `caller` payload: `caller`
16
+ * is descriptive metadata the platform never acts on, and a target is a
17
+ * per-request selector the platform does act on. Keeping them apart is what
18
+ * lets `caller` stay a free-form bag, and it is the only transport that
19
+ * reaches a bare `POST /v1/tasks` with a dev API key, whose JWT is minted from
20
+ * the key row with no per-request input path.
21
+ *
22
+ * Deliberately env-only, with no `os.userInfo()` fallback. Defaulting to the
23
+ * local username would be zero-config on a laptop and wrong everywhere else: a
24
+ * process running in a shared development deployment under an account like
25
+ * `node` would silently name a machine nobody is serving and fail closed,
26
+ * where today it reaches the one connected dev server. The CLI defaults to the
27
+ * username because it is naming *itself* and always runs on the developer's
28
+ * machine; this names *someone else's* machine and can run anywhere.
29
+ *
30
+ * Inert outside development: the Data Plane consults a target only on the
31
+ * development pin path, so a stray value in staging or production is ignored.
32
+ */
33
+ /** Header carrying the target on requests that have no runner to ride. */
34
+ export const DEV_TARGET_HEADER = "x-introspection-dev-target";
35
+ /** The env var `introspection dev` prints and both the CLI and SDK read. */
36
+ export const DEV_TARGET_ENV = "INTROSPECTION_DEV_TARGET";
37
+ /**
38
+ * Percent-encode to RFC 3986, so the three SDKs put identical bytes on the
39
+ * wire for the same target.
40
+ *
41
+ * `encodeURIComponent` leaves `!'()*` alone where Python's `quote(safe="")`
42
+ * and the Rust client encode them. The Data Plane decodes before it
43
+ * normalizes, so either form routes — but a header that differs by which
44
+ * client sent it is a debugging trap for no benefit.
45
+ */
46
+ function encodeTarget(value) {
47
+ return encodeURIComponent(value).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
48
+ }
49
+ /**
50
+ * The development target for this process, or `undefined` when unset.
51
+ *
52
+ * Percent-encoded, because the value becomes an HTTP header and a header is
53
+ * bytes: a login name like `andré` is not transmissible as-is, and a runtime
54
+ * that lets it through sends latin-1 bytes the server would read back as a
55
+ * different string. Encoding here is lossless and costs nothing for the
56
+ * ordinary ASCII name, which encodes to itself.
57
+ *
58
+ * Safe to send encoded because the Data Plane decodes before it normalizes,
59
+ * so `andré` and `andr%C3%A9` land on the same target as the `--as andré`
60
+ * the CLI advertises over protobuf, where no encoding is needed.
61
+ *
62
+ * Read through `globalThis.process` so the module stays importable from
63
+ * non-Node runtimes that share this package's build.
64
+ */
65
+ export function resolveDevTarget() {
66
+ const env = globalThis.process?.env;
67
+ const raw = env?.[DEV_TARGET_ENV];
68
+ const trimmed = raw?.trim();
69
+ return trimmed ? encodeTarget(trimmed) : undefined;
70
+ }
71
+ //# sourceMappingURL=dev-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-target.js","sourceRoot":"","sources":["../src/dev-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAE9D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAEzD;;;;;;;;GAQG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC,OAAO,CACtC,UAAU,EACV,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CACxD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,GAAG,GACP,UACD,CAAC,OAAO,EAAE,GAAG,CAAC;IACf,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC;IAClC,MAAM,OAAO,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC;IAC5B,OAAO,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC"}
@@ -13,6 +13,8 @@ export { setupTracing } from "./setup.js";
13
13
  export type { SetupTracingOptions, ConflictBehavior } from "./setup.js";
14
14
  export { IntrospectionSpanProcessor } from "./span-processor.js";
15
15
  export type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
16
+ export { tracedEmbeddingsCreate } from "./openai.js";
17
+ export type { OpenAIEmbeddingsClient, TracedEmbeddingsOptions, } from "./openai.js";
16
18
  export { IntrospectionTracingProcessor } from "./tracing-processor.js";
17
19
  export type { IntrospectionTracingProcessorOptions, TracingProcessorAdvancedOptions, } from "./tracing-processor.js";
18
20
  export { IntrospectionClaudeHooks } from "./claude-hooks.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/otel/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAI1D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,iCAAiC,EAAE,MAAM,qBAAqB,CAAC;AAG7E,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EACV,oCAAoC,EACpC,+BAA+B,GAChC,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EACV,+BAA+B,EAC/B,0BAA0B,EAC1B,iBAAiB,EACjB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EACV,kCAAkC,EAClC,SAAS,IAAI,WAAW,GACzB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAIjD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,WAAW,EACX,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/otel/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAI1D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,iCAAiC,EAAE,MAAM,qBAAqB,CAAC;AAG7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EACV,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,YAAY,EACV,oCAAoC,EACpC,+BAA+B,GAChC,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,YAAY,EACV,+BAA+B,EAC/B,0BAA0B,EAC1B,iBAAiB,EACjB,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AACtD,YAAY,EACV,kCAAkC,EAClC,SAAS,IAAI,WAAW,GACzB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAIjD,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAGjD,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,WAAW,EACX,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC"}
@@ -15,6 +15,8 @@ export { IntrospectionLogs } from "./logs.js";
15
15
  export { setupTracing } from "./setup.js";
16
16
  // Span processor — attach to your own TracerProvider.
17
17
  export { IntrospectionSpanProcessor } from "./span-processor.js";
18
+ // OpenAI embeddings wrapper (metadata-only; never records inputs or vectors).
19
+ export { tracedEmbeddingsCreate } from "./openai.js";
18
20
  // OpenAI Agents SDK tracing processor.
19
21
  export { IntrospectionTracingProcessor } from "./tracing-processor.js";
20
22
  // Claude Agent SDK hooks.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/otel/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,sEAAsE;AACtE,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAG9C,uEAAuE;AACvE,wDAAwD;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,sDAAsD;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGjE,uCAAuC;AACvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAMvE,0BAA0B;AAC1B,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAiB7D,4BAA4B;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAOxD,8BAA8B;AAC9B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAExB,6BAA6B;AAC7B,OAAO,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAMtD,2BAA2B;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,sEAAsE;AACtE,iCAAiC;AACjC,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AAExC,2EAA2E;AAC3E,6EAA6E;AAC7E,2CAA2C;AAC3C,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,cAAc,GACf,MAAM,WAAW,CAAC;AAGnB,4DAA4D;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,mEAAmE;AACnE,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/otel/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,sEAAsE;AACtE,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAG9C,uEAAuE;AACvE,wDAAwD;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,sDAAsD;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGjE,8EAA8E;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAMrD,uCAAuC;AACvC,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAMvE,0BAA0B;AAC1B,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAiB7D,4BAA4B;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAOxD,8BAA8B;AAC9B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAExB,6BAA6B;AAC7B,OAAO,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAMtD,2BAA2B;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,sEAAsE;AACtE,iCAAiC;AACjC,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AAExC,2EAA2E;AAC3E,6EAA6E;AAC7E,2CAA2C;AAC3C,OAAO,EACL,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,cAAc,GACf,MAAM,WAAW,CAAC;AAGnB,4DAA4D;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,mEAAmE;AACnE,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /** Privacy-preserving OpenAI embeddings instrumentation. */
2
+ import { type Tracer } from "@opentelemetry/api";
3
+ import type { CreateEmbeddingResponse, EmbeddingCreateParams } from "openai/resources/embeddings";
4
+ /** The narrow OpenAI client surface required by the embedding wrapper. */
5
+ export interface OpenAIEmbeddingsClient {
6
+ embeddings: {
7
+ create(params: EmbeddingCreateParams): Promise<CreateEmbeddingResponse>;
8
+ };
9
+ }
10
+ export interface TracedEmbeddingsOptions {
11
+ /** GenAI provider name. Defaults to `openai`. */
12
+ providerName?: string;
13
+ }
14
+ /**
15
+ * Call `client.embeddings.create` and emit a metadata-only GenAI span.
16
+ *
17
+ * Inputs and embedding vectors are deliberately never attached to the span.
18
+ */
19
+ export declare function tracedEmbeddingsCreate(tracer: Tracer, client: OpenAIEmbeddingsClient, params: EmbeddingCreateParams, options?: TracedEmbeddingsOptions): Promise<CreateEmbeddingResponse>;
20
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/otel/openai.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,OAAO,EAML,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,6BAA6B,CAAC;AAErC,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE;QACV,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;KACzE,CAAC;CACH;AAED,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAmBD;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,sBAAsB,EAC9B,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,uBAAuB,CAAC,CAsClC"}
@@ -0,0 +1,55 @@
1
+ /** Privacy-preserving OpenAI embeddings instrumentation. */
2
+ import { context, SpanKind, SpanStatusCode, trace, } from "@opentelemetry/api";
3
+ function setResponseAttributes(span, response) {
4
+ span.setAttribute("gen_ai.response.model", response.model);
5
+ span.setAttribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens);
6
+ const firstEmbedding = response.data[0]?.embedding;
7
+ if (Array.isArray(firstEmbedding)) {
8
+ span.setAttribute("gen_ai.embeddings.dimension.count", firstEmbedding.length);
9
+ }
10
+ span.setStatus({ code: SpanStatusCode.OK });
11
+ }
12
+ /**
13
+ * Call `client.embeddings.create` and emit a metadata-only GenAI span.
14
+ *
15
+ * Inputs and embedding vectors are deliberately never attached to the span.
16
+ */
17
+ export async function tracedEmbeddingsCreate(tracer, client, params, options = {}) {
18
+ const providerName = options.providerName ?? "openai";
19
+ const span = tracer.startSpan(`embeddings ${params.model}`, {
20
+ kind: SpanKind.CLIENT,
21
+ attributes: {
22
+ "gen_ai.system": providerName,
23
+ "gen_ai.provider.name": providerName,
24
+ "gen_ai.operation.name": "embeddings",
25
+ "gen_ai.request.model": params.model,
26
+ "openinference.span.kind": "EMBEDDING",
27
+ ...(params.dimensions !== undefined
28
+ ? { "gen_ai.embeddings.dimension.count": params.dimensions }
29
+ : undefined),
30
+ ...(params.encoding_format
31
+ ? { "gen_ai.request.encoding_formats": [params.encoding_format] }
32
+ : undefined),
33
+ },
34
+ });
35
+ try {
36
+ return await context.with(trace.setSpan(context.active(), span), async () => {
37
+ const response = await client.embeddings.create(params);
38
+ setResponseAttributes(span, response);
39
+ return response;
40
+ });
41
+ }
42
+ catch (error) {
43
+ span.setStatus({
44
+ code: SpanStatusCode.ERROR,
45
+ message: error instanceof Error ? error.message : String(error),
46
+ });
47
+ if (error instanceof Error)
48
+ span.recordException(error);
49
+ throw error;
50
+ }
51
+ finally {
52
+ span.end();
53
+ }
54
+ }
55
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/otel/openai.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAE5D,OAAO,EACL,OAAO,EACP,QAAQ,EACR,cAAc,EACd,KAAK,GAGN,MAAM,oBAAoB,CAAC;AAkB5B,SAAS,qBAAqB,CAC5B,IAAU,EACV,QAAiC;IAEjC,IAAI,CAAC,YAAY,CAAC,uBAAuB,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3D,IAAI,CAAC,YAAY,CAAC,2BAA2B,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAE7E,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;IACnD,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC,YAAY,CACf,mCAAmC,EACnC,cAAc,CAAC,MAAM,CACtB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,MAA8B,EAC9B,MAA6B,EAC7B,UAAmC,EAAE;IAErC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,QAAQ,CAAC;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,cAAc,MAAM,CAAC,KAAK,EAAE,EAAE;QAC1D,IAAI,EAAE,QAAQ,CAAC,MAAM;QACrB,UAAU,EAAE;YACV,eAAe,EAAE,YAAY;YAC7B,sBAAsB,EAAE,YAAY;YACpC,uBAAuB,EAAE,YAAY;YACrC,sBAAsB,EAAE,MAAM,CAAC,KAAK;YACpC,yBAAyB,EAAE,WAAW;YACtC,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS;gBACjC,CAAC,CAAC,EAAE,mCAAmC,EAAE,MAAM,CAAC,UAAU,EAAE;gBAC5D,CAAC,CAAC,SAAS,CAAC;YACd,GAAG,CAAC,MAAM,CAAC,eAAe;gBACxB,CAAC,CAAC,EAAE,iCAAiC,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE;gBACjE,CAAC,CAAC,SAAS,CAAC;SACf;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CACvB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,EACrC,KAAK,IAAI,EAAE;YACT,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACxD,qBAAqB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YACtC,OAAO,QAAQ,CAAC;QAClB,CAAC,CACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,cAAc,CAAC,KAAK;YAC1B,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAChE,CAAC,CAAC;QACH,IAAI,KAAK,YAAY,KAAK;YAAE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,KAAK,CAAC;IACd,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,GAAG,EAAE,CAAC;IACb,CAAC;AACH,CAAC"}
package/dist/otel/pi.d.ts CHANGED
@@ -43,7 +43,8 @@ export declare class IntrospectionPiInstrumentor {
43
43
  constructor(opts?: IntrospectionPiInstrumentorOptions);
44
44
  /**
45
45
  * Instrument a pi {@link Agent}:
46
- * - Wraps `agent.streamFn` to emit a `chat ${provider}` span per LLM call.
46
+ * - Wraps the agent stream function to emit a `chat ${provider}` span per
47
+ * LLM call.
47
48
  * - Subscribes to the agent loop to emit an `execute_tool ${name}` span per
48
49
  * tool execution.
49
50
  *
@@ -1 +1 @@
1
- {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../src/otel/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,qCAAqC,CAAC;AAG7C,YAAY,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAErE,MAAM,WAAW,kCAAkC;IACjD,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,2BAA2B;IACtC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,iBAAiB,CAA8B;gBAE3C,IAAI,GAAE,kCAAuC;IAOzD;;;;;;;;OAQG;IACH,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAU/C;;OAEG;IACH,IAAI,IAAI,IAAI;CAIb"}
1
+ {"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../../src/otel/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAY,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,qCAAqC,CAAC;AAG7C,YAAY,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAC;AAErE,MAAM,WAAW,kCAAkC;IACjD,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,qBAAa,2BAA2B;IACtC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,iBAAiB,CAA8B;gBAE3C,IAAI,GAAE,kCAAuC;IAOzD;;;;;;;;;OASG;IACH,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAe/C;;OAEG;IACH,IAAI,IAAI,IAAI;CAIb"}
package/dist/otel/pi.js CHANGED
@@ -41,7 +41,8 @@ export class IntrospectionPiInstrumentor {
41
41
  }
42
42
  /**
43
43
  * Instrument a pi {@link Agent}:
44
- * - Wraps `agent.streamFn` to emit a `chat ${provider}` span per LLM call.
44
+ * - Wraps the agent stream function to emit a `chat ${provider}` span per
45
+ * LLM call.
45
46
  * - Subscribes to the agent loop to emit an `execute_tool ${name}` span per
46
47
  * tool execution.
47
48
  *
@@ -49,7 +50,9 @@ export class IntrospectionPiInstrumentor {
49
50
  * @param meta - Identity metadata stamped on every span produced by this agent.
50
51
  */
51
52
  instrument(agent, meta) {
52
- agent.streamFn = instrumentStream(agent.streamFn, {
53
+ const key = "streamFunction" in agent ? "streamFunction" : "streamFn";
54
+ const instrumentable = agent;
55
+ instrumentable[key] = instrumentStream(instrumentable[key], {
53
56
  tracer: this._tracer,
54
57
  meta,
55
58
  });
@@ -1 +1 @@
1
- {"version":3,"file":"pi.js","sourceRoot":"","sources":["../../src/otel/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EACL,gBAAgB,EAChB,eAAe,GAGhB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AASxC;;;;;;GAMG;AACH,MAAM,OAAO,2BAA2B;IAC9B,OAAO,CAAqC;IAC5C,iBAAiB,GAA2B,EAAE,CAAC;IAEvD,YAAY,OAA2C,EAAE;QACvD,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,SAAS,CAC5B,IAAI,CAAC,UAAU,IAAI,kBAAkB,EACrC,OAAO,CACR,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,UAAU,CAAC,KAAY,EAAE,IAAe;QACtC,KAAK,CAAC,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,QAAQ,EAAE;YAChD,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,IAAI;SACL,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,CAAC,IAAI,CACzB,eAAe,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CACvD,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,IAAI;QACF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,iBAAiB;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;IAC9B,CAAC;CACF"}
1
+ {"version":3,"file":"pi.js","sourceRoot":"","sources":["../../src/otel/pi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EACL,gBAAgB,EAChB,eAAe,GAGhB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AASxC;;;;;;GAMG;AACH,MAAM,OAAO,2BAA2B;IAC9B,OAAO,CAAqC;IAC5C,iBAAiB,GAA2B,EAAE,CAAC;IAEvD,YAAY,OAA2C,EAAE;QACvD,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,SAAS,CAC5B,IAAI,CAAC,UAAU,IAAI,kBAAkB,EACrC,OAAO,CACR,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,UAAU,CAAC,KAAY,EAAE,IAAe;QACtC,MAAM,GAAG,GAAG,gBAAgB,IAAI,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC;QACtE,MAAM,cAAc,GAAG,KAGtB,CAAC;QACF,cAAc,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;YAC1D,MAAM,EAAE,IAAI,CAAC,OAAO;YACpB,IAAI;SACL,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB,CAAC,IAAI,CACzB,eAAe,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CACvD,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,IAAI;QACF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,iBAAiB;YAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACvD,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;IAC9B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@introspection-sdk/introspection-node",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Introspection observability SDK for Node.js",
6
6
  "engines": {
@@ -32,29 +32,29 @@
32
32
  }
33
33
  },
34
34
  "devDependencies": {
35
- "@anthropic-ai/claude-agent-sdk": "^0.3.215",
36
- "@anthropic-ai/sdk": "^0.112.3",
37
- "@earendil-works/pi-agent-core": "^0.80.10",
38
- "@earendil-works/pi-ai": "^0.80.10",
39
- "@google/genai": "^2.12.0",
35
+ "@anthropic-ai/claude-agent-sdk": "^0.3.220",
36
+ "@anthropic-ai/sdk": "^0.115.0",
37
+ "@earendil-works/pi-agent-core": "^0.82.1",
38
+ "@earendil-works/pi-ai": "^0.82.1",
39
+ "@google/genai": "^2.13.0",
40
40
  "@langchain/core": "^1.2.3",
41
- "@mastra/core": "^1.51.0",
42
- "@mastra/observability": "^1.16.1",
41
+ "@mastra/core": "^1.52.1",
42
+ "@mastra/observability": "^1.16.2",
43
43
  "@openai/agents": "^0.13.5",
44
- "@opentelemetry/api-logs": "^0.220.0",
45
- "@opentelemetry/context-async-hooks": "^2.9.0",
46
- "@opentelemetry/core": "^2.9.0",
47
- "@opentelemetry/exporter-logs-otlp-proto": "^0.220.0",
48
- "@opentelemetry/exporter-trace-otlp-proto": "^0.220.0",
49
- "@opentelemetry/resources": "^2.9.0",
50
- "@opentelemetry/sdk-logs": "^0.220.0",
51
- "@opentelemetry/sdk-trace-base": "^2.9.0",
52
- "@opentelemetry/sdk-trace-node": "^2.9.0",
44
+ "@opentelemetry/api-logs": "^0.221.0",
45
+ "@opentelemetry/context-async-hooks": "^2.10.0",
46
+ "@opentelemetry/core": "^2.10.0",
47
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.221.0",
48
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
49
+ "@opentelemetry/resources": "^2.10.0",
50
+ "@opentelemetry/sdk-logs": "^0.221.0",
51
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
52
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
53
53
  "@opentelemetry/semantic-conventions": "^1.43.0",
54
54
  "@types/node": "^22.20.1",
55
- "ai": "^6.0.230",
55
+ "ai": "^6.0.235",
56
56
  "eslint": "^9.39.5",
57
- "openai": "^6.48.0",
57
+ "openai": "^6.49.0",
58
58
  "typescript": "5.9.3",
59
59
  "@repo/eslint-config": "0.0.0",
60
60
  "@repo/typescript-config": "0.0.0"
@@ -67,6 +67,7 @@
67
67
  "@mastra/core": ">=1.0.0",
68
68
  "@mastra/observability": ">=1.0.0",
69
69
  "@openai/agents": ">=0.0.1",
70
+ "openai": ">=4.0.0",
70
71
  "@opentelemetry/api-logs": ">=0.50.0",
71
72
  "@opentelemetry/context-async-hooks": ">=1.20.0 || >=2.0.0",
72
73
  "@opentelemetry/core": ">=1.20.0 || >=2.0.0",
@@ -83,6 +84,9 @@
83
84
  "@openai/agents": {
84
85
  "optional": true
85
86
  },
87
+ "openai": {
88
+ "optional": true
89
+ },
86
90
  "@anthropic-ai/claude-agent-sdk": {
87
91
  "optional": true
88
92
  },
@@ -139,10 +143,10 @@
139
143
  "@opentelemetry/api": "^1.9.1",
140
144
  "https-proxy-agent": "^7.0.6",
141
145
  "proxy-from-env": "^2.1.0",
142
- "@introspection-sdk/http": "0.13.0",
143
- "@introspection-sdk/types": "0.13.0",
144
- "@introspection-sdk/introspection-proxy": "0.13.0",
145
- "@introspection-sdk/introspection-pi": "0.13.0"
146
+ "@introspection-sdk/http": "0.15.0",
147
+ "@introspection-sdk/introspection-proxy": "0.15.0",
148
+ "@introspection-sdk/introspection-pi": "0.15.0",
149
+ "@introspection-sdk/types": "0.15.0"
146
150
  },
147
151
  "scripts": {
148
152
  "lint": "eslint . --max-warnings 0",
package/src/client.ts CHANGED
@@ -28,6 +28,7 @@ import {
28
28
  type ExperimentsApi,
29
29
  } from "./resources/experiments.js";
30
30
  import { attachRecipes, type RecipesApi } from "./resources/recipes.js";
31
+ import { DEV_TARGET_HEADER, resolveDevTarget } from "./dev-target.js";
31
32
 
32
33
  /**
33
34
  * Introspection REST client.
@@ -77,7 +78,6 @@ export class IntrospectionClient {
77
78
  constructor(options: IntrospectionClientOptions = {}) {
78
79
  const token = options.token || process.env.INTROSPECTION_TOKEN || "";
79
80
  const advanced = options.advanced || {};
80
- this.advancedOptions = advanced;
81
81
  const baseApiUrl =
82
82
  advanced.baseApiUrl ||
83
83
  process.env.INTROSPECTION_BASE_API_URL ||
@@ -89,13 +89,24 @@ export class IntrospectionClient {
89
89
  );
90
90
  }
91
91
 
92
+ // INTROSPECTION_DEV_TARGET rides every request as a header so it reaches
93
+ // the paths a runner cannot: a bare `POST /v1/tasks` with a dev API key
94
+ // mints its JWT from the key row and has no per-request claim to carry.
95
+ // Merged first, so an explicit `additionalHeaders` entry still wins, and
96
+ // stored on advancedOptions so the Runner's own DP client inherits it.
97
+ const devTarget = resolveDevTarget();
98
+ const additionalHeaders = devTarget
99
+ ? { [DEV_TARGET_HEADER]: devTarget, ...advanced.additionalHeaders }
100
+ : advanced.additionalHeaders;
101
+ this.advancedOptions = { ...advanced, additionalHeaders };
102
+
92
103
  // CP HTTP client — talks to the customer-facing API with the customer
93
104
  // API key. Runners get their own HttpClient instances pointed at the
94
105
  // `deployment.endpoint` returned by `/v1/runtimes/{id}/run`.
95
106
  this.cpHttp = new HttpClient({
96
107
  apiUrl: baseApiUrl,
97
108
  token,
98
- additionalHeaders: advanced.additionalHeaders,
109
+ additionalHeaders: this.advancedOptions.additionalHeaders,
99
110
  fetch: advanced.fetch,
100
111
  });
101
112
 
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Development-target resolution — which `introspection dev` server this
3
+ * process's tasks should reach.
4
+ *
5
+ * Two developers can run `introspection dev` against one shared Runtime. A
6
+ * task created by a shared application credential carries no developer, so the
7
+ * platform cannot tell their machines apart; the caller names one instead.
8
+ * `introspection dev` prints the value to set:
9
+ *
10
+ * ```
11
+ * serving as: roland
12
+ * for your app: INTROSPECTION_DEV_TARGET=roland
13
+ * ```
14
+ *
15
+ * Carried as a header rather than on the runner's `caller` payload: `caller`
16
+ * is descriptive metadata the platform never acts on, and a target is a
17
+ * per-request selector the platform does act on. Keeping them apart is what
18
+ * lets `caller` stay a free-form bag, and it is the only transport that
19
+ * reaches a bare `POST /v1/tasks` with a dev API key, whose JWT is minted from
20
+ * the key row with no per-request input path.
21
+ *
22
+ * Deliberately env-only, with no `os.userInfo()` fallback. Defaulting to the
23
+ * local username would be zero-config on a laptop and wrong everywhere else: a
24
+ * process running in a shared development deployment under an account like
25
+ * `node` would silently name a machine nobody is serving and fail closed,
26
+ * where today it reaches the one connected dev server. The CLI defaults to the
27
+ * username because it is naming *itself* and always runs on the developer's
28
+ * machine; this names *someone else's* machine and can run anywhere.
29
+ *
30
+ * Inert outside development: the Data Plane consults a target only on the
31
+ * development pin path, so a stray value in staging or production is ignored.
32
+ */
33
+
34
+ /** Header carrying the target on requests that have no runner to ride. */
35
+ export const DEV_TARGET_HEADER = "x-introspection-dev-target";
36
+
37
+ /** The env var `introspection dev` prints and both the CLI and SDK read. */
38
+ export const DEV_TARGET_ENV = "INTROSPECTION_DEV_TARGET";
39
+
40
+ /**
41
+ * Percent-encode to RFC 3986, so the three SDKs put identical bytes on the
42
+ * wire for the same target.
43
+ *
44
+ * `encodeURIComponent` leaves `!'()*` alone where Python's `quote(safe="")`
45
+ * and the Rust client encode them. The Data Plane decodes before it
46
+ * normalizes, so either form routes — but a header that differs by which
47
+ * client sent it is a debugging trap for no benefit.
48
+ */
49
+ function encodeTarget(value: string): string {
50
+ return encodeURIComponent(value).replace(
51
+ /[!'()*]/g,
52
+ (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
53
+ );
54
+ }
55
+
56
+ /**
57
+ * The development target for this process, or `undefined` when unset.
58
+ *
59
+ * Percent-encoded, because the value becomes an HTTP header and a header is
60
+ * bytes: a login name like `andré` is not transmissible as-is, and a runtime
61
+ * that lets it through sends latin-1 bytes the server would read back as a
62
+ * different string. Encoding here is lossless and costs nothing for the
63
+ * ordinary ASCII name, which encodes to itself.
64
+ *
65
+ * Safe to send encoded because the Data Plane decodes before it normalizes,
66
+ * so `andré` and `andr%C3%A9` land on the same target as the `--as andré`
67
+ * the CLI advertises over protobuf, where no encoding is needed.
68
+ *
69
+ * Read through `globalThis.process` so the module stays importable from
70
+ * non-Node runtimes that share this package's build.
71
+ */
72
+ export function resolveDevTarget(): string | undefined {
73
+ const env = (
74
+ globalThis as { process?: { env?: Record<string, string | undefined> } }
75
+ ).process?.env;
76
+ const raw = env?.[DEV_TARGET_ENV];
77
+ const trimmed = raw?.trim();
78
+ return trimmed ? encodeTarget(trimmed) : undefined;
79
+ }
package/src/otel/index.ts CHANGED
@@ -22,6 +22,13 @@ export type { SetupTracingOptions, ConflictBehavior } from "./setup.js";
22
22
  export { IntrospectionSpanProcessor } from "./span-processor.js";
23
23
  export type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
24
24
 
25
+ // OpenAI embeddings wrapper (metadata-only; never records inputs or vectors).
26
+ export { tracedEmbeddingsCreate } from "./openai.js";
27
+ export type {
28
+ OpenAIEmbeddingsClient,
29
+ TracedEmbeddingsOptions,
30
+ } from "./openai.js";
31
+
25
32
  // OpenAI Agents SDK tracing processor.
26
33
  export { IntrospectionTracingProcessor } from "./tracing-processor.js";
27
34
  export type {
@@ -0,0 +1,93 @@
1
+ /** Privacy-preserving OpenAI embeddings instrumentation. */
2
+
3
+ import {
4
+ context,
5
+ SpanKind,
6
+ SpanStatusCode,
7
+ trace,
8
+ type Span,
9
+ type Tracer,
10
+ } from "@opentelemetry/api";
11
+ import type {
12
+ CreateEmbeddingResponse,
13
+ EmbeddingCreateParams,
14
+ } from "openai/resources/embeddings";
15
+
16
+ /** The narrow OpenAI client surface required by the embedding wrapper. */
17
+ export interface OpenAIEmbeddingsClient {
18
+ embeddings: {
19
+ create(params: EmbeddingCreateParams): Promise<CreateEmbeddingResponse>;
20
+ };
21
+ }
22
+
23
+ export interface TracedEmbeddingsOptions {
24
+ /** GenAI provider name. Defaults to `openai`. */
25
+ providerName?: string;
26
+ }
27
+
28
+ function setResponseAttributes(
29
+ span: Span,
30
+ response: CreateEmbeddingResponse,
31
+ ): void {
32
+ span.setAttribute("gen_ai.response.model", response.model);
33
+ span.setAttribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens);
34
+
35
+ const firstEmbedding = response.data[0]?.embedding;
36
+ if (Array.isArray(firstEmbedding)) {
37
+ span.setAttribute(
38
+ "gen_ai.embeddings.dimension.count",
39
+ firstEmbedding.length,
40
+ );
41
+ }
42
+ span.setStatus({ code: SpanStatusCode.OK });
43
+ }
44
+
45
+ /**
46
+ * Call `client.embeddings.create` and emit a metadata-only GenAI span.
47
+ *
48
+ * Inputs and embedding vectors are deliberately never attached to the span.
49
+ */
50
+ export async function tracedEmbeddingsCreate(
51
+ tracer: Tracer,
52
+ client: OpenAIEmbeddingsClient,
53
+ params: EmbeddingCreateParams,
54
+ options: TracedEmbeddingsOptions = {},
55
+ ): Promise<CreateEmbeddingResponse> {
56
+ const providerName = options.providerName ?? "openai";
57
+ const span = tracer.startSpan(`embeddings ${params.model}`, {
58
+ kind: SpanKind.CLIENT,
59
+ attributes: {
60
+ "gen_ai.system": providerName,
61
+ "gen_ai.provider.name": providerName,
62
+ "gen_ai.operation.name": "embeddings",
63
+ "gen_ai.request.model": params.model,
64
+ "openinference.span.kind": "EMBEDDING",
65
+ ...(params.dimensions !== undefined
66
+ ? { "gen_ai.embeddings.dimension.count": params.dimensions }
67
+ : undefined),
68
+ ...(params.encoding_format
69
+ ? { "gen_ai.request.encoding_formats": [params.encoding_format] }
70
+ : undefined),
71
+ },
72
+ });
73
+
74
+ try {
75
+ return await context.with(
76
+ trace.setSpan(context.active(), span),
77
+ async () => {
78
+ const response = await client.embeddings.create(params);
79
+ setResponseAttributes(span, response);
80
+ return response;
81
+ },
82
+ );
83
+ } catch (error) {
84
+ span.setStatus({
85
+ code: SpanStatusCode.ERROR,
86
+ message: error instanceof Error ? error.message : String(error),
87
+ });
88
+ if (error instanceof Error) span.recordException(error);
89
+ throw error;
90
+ } finally {
91
+ span.end();
92
+ }
93
+ }
package/src/otel/pi.ts CHANGED
@@ -25,7 +25,7 @@
25
25
  */
26
26
 
27
27
  import { trace } from "@opentelemetry/api";
28
- import type { Agent } from "@earendil-works/pi-agent-core";
28
+ import type { Agent, StreamFn } from "@earendil-works/pi-agent-core";
29
29
  import {
30
30
  instrumentStream,
31
31
  instrumentAgent,
@@ -61,7 +61,8 @@ export class IntrospectionPiInstrumentor {
61
61
 
62
62
  /**
63
63
  * Instrument a pi {@link Agent}:
64
- * - Wraps `agent.streamFn` to emit a `chat ${provider}` span per LLM call.
64
+ * - Wraps the agent stream function to emit a `chat ${provider}` span per
65
+ * LLM call.
65
66
  * - Subscribes to the agent loop to emit an `execute_tool ${name}` span per
66
67
  * tool execution.
67
68
  *
@@ -69,7 +70,12 @@ export class IntrospectionPiInstrumentor {
69
70
  * @param meta - Identity metadata stamped on every span produced by this agent.
70
71
  */
71
72
  instrument(agent: Agent, meta: AgentMeta): void {
72
- agent.streamFn = instrumentStream(agent.streamFn, {
73
+ const key = "streamFunction" in agent ? "streamFunction" : "streamFn";
74
+ const instrumentable = agent as unknown as Record<
75
+ "streamFunction" | "streamFn",
76
+ StreamFn
77
+ >;
78
+ instrumentable[key] = instrumentStream(instrumentable[key], {
73
79
  tracer: this._tracer,
74
80
  meta,
75
81
  });