@rasputin-ai/node 0.3.0 → 0.4.1

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 (26) hide show
  1. package/README.md +18 -4
  2. package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts +6 -0
  3. package/dist/auto-instrumentation/collect-instrumentation-delta.d.ts.map +1 -1
  4. package/dist/auto-instrumentation/format-configuration-report.d.ts +14 -0
  5. package/dist/auto-instrumentation/format-configuration-report.d.ts.map +1 -0
  6. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts +9 -3
  7. package/dist/auto-instrumentation/instrumentation-manifest-registry.d.ts.map +1 -1
  8. package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts +11 -1
  9. package/dist/auto-instrumentation/schedule-instrumentation-manifest-upload.d.ts.map +1 -1
  10. package/dist/execution-recorder/automatic-runtime.d.ts +11 -8
  11. package/dist/execution-recorder/automatic-runtime.d.ts.map +1 -1
  12. package/dist/execution-recorder/execution-recorder-types.d.ts +10 -3
  13. package/dist/execution-recorder/execution-recorder-types.d.ts.map +1 -1
  14. package/dist/execution-recorder/execution-recorder.d.ts.map +1 -1
  15. package/dist/execution-recorder/index.d.ts +1 -1
  16. package/dist/execution-recorder/index.d.ts.map +1 -1
  17. package/dist/execution-recorder/source-exclusions.d.ts +2 -1
  18. package/dist/execution-recorder/source-exclusions.d.ts.map +1 -1
  19. package/dist/index.js +322 -113
  20. package/dist/instrument/bun.js +33 -21
  21. package/dist/instrument/node.js +32 -20
  22. package/dist/rasputin-init.d.ts +3 -2
  23. package/dist/rasputin-init.d.ts.map +1 -1
  24. package/dist/sdk-meta.d.ts +2 -2
  25. package/dist/sdk-meta.d.ts.map +1 -1
  26. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5,23 +5,133 @@ import {
5
5
  uploadInstrumentationManifest
6
6
  } from "@rasputin-ai/core";
7
7
 
8
+ // src/auto-instrumentation/format-configuration-report.ts
9
+ var RULE = "-".repeat(64);
10
+ var BANNER = [
11
+ RULE,
12
+ " ____ _ ____ ____ _ _ _____ ___ _ _",
13
+ "| _ \\ / \\ / ___| | _ \\ | | | | |_ _| |_ _| | \\ | |",
14
+ "| |_) | / _ \\ \\___ \\ | |_) | | | | | | | | | | \\| |",
15
+ "| _ < / ___ \\ ___) | | __/ | |_| | | | | | | |\\ |",
16
+ "|_| \\_\\ /_/ \\_\\ |____/ |_| \\___/ |_| |___| |_| \\_|",
17
+ "",
18
+ "",
19
+ "CONFIGURATION:"
20
+ ];
21
+ var counted = (n, singular, plural) => `${n} ${n === 1 ? singular : plural}`;
22
+ var issueHeadline = (issue) => {
23
+ const target = issue.packageName ? `package ${issue.packageName}` : issue.packageRelativePath;
24
+ if (issue.reason === "ambiguous") return `Could not uniquely locate ${target}.`;
25
+ if (issue.reason === "not_found") return `Could not find ${target} in this Git release.`;
26
+ if (issue.reason === "invalid_mapping") {
27
+ return `The configured package root for ${target} does not match this Git release.`;
28
+ }
29
+ return `GitHub could not be reached while locating ${target}.`;
30
+ };
31
+ var formatIssue = (issue) => {
32
+ const lines = [
33
+ `! ${issueHeadline(issue)}`,
34
+ "",
35
+ " Captured:",
36
+ ` ${issue.packageRelativePath}`
37
+ ];
38
+ if (issue.candidates?.length) {
39
+ lines.push("", " Possible repository locations:");
40
+ for (const candidate of issue.candidates) lines.push(` ${candidate}`);
41
+ }
42
+ return lines;
43
+ };
44
+ var formatConfigurationReport = (input) => {
45
+ const packages = input.verification.packages ?? [];
46
+ const unresolved = input.verification.unresolved;
47
+ const mappingIssues = unresolved.filter((issue) => issue.reason !== "github_unavailable");
48
+ const githubUnavailable = input.verification.status === "unavailable" || unresolved.some((issue) => issue.reason === "github_unavailable");
49
+ const verified = input.uploaded && input.verification.status === "verified";
50
+ const lines = [
51
+ ...BANNER,
52
+ "",
53
+ "Release",
54
+ `\u2713 ${input.release}`,
55
+ "",
56
+ "Runtime",
57
+ `\u2713 ${input.runtime}`,
58
+ "",
59
+ "Instrumentation",
60
+ `\u2713 ${counted(input.modules, "application module", "application modules")} observed`,
61
+ `\u2713 ${counted(input.functions, "function", "functions")} instrumented`,
62
+ `\u2713 ${counted(input.packageNames.length, "workspace package", "workspace packages")} detected`
63
+ ];
64
+ if (packages.length > 0 || mappingIssues.length > 0) {
65
+ lines.push("", "Package roots");
66
+ for (const mapping of packages) {
67
+ lines.push(
68
+ mapping.packageName ? `\u2713 ${mapping.packageName} \u2192 ${mapping.repositoryRoot}` : `\u2713 ${mapping.repositoryRoot}`
69
+ );
70
+ }
71
+ for (const [index, issue] of mappingIssues.entries()) {
72
+ if (packages.length > 0 || index > 0) lines.push("");
73
+ lines.push(...formatIssue(issue));
74
+ }
75
+ }
76
+ lines.push("", "Manifest");
77
+ if (!input.uploaded) {
78
+ lines.push("! upload failed");
79
+ } else {
80
+ lines.push("\u2713 uploaded");
81
+ lines.push(
82
+ verified ? "\u2713 verified against GitHub" : "! some source files could not be mapped to this Git release"
83
+ );
84
+ }
85
+ lines.push("", "GitHub");
86
+ if (!input.uploaded) {
87
+ lines.push("! not verified");
88
+ } else if (githubUnavailable) {
89
+ lines.push("! repository is not connected, or this release could not be read");
90
+ } else {
91
+ lines.push("\u2713 repository connected");
92
+ }
93
+ if (input.uploaded && input.verification.status !== "verified") {
94
+ lines.push("", "Runtime recording is unaffected.");
95
+ }
96
+ lines.push(RULE);
97
+ return lines.join("\n");
98
+ };
99
+ var runtimeLabel = () => {
100
+ const bun = globalThis.Bun;
101
+ if (typeof bun?.version === "string" && bun.version) return `Bun ${bun.version}`;
102
+ return `Node ${process.versions.node}`;
103
+ };
104
+
8
105
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
9
- import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
106
+ import { createHash } from "node:crypto";
107
+ import {
108
+ INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
109
+ sourceLocatorFromFile as sourceLocatorFromFile2
110
+ } from "@rasputin-ai/core";
10
111
 
11
112
  // src/execution-recorder/automatic-runtime.ts
12
- import { isAbsolute, relative, resolve, sep } from "node:path";
13
- var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v1";
14
- var AUTOMATIC_SOURCE_SEPARATOR = "\0";
113
+ import {
114
+ runtimeFunctionId,
115
+ sourceLocatorFromFile
116
+ } from "@rasputin-ai/core";
117
+ var AUTOMATIC_RUNTIME_SYMBOL = "rasputin.execution.runtime.v2";
118
+ var AUTOMATIC_SOURCE_PREFIX = "rasputin-source-v2:";
15
119
  var runtimeSymbol = Symbol.for(AUTOMATIC_RUNTIME_SYMBOL);
16
- var normalizeAutomaticFunctionId = (sourceToken, repoRoot) => {
17
- if (!sourceToken.includes(AUTOMATIC_SOURCE_SEPARATOR)) return sourceToken;
18
- const [filePath, name, line, column] = sourceToken.split(AUTOMATIC_SOURCE_SEPARATOR);
19
- if (!filePath || !name || !line || !column) return void 0;
20
- const fromRoot = relative(repoRoot, resolve(filePath));
21
- if (!fromRoot || fromRoot === ".." || fromRoot.startsWith(`..${sep}`) || isAbsolute(fromRoot)) {
120
+ var decodeAutomaticFunctionSource = (sourceToken) => {
121
+ if (!sourceToken.startsWith(AUTOMATIC_SOURCE_PREFIX)) return void 0;
122
+ try {
123
+ const parsed = JSON.parse(
124
+ sourceToken.slice(AUTOMATIC_SOURCE_PREFIX.length)
125
+ );
126
+ if (typeof parsed.name !== "string" || !parsed.name) return void 0;
127
+ const definition = {
128
+ name: parsed.name,
129
+ ...parsed.source ? { source: parsed.source } : {}
130
+ };
131
+ return { ...definition, functionId: runtimeFunctionId(definition) };
132
+ } catch {
22
133
  return void 0;
23
134
  }
24
- return `${fromRoot.replaceAll("\\", "/")}:${name}@${line}:${column}`;
25
135
  };
26
136
  var getRegistry = () => {
27
137
  const existing = Reflect.get(globalThis, runtimeSymbol);
@@ -32,30 +142,25 @@ var getRegistry = () => {
32
142
  const registry = {
33
143
  __rasputinRuntimeRegistry: true,
34
144
  runtimes,
35
- run: (functionId, args, callback) => {
145
+ run: (sourceToken, args, callback) => {
36
146
  const runtime = runtimes.at(-1);
37
- return runtime ? runtime.run(functionId, args, callback) : callback();
147
+ return runtime ? runtime.run(sourceToken, args, callback) : callback();
38
148
  }
39
149
  };
40
150
  Reflect.set(globalThis, runtimeSymbol, registry);
41
151
  return registry;
42
152
  };
43
- var installAutomaticExecutionRuntime = (execution, options = {}) => {
153
+ var installAutomaticExecutionRuntime = (execution) => {
44
154
  const registry = getRegistry();
45
- const repoRoot = options.repoRoot ? resolve(options.repoRoot) : void 0;
46
- const normalizedIds = /* @__PURE__ */ new Map();
155
+ const definitions = /* @__PURE__ */ new Map();
47
156
  const runtime = {
48
157
  run: (sourceToken, args, callback) => {
49
- if (!sourceToken.includes(AUTOMATIC_SOURCE_SEPARATOR)) {
50
- return execution.runFunction(sourceToken, args, callback);
158
+ let definition = definitions.get(sourceToken);
159
+ if (!definitions.has(sourceToken)) {
160
+ definition = decodeAutomaticFunctionSource(sourceToken);
161
+ definitions.set(sourceToken, definition);
51
162
  }
52
- if (!repoRoot) return callback();
53
- let functionId = normalizedIds.get(sourceToken);
54
- if (!normalizedIds.has(sourceToken)) {
55
- functionId = normalizeAutomaticFunctionId(sourceToken, repoRoot);
56
- normalizedIds.set(sourceToken, functionId);
57
- }
58
- return functionId ? execution.runFunction(functionId, args, callback) : callback();
163
+ return definition ? execution.runFunction(definition, args, callback) : callback();
59
164
  }
60
165
  };
61
166
  registry.runtimes.push(runtime);
@@ -66,7 +171,7 @@ var installAutomaticExecutionRuntime = (execution, options = {}) => {
66
171
  };
67
172
 
68
173
  // src/auto-instrumentation/instrumentation-manifest-registry.ts
69
- var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v1";
174
+ var INSTRUMENTATION_MANIFEST_SYMBOL = "rasputin.instrumentation.manifest.v2";
70
175
  var registrySymbol = Symbol.for(INSTRUMENTATION_MANIFEST_SYMBOL);
71
176
  var getRegistry2 = () => {
72
177
  const existing = Reflect.get(globalThis, registrySymbol);
@@ -82,31 +187,28 @@ var getRegistry2 = () => {
82
187
  Reflect.set(globalThis, registrySymbol, registry);
83
188
  return registry;
84
189
  };
85
- var repoRelativePath = (filePath, repoRoot) => {
86
- const fromRoot = relative2(repoRoot, resolve2(filePath));
87
- if (!fromRoot || fromRoot === ".." || fromRoot.startsWith(`..${sep2}`) || isAbsolute2(fromRoot)) {
88
- return void 0;
89
- }
90
- return fromRoot.replaceAll("\\", "/");
190
+ var sourceAt = (filePath, line, column) => {
191
+ const source = sourceLocatorFromFile2(filePath);
192
+ return source ? { ...source, line, column } : void 0;
91
193
  };
194
+ var stableId = (kind, ...parts) => `${kind}_${createHash("sha256").update(parts.join("\0")).digest("base64url").slice(0, 24)}`;
92
195
  var isInstrumentationManifestDirty = () => {
93
196
  const registry = getRegistry2();
94
197
  return registry.generation !== registry.uploadedGeneration && registry.files.size > 0;
95
198
  };
96
199
  var instrumentationManifestGeneration = () => getRegistry2().generation;
97
- var snapshotInstrumentationManifest = (repoRoot) => {
200
+ var instrumentationManifestModuleCount = () => getRegistry2().files.size;
201
+ var snapshotInstrumentationManifest = (options = {}) => {
98
202
  const functions = [];
99
203
  const callSites = [];
100
204
  const destructures = [];
101
- const root = resolve2(repoRoot);
102
205
  for (const delta of getRegistry2().files.values()) {
103
206
  for (const fn of delta.functions) {
104
- const functionId = normalizeAutomaticFunctionId(fn.sourceToken, root);
105
- const path = repoRelativePath(fn.filePath, root);
106
- if (!functionId || !path) continue;
207
+ const definition = decodeAutomaticFunctionSource(fn.sourceToken);
208
+ if (!definition?.source) continue;
107
209
  functions.push({
108
- functionId,
109
- path,
210
+ functionId: definition.functionId,
211
+ source: definition.source,
110
212
  name: fn.name,
111
213
  startLine: fn.startLine,
112
214
  startColumn: fn.startColumn,
@@ -116,15 +218,22 @@ var snapshotInstrumentationManifest = (repoRoot) => {
116
218
  });
117
219
  }
118
220
  for (const site of delta.callSites) {
119
- const callerFunctionId = normalizeAutomaticFunctionId(site.callerSourceToken, root);
120
- const path = repoRelativePath(site.filePath, root);
121
- if (!callerFunctionId || !path) continue;
221
+ const caller = decodeAutomaticFunctionSource(site.callerSourceToken);
222
+ const source = sourceAt(site.filePath, site.line, site.column);
223
+ if (!caller || !source) continue;
122
224
  callSites.push({
123
- id: `${path}:${site.line}:${site.column}`,
124
- callerFunctionId,
225
+ id: stableId(
226
+ "cs",
227
+ caller.functionId,
228
+ source.packageName ?? "",
229
+ source.packageRelativePath,
230
+ site.line,
231
+ site.column
232
+ ),
233
+ callerFunctionId: caller.functionId,
125
234
  calleeName: site.calleeName,
126
235
  calleeText: site.calleeText,
127
- path,
236
+ source,
128
237
  line: site.line,
129
238
  column: site.column,
130
239
  endLine: site.endLine,
@@ -134,13 +243,13 @@ var snapshotInstrumentationManifest = (repoRoot) => {
134
243
  });
135
244
  }
136
245
  for (const destructure of delta.destructures) {
137
- const callerFunctionId = normalizeAutomaticFunctionId(destructure.callerSourceToken, root);
138
- const path = repoRelativePath(destructure.filePath, root);
139
- if (!callerFunctionId || !path) continue;
246
+ const caller = decodeAutomaticFunctionSource(destructure.callerSourceToken);
247
+ const source = sourceAt(destructure.filePath, destructure.line, destructure.column);
248
+ if (!caller || !source) continue;
140
249
  destructures.push({
141
- callerFunctionId,
250
+ callerFunctionId: caller.functionId,
142
251
  sourceParam: destructure.sourceParam,
143
- path,
252
+ source,
144
253
  line: destructure.line,
145
254
  column: destructure.column,
146
255
  binding: destructure.binding
@@ -150,8 +259,15 @@ var snapshotInstrumentationManifest = (repoRoot) => {
150
259
  if (functions.length === 0 && callSites.length === 0 && destructures.length === 0) {
151
260
  return void 0;
152
261
  }
262
+ const packages = { ...options.sourceRoots ?? {} };
153
263
  return {
154
- schema_version: 1,
264
+ schema_version: INSTRUMENTATION_MANIFEST_SCHEMA_VERSION,
265
+ ...options.sourceRoot || Object.keys(packages).length > 0 ? {
266
+ sourceRoots: {
267
+ ...options.sourceRoot ? { default: options.sourceRoot } : {},
268
+ packages
269
+ }
270
+ } : {},
155
271
  functions,
156
272
  callSites,
157
273
  destructures
@@ -159,35 +275,63 @@ var snapshotInstrumentationManifest = (repoRoot) => {
159
275
  };
160
276
  var markInstrumentationManifestUploaded = (generation) => {
161
277
  const registry = getRegistry2();
162
- if (registry.generation === generation) {
163
- registry.uploadedGeneration = generation;
164
- }
278
+ if (registry.generation === generation) registry.uploadedGeneration = generation;
165
279
  };
166
280
  var setInstrumentationManifestOnDirty = (onDirty) => {
167
281
  getRegistry2().onDirty = onDirty;
168
282
  };
169
283
 
170
284
  // src/auto-instrumentation/schedule-instrumentation-manifest-upload.ts
285
+ var unavailable = () => ({
286
+ status: "unavailable",
287
+ verified: 0,
288
+ unresolved: [],
289
+ packages: []
290
+ });
171
291
  var noopHandle = {
172
292
  flushSoon() {
173
293
  },
174
294
  async wait() {
175
295
  },
176
296
  disconnect() {
297
+ },
298
+ async verify() {
299
+ return unavailable();
177
300
  }
178
301
  };
179
302
  var scheduleInstrumentationManifestUpload = (options) => {
180
303
  const release = detectRelease({ release: options.release });
181
- const repoRoot = options.repoRoot;
182
- if (!options.enabled || !release || !repoRoot || !options.projectApiKey.trim()) {
304
+ if (!options.enabled || !release || !options.projectApiKey.trim()) {
183
305
  return noopHandle;
184
306
  }
185
307
  const { instrumentationManifestUrl } = resolveEndpointUrls(options.apiUrl);
186
308
  let inflight;
309
+ let verification = unavailable();
310
+ let loggedSuccess = false;
311
+ let loggedFailure = false;
312
+ const packageNamesOf = (manifest) => [
313
+ ...new Set(
314
+ manifest.functions.map((fn) => fn.source.packageName).filter((name) => Boolean(name))
315
+ )
316
+ ].sort();
317
+ const report = (manifest, uploaded, next) => {
318
+ return formatConfigurationReport({
319
+ release,
320
+ runtime: runtimeLabel(),
321
+ modules: instrumentationManifestModuleCount(),
322
+ functions: manifest.functions.length,
323
+ packageNames: packageNamesOf(manifest),
324
+ verification: next,
325
+ uploaded
326
+ });
327
+ };
187
328
  const flushSoon = () => {
188
329
  if (!isInstrumentationManifestDirty()) return;
189
330
  const generation = instrumentationManifestGeneration();
190
- const manifest = snapshotInstrumentationManifest(repoRoot);
331
+ const manifest = snapshotInstrumentationManifest({
332
+ sourceRoot: options.sourceRoot,
333
+ sourceRoots: options.sourceRoots
334
+ });
191
335
  if (!manifest) {
192
336
  markInstrumentationManifestUploaded(generation);
193
337
  return;
@@ -199,7 +343,26 @@ var scheduleInstrumentationManifestUpload = (options) => {
199
343
  manifest,
200
344
  fetch: options.fetch
201
345
  }).then((result) => {
202
- if (result === "ok") markInstrumentationManifestUploaded(generation);
346
+ if (result.ok) {
347
+ verification = result.verification;
348
+ markInstrumentationManifestUploaded(generation);
349
+ if (verification.status === "verified") {
350
+ if (!loggedSuccess && options.logSuccess !== false) {
351
+ loggedSuccess = true;
352
+ console.info(report(manifest, true, verification));
353
+ }
354
+ return;
355
+ }
356
+ if (!loggedFailure) {
357
+ loggedFailure = true;
358
+ console.warn(report(manifest, true, verification));
359
+ }
360
+ return;
361
+ }
362
+ if (!loggedFailure) {
363
+ loggedFailure = true;
364
+ console.warn(report(manifest, false, unavailable()));
365
+ }
203
366
  });
204
367
  inflight = inflight ? inflight.then(() => work) : work;
205
368
  };
@@ -212,12 +375,18 @@ var scheduleInstrumentationManifestUpload = (options) => {
212
375
  },
213
376
  disconnect: () => {
214
377
  setInstrumentationManifestOnDirty(void 0);
378
+ },
379
+ verify: async () => {
380
+ flushSoon();
381
+ await inflight;
382
+ return verification;
215
383
  }
216
384
  };
217
385
  };
218
386
 
219
387
  // src/execution-recorder/execution-recorder.ts
220
388
  import { AsyncLocalStorage } from "node:async_hooks";
389
+ import { runtimeFunctionId as runtimeFunctionId2 } from "@rasputin-ai/core";
221
390
 
222
391
  // src/execution-recorder/call-aware-event-buffer.ts
223
392
  var CallAwareEventBuffer = class {
@@ -577,9 +746,10 @@ var safeSerializeWithBytes = (value, options = {}) => {
577
746
  var safeSerialize = (value, options = {}) => safeSerializeWithBytes(value, options).value;
578
747
 
579
748
  // src/execution-recorder/source-exclusions.ts
580
- var sourcePathFrom = (functionId) => {
581
- const separator = functionId.indexOf(":");
582
- return (separator < 0 ? functionId : functionId.slice(0, separator)).replaceAll("\\", "/");
749
+ var sourcePathFrom = (definition) => definition.source?.packageRelativePath.replaceAll("\\", "/") ?? definition.name;
750
+ var searchableIdentity = (definition) => {
751
+ const source = definition.source;
752
+ return source ? `${source.packageName ? `${source.packageName}:` : ""}${source.packageRelativePath}:${definition.name}@${source.line ?? ""}:${source.column ?? ""}` : definition.name;
583
753
  };
584
754
  var normalizePattern = (pattern) => pattern.trim().replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
585
755
  var globRegex = (pattern) => {
@@ -610,35 +780,51 @@ var globRegex = (pattern) => {
610
780
  return new RegExp(`^${expression}$`);
611
781
  };
612
782
  var createSourceExclusionMatcher = (exclusions) => {
613
- const matchers = (exclusions ?? []).flatMap((exclusion) => {
614
- if (exclusion instanceof RegExp) {
783
+ const matchers = (exclusions ?? []).flatMap(
784
+ (exclusion) => {
785
+ if (exclusion instanceof RegExp) {
786
+ return [
787
+ (definition) => {
788
+ exclusion.lastIndex = 0;
789
+ const matched = exclusion.test(searchableIdentity(definition));
790
+ exclusion.lastIndex = 0;
791
+ return matched;
792
+ }
793
+ ];
794
+ }
795
+ const pattern = normalizePattern(exclusion);
796
+ if (!pattern) return [];
797
+ if (pattern.includes("*") || pattern.includes("?")) {
798
+ const regex = globRegex(pattern);
799
+ return [(definition) => regex.test(sourcePathFrom(definition))];
800
+ }
615
801
  return [
616
- (functionId) => {
617
- exclusion.lastIndex = 0;
618
- const matched = exclusion.test(functionId);
619
- exclusion.lastIndex = 0;
620
- return matched;
802
+ (definition) => {
803
+ const sourcePath = sourcePathFrom(definition);
804
+ return sourcePath === pattern || sourcePath.startsWith(`${pattern}/`);
621
805
  }
622
806
  ];
623
807
  }
624
- const pattern = normalizePattern(exclusion);
625
- if (!pattern) return [];
626
- if (pattern.includes("*") || pattern.includes("?")) {
627
- const regex = globRegex(pattern);
628
- return [(functionId) => regex.test(sourcePathFrom(functionId))];
629
- }
630
- return [
631
- (functionId) => {
632
- const sourcePath = sourcePathFrom(functionId);
633
- return sourcePath === pattern || sourcePath.startsWith(`${pattern}/`);
634
- }
635
- ];
636
- });
637
- return matchers.length === 0 ? () => false : (functionId) => matchers.some((matcher) => matcher(functionId));
808
+ );
809
+ return matchers.length === 0 ? () => false : (definition) => matchers.some((matcher) => matcher(definition));
638
810
  };
639
811
 
640
812
  // src/execution-recorder/execution-recorder.ts
641
813
  var DEFAULT_MAX_ACTIVE_MEMORY_BYTES = 64 * 1024 * 1024;
814
+ var manualFunctionDefinition = (value) => {
815
+ const located = /^(.+):([^:@]+)@(\d+):(\d+)$/.exec(value);
816
+ const named = located ? void 0 : /^(.+):([^:@]+)$/.exec(value);
817
+ const path = located?.[1] ?? named?.[1];
818
+ const name = located?.[2] ?? named?.[2] ?? value;
819
+ const source = path ? {
820
+ packageRelativePath: path.replaceAll("\\", "/").replace(/^\.\//, ""),
821
+ ...located?.[3] ? { line: Number(located[3]) } : {},
822
+ ...located?.[4] ? { column: Number(located[4]) } : {}
823
+ } : void 0;
824
+ const definition = { name, ...source ? { source } : {} };
825
+ return { ...definition, functionId: runtimeFunctionId2(definition) };
826
+ };
827
+ var normalizeFunctionDefinition = (value) => typeof value === "string" ? manualFunctionDefinition(value) : value;
642
828
  var elapsedMs = (startedAtNs, endedAtNs = process.hrtime.bigint()) => Number(endedAtNs - startedAtNs) / 1e6;
643
829
  var roundedMilliseconds = (nanoseconds) => Math.round(Number(nanoseconds) / 1e6 * 1e4) / 1e4;
644
830
  var addRecorderTime = (recording, nanoseconds) => {
@@ -734,7 +920,10 @@ var noOpExecution = {
734
920
  memoryPressureDegradations: 0,
735
921
  valuesDroppedByMemoryBudget: 0,
736
922
  eventsDroppedByMemoryBudget: 0,
737
- partialErrorSnapshots: 0
923
+ partialErrorSnapshots: 0,
924
+ measuredExecutions: 0,
925
+ totalInlineWallTimeMs: 0,
926
+ maxInlineWallTimeMs: 0
738
927
  })
739
928
  };
740
929
  var mergeExecutionMetadata = (target, metadata) => {
@@ -768,13 +957,28 @@ var createExecutionRecorder = (options) => {
768
957
  repeatedCallsSuppressed: 0,
769
958
  activeExecutions: 0,
770
959
  peakActiveExecutions: 0,
771
- partialErrorSnapshots: 0
960
+ partialErrorSnapshots: 0,
961
+ measuredExecutions: 0,
962
+ totalInlineWallTimeMs: 0,
963
+ maxInlineWallTimeMs: 0
964
+ };
965
+ const recordInlineTiming = (recording) => {
966
+ const inlineWallTimeMs = roundedMilliseconds(recording.inlineRecorderTimeNs);
967
+ stats.measuredExecutions++;
968
+ stats.totalInlineWallTimeMs = Math.round((stats.totalInlineWallTimeMs + inlineWallTimeMs) * 1e4) / 1e4;
969
+ stats.maxInlineWallTimeMs = Math.max(stats.maxInlineWallTimeMs, inlineWallTimeMs);
772
970
  };
773
971
  const captureValue = (recording, value) => memoryBudget.captureValue(recording.memory, () => safeSerializeWithBytes(value, options));
774
972
  const stateFrom = (recording, endedAtNs = process.hrtime.bigint()) => {
775
973
  const recorderStartedAtNs = process.hrtime.bigint();
974
+ const events = recording.events.values().map((event) => event.type === "function_calls_suppressed" ? { ...event } : event);
975
+ const referencedFunctionIds = new Set(
976
+ events.flatMap(
977
+ (event) => event.type === "function_enter" || event.type === "function_calls_suppressed" ? [event.functionId] : []
978
+ )
979
+ );
776
980
  const state = {
777
- version: 1,
981
+ version: 2,
778
982
  executionId: recording.executionId,
779
983
  startedAt: recording.startedAt,
780
984
  durationMs: elapsedMs(recording.startedAtNs, endedAtNs),
@@ -783,7 +987,10 @@ var createExecutionRecorder = (options) => {
783
987
  ...recording.execution,
784
988
  ...recording.execution.request ? { request: { ...recording.execution.request } } : {}
785
989
  },
786
- events: recording.events.values().map((event) => event.type === "function_calls_suppressed" ? { ...event } : event),
990
+ functions: [...recording.functions.values()].filter(
991
+ (definition) => referencedFunctionIds.has(definition.functionId)
992
+ ),
993
+ events,
787
994
  ...recording.events.dropped > 0 ? { truncated: { eventsDropped: recording.events.dropped } } : {},
788
995
  ...recording.memory.underPressure ? {
789
996
  capture: {
@@ -911,6 +1118,7 @@ var createExecutionRecorder = (options) => {
911
1118
  if (!recording.hasError) stats.successfulExecutionsDiscarded++;
912
1119
  recording.finishedAtNs = process.hrtime.bigint();
913
1120
  addRecorderTime(recording, recording.finishedAtNs - recorderStartedAtNs);
1121
+ recordInlineTiming(recording);
914
1122
  };
915
1123
  const createScope = (metadata) => {
916
1124
  const recorderStartedAtNs = process.hrtime.bigint();
@@ -925,6 +1133,7 @@ var createExecutionRecorder = (options) => {
925
1133
  ...metadata.request ? { request: { ...metadata.request } } : {}
926
1134
  },
927
1135
  events: new CallAwareEventBuffer(maxEventsPerExecution),
1136
+ functions: /* @__PURE__ */ new Map(),
928
1137
  repeatedCalls: /* @__PURE__ */ new Map(),
929
1138
  capturedErrors: /* @__PURE__ */ new WeakMap(),
930
1139
  nextCallId: 0,
@@ -993,16 +1202,19 @@ var createExecutionRecorder = (options) => {
993
1202
  return void 0;
994
1203
  }
995
1204
  };
996
- const runFunction = (functionId, args, callback) => {
1205
+ const runFunction = (input, args, callback) => {
997
1206
  const store = storage.getStore();
998
1207
  if (!store || store.recording.finished) return callback();
1208
+ const definition = normalizeFunctionDefinition(input);
1209
+ const functionId = definition.functionId;
999
1210
  const recorderStartedAtNs = process.hrtime.bigint();
1000
- if (store.suppressCapture || excludesSource(functionId)) {
1211
+ if (store.suppressCapture || excludesSource(definition)) {
1001
1212
  return storage.run({ ...store, suppressCapture: true }, () => {
1002
1213
  addRecorderTime(store.recording, process.hrtime.bigint() - recorderStartedAtNs);
1003
1214
  return callback();
1004
1215
  });
1005
1216
  }
1217
+ store.recording.functions.set(functionId, definition);
1006
1218
  const parentCallId = store.currentCallId;
1007
1219
  const group = repeatedCallGroup(store.recording, functionId, parentCallId);
1008
1220
  group.observedCalls++;
@@ -1111,9 +1323,10 @@ var createExecutionRecorder = (options) => {
1111
1323
  )
1112
1324
  );
1113
1325
  };
1114
- function trace(functionId, fn) {
1326
+ function trace(input, fn) {
1327
+ const definition = typeof input === "string" ? manualFunctionDefinition(input) : { ...input, functionId: runtimeFunctionId2(input) };
1115
1328
  return function traced(...args) {
1116
- return runFunction(functionId, args, () => fn.apply(this, args));
1329
+ return runFunction(definition, args, () => fn.apply(this, args));
1117
1330
  };
1118
1331
  }
1119
1332
  return {
@@ -1177,26 +1390,23 @@ var installGlobalHandlers = (client, options = {}) => {
1177
1390
  // src/rasputin-init.ts
1178
1391
  import {
1179
1392
  createClient,
1180
- isClientEnabled,
1181
- resolveRepoRoot
1393
+ isClientEnabled
1182
1394
  } from "@rasputin-ai/core";
1183
1395
 
1184
1396
  // src/sdk-meta.ts
1185
1397
  var SDK_NAME = "@rasputin-ai/node";
1186
- var SDK_VERSION = "0.3.0";
1398
+ var SDK_VERSION = "0.4.1";
1187
1399
 
1188
1400
  // src/rasputin-init.ts
1189
- var withExecution = (client, execution, installRuntime, repoRoot, manifest) => {
1190
- const uninstallRuntime = installRuntime ? installAutomaticExecutionRuntime(execution, { repoRoot }) : () => {
1401
+ var withExecution = (client, execution, installRuntime, manifest) => {
1402
+ const uninstallRuntime = installRuntime ? installAutomaticExecutionRuntime(execution) : () => {
1191
1403
  };
1192
- const manifestUpload = scheduleInstrumentationManifestUpload({
1193
- ...manifest,
1194
- repoRoot
1195
- });
1404
+ const manifestUpload = scheduleInstrumentationManifestUpload(manifest);
1196
1405
  return {
1197
1406
  ...client,
1198
1407
  execution,
1199
1408
  getStats: () => ({ ...client.getStats(), recorder: execution.getStats() }),
1409
+ verifyConfiguration: manifestUpload.verify,
1200
1410
  captureException: (error, context) => {
1201
1411
  const runtimeState = context?.runtimeState ?? execution.getErrorState(
1202
1412
  error,
@@ -1226,18 +1436,15 @@ var RasputinInit = (options) => {
1226
1436
  ...options.executionRecorder,
1227
1437
  enabled: recorderEnabled
1228
1438
  });
1229
- const wrappedClient = withExecution(
1230
- client,
1231
- execution,
1232
- recorderEnabled,
1233
- resolveRepoRoot(options),
1234
- {
1235
- projectApiKey: options.projectApiKey,
1236
- release: options.release,
1237
- apiUrl: options.apiUrl,
1238
- enabled: isClientEnabled(options)
1239
- }
1240
- );
1439
+ const wrappedClient = withExecution(client, execution, recorderEnabled, {
1440
+ projectApiKey: options.projectApiKey,
1441
+ release: options.release,
1442
+ apiUrl: options.apiUrl,
1443
+ enabled: isClientEnabled(options),
1444
+ sourceRoot: options.sourceRoot,
1445
+ sourceRoots: options.sourceRoots,
1446
+ logSuccess: options.logSuccess
1447
+ });
1241
1448
  if (isClientEnabled(options)) {
1242
1449
  installGlobalHandlers(wrappedClient);
1243
1450
  }
@@ -1247,12 +1454,14 @@ var RasputinInit = (options) => {
1247
1454
  createClient({ ...options, enabled: false }),
1248
1455
  createExecutionRecorder({ enabled: false }),
1249
1456
  false,
1250
- void 0,
1251
1457
  {
1252
1458
  projectApiKey: options.projectApiKey,
1253
1459
  release: options.release,
1254
1460
  apiUrl: options.apiUrl,
1255
- enabled: false
1461
+ enabled: false,
1462
+ sourceRoot: options.sourceRoot,
1463
+ sourceRoots: options.sourceRoots,
1464
+ logSuccess: options.logSuccess
1256
1465
  }
1257
1466
  );
1258
1467
  }