@intelli-1113/stratos-sdk 1.2.3 → 1.3.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.
@@ -0,0 +1,428 @@
1
+ // LangChain / Stratos compatibility inspect + optional fix (client project cwd).
2
+ //
3
+ // Traceloop instrumentation peer (via @traceloop/instrumentation-langchain):
4
+ // @langchain/core >=1.0.0 <2.0.0
5
+ //
6
+ // Usage (CLI):
7
+ // npx stratos-inspect
8
+ // npx stratos-inspect --fix
9
+ // npx stratos-inspect --fix --yes
10
+
11
+ import { createRequire } from "node:module";
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import { spawnSync } from "node:child_process";
15
+ import { createInterface } from "node:readline";
16
+
17
+ /** Peer range of @traceloop/instrumentation-langchain@0.27 */
18
+ export const LANGCHAIN_CORE_RANGE = {
19
+ minInclusive: "1.0.0",
20
+ maxExclusive: "2.0.0",
21
+ npmInstall: "@langchain/core@^1.2.4",
22
+ };
23
+
24
+ /** Packages we may touch when --fix is used (only if present / related). */
25
+ export const LANGCHAIN_FAMILY = [
26
+ "langchain",
27
+ "@langchain/core",
28
+ "@langchain/community",
29
+ "@langchain/openai",
30
+ "@langchain/anthropic",
31
+ "@langchain/mistralai",
32
+ "@langchain/google-genai",
33
+ "@langchain/google-vertexai",
34
+ "@langchain/tavily",
35
+ "@langchain/langgraph",
36
+ "@langchain/langgraph-checkpoint",
37
+ "@langchain/classic",
38
+ "@langchain/textsplitters",
39
+ ];
40
+
41
+ /** Safe 1.x installs for a typical Stratos + LangChain agent. */
42
+ export const RECOMMENDED_INSTALL = [
43
+ "@langchain/core@^1.2.4",
44
+ "langchain@^1.5.4",
45
+ ];
46
+
47
+ function parseSemver(v) {
48
+ if (!v || typeof v !== "string") return null;
49
+ const cleaned = v.replace(/^[=v^~<>]*/, "").split("-")[0].trim();
50
+ const m = cleaned.match(/^(\d+)\.(\d+)\.(\d+)/);
51
+ if (!m) {
52
+ // package.json range like "^0.3.0" → take first numbers
53
+ const m2 = v.match(/(\d+)\.(\d+)\.(\d+)/) || v.match(/(\d+)\.(\d+)/);
54
+ if (!m2) return null;
55
+ return {
56
+ major: Number(m2[1]),
57
+ minor: Number(m2[2]),
58
+ patch: Number(m2[3] || 0),
59
+ raw: v,
60
+ };
61
+ }
62
+ return {
63
+ major: Number(m[1]),
64
+ minor: Number(m[2]),
65
+ patch: Number(m[3]),
66
+ raw: v,
67
+ };
68
+ }
69
+
70
+ function cmp(a, b) {
71
+ if (a.major !== b.major) return a.major - b.major;
72
+ if (a.minor !== b.minor) return a.minor - b.minor;
73
+ return a.patch - b.patch;
74
+ }
75
+
76
+ /**
77
+ * @returns {"ok"|"too_old"|"too_new"|"unknown"|"absent"}
78
+ */
79
+ export function classifyCoreVersion(versionStr) {
80
+ if (!versionStr) return "absent";
81
+ const v = parseSemver(versionStr);
82
+ if (!v) return "unknown";
83
+ const min = parseSemver(LANGCHAIN_CORE_RANGE.minInclusive);
84
+ const max = parseSemver(LANGCHAIN_CORE_RANGE.maxExclusive);
85
+ if (cmp(v, min) < 0) return "too_old";
86
+ if (cmp(v, max) >= 0) return "too_new";
87
+ return "ok";
88
+ }
89
+
90
+ function readPkgJson(cwd) {
91
+ const p = join(cwd, "package.json");
92
+ if (!existsSync(p)) return null;
93
+ try {
94
+ return JSON.parse(readFileSync(p, "utf8"));
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ function declaredDeps(pkg) {
101
+ return {
102
+ ...(pkg?.dependencies || {}),
103
+ ...(pkg?.devDependencies || {}),
104
+ ...(pkg?.optionalDependencies || {}),
105
+ };
106
+ }
107
+
108
+ function resolveInstalledVersion(cwd, name) {
109
+ try {
110
+ const req = createRequire(join(cwd, "package.json"));
111
+ const pkgPath = req.resolve(`${name}/package.json`);
112
+ const meta = JSON.parse(readFileSync(pkgPath, "utf8"));
113
+ return { version: meta.version, path: pkgPath };
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+
119
+ function detectPackageManager(cwd) {
120
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
121
+ if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
122
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock")))
123
+ return "bun";
124
+ return "npm";
125
+ }
126
+
127
+ /**
128
+ * Inspect LangChain compatibility for a project directory (default: cwd).
129
+ * @param {{ cwd?: string }} [opts]
130
+ */
131
+ export function inspectLangChain(opts = {}) {
132
+ const cwd = opts.cwd || process.cwd();
133
+ const pkg = readPkgJson(cwd);
134
+ const declared = pkg ? declaredDeps(pkg) : {};
135
+
136
+ const found = [];
137
+ for (const name of LANGCHAIN_FAMILY) {
138
+ const declaredRange = declared[name] || null;
139
+ const installed = resolveInstalledVersion(cwd, name);
140
+ if (!declaredRange && !installed) continue;
141
+ found.push({
142
+ name,
143
+ declared: declaredRange,
144
+ installed: installed?.version || null,
145
+ installPath: installed?.path || null,
146
+ });
147
+ }
148
+
149
+ const core =
150
+ found.find((f) => f.name === "@langchain/core") ||
151
+ (declared["@langchain/core"]
152
+ ? {
153
+ name: "@langchain/core",
154
+ declared: declared["@langchain/core"],
155
+ installed: null,
156
+ installPath: null,
157
+ }
158
+ : null);
159
+
160
+ const coreVersion = core?.installed || core?.declared || null;
161
+ const status = core
162
+ ? classifyCoreVersion(core?.installed || core?.declared)
163
+ : found.length
164
+ ? "unknown"
165
+ : "absent";
166
+
167
+ const compatible = status === "ok" || status === "absent";
168
+ // absent = no LangChain → Stratos still fine (other frameworks)
169
+ // too_old / too_new / unknown with langchain present → problem for LC metrics
170
+
171
+ const hasLangChain = found.length > 0 || !!core;
172
+ const needsUpgrade = status === "too_old";
173
+ const needsAttention = status === "too_old" || status === "too_new";
174
+
175
+ return {
176
+ cwd,
177
+ packageManager: detectPackageManager(cwd),
178
+ hasLangChain,
179
+ status,
180
+ compatible: !hasLangChain || status === "ok",
181
+ needsUpgrade,
182
+ needsAttention,
183
+ core: core
184
+ ? {
185
+ declared: core.declared,
186
+ installed: core.installed,
187
+ versionForCheck: coreVersion,
188
+ status,
189
+ }
190
+ : null,
191
+ packages: found,
192
+ required: {
193
+ peer: `@langchain/core >=${LANGCHAIN_CORE_RANGE.minInclusive} <${LANGCHAIN_CORE_RANGE.maxExclusive}`,
194
+ recommendedInstall: RECOMMENDED_INSTALL,
195
+ },
196
+ message: buildMessage(status, hasLangChain, coreVersion),
197
+ };
198
+ }
199
+
200
+ function buildMessage(status, hasLangChain, coreVersion) {
201
+ if (!hasLangChain || status === "absent") {
202
+ return "No LangChain packages found — nothing to upgrade for LangChain metrics.";
203
+ }
204
+ if (status === "ok") {
205
+ return `@langchain/core@${coreVersion} is compatible with Stratos LangChain auto-instrumentation.`;
206
+ }
207
+ if (status === "too_old") {
208
+ return (
209
+ `@langchain/core@${coreVersion} is too old for Stratos LangChain metrics. ` +
210
+ `Required: >=1.0.0 <2.0.0. Uninstall old LangChain packages, then install 1.x.`
211
+ );
212
+ }
213
+ if (status === "too_new") {
214
+ return (
215
+ `@langchain/core@${coreVersion} is newer than the current OpenLLMetry peer range (<2.0.0). ` +
216
+ `LangChain metrics may not work until Stratos/Traceloop supports core 2.x.`
217
+ );
218
+ }
219
+ return `Could not parse @langchain/core version (${coreVersion}). Expected >=1.0.0 <2.0.0.`;
220
+ }
221
+
222
+ function runPm(pm, args, cwd) {
223
+ const cmd = pm === "npm" ? "npm" : pm;
224
+ const result = spawnSync(cmd, args, {
225
+ cwd,
226
+ stdio: "inherit",
227
+ shell: process.platform === "win32",
228
+ env: process.env,
229
+ });
230
+ return result.status === 0;
231
+ }
232
+
233
+ /**
234
+ * Uninstall detected LangChain family packages, then install recommended 1.x set
235
+ * (+ re-install any previously present family packages at ^1 if they were there).
236
+ *
237
+ * @param {{ cwd?: string, yes?: boolean, dryRun?: boolean }} [opts]
238
+ */
239
+ export async function fixLangChain(opts = {}) {
240
+ const cwd = opts.cwd || process.cwd();
241
+ const report = inspectLangChain({ cwd });
242
+ const pm = report.packageManager;
243
+
244
+ if (!report.hasLangChain) {
245
+ return {
246
+ ok: true,
247
+ skipped: true,
248
+ reason: "no-langchain",
249
+ report,
250
+ };
251
+ }
252
+
253
+ if (report.status === "ok") {
254
+ return {
255
+ ok: true,
256
+ skipped: true,
257
+ reason: "already-compatible",
258
+ report,
259
+ };
260
+ }
261
+
262
+ if (report.status === "too_new") {
263
+ return {
264
+ ok: false,
265
+ skipped: true,
266
+ reason: "too-new",
267
+ report,
268
+ message:
269
+ "Core is >=2.0.0 — automatic downgrade is not performed. Pin <2 or wait for SDK support.",
270
+ };
271
+ }
272
+
273
+ if (report.status !== "too_old" && report.status !== "unknown") {
274
+ return { ok: false, skipped: true, reason: report.status, report };
275
+ }
276
+
277
+ const toRemove = report.packages.map((p) => p.name);
278
+ // Also remove declared-only names
279
+ const pkg = readPkgJson(cwd);
280
+ const declared = pkg ? declaredDeps(pkg) : {};
281
+ for (const name of LANGCHAIN_FAMILY) {
282
+ if (declared[name] && !toRemove.includes(name)) toRemove.push(name);
283
+ }
284
+
285
+ // Reinstall: recommended base + any extra family packages client had, on ^1
286
+ const reinstall = new Set(RECOMMENDED_INSTALL);
287
+ for (const name of toRemove) {
288
+ if (name === "@langchain/core" || name === "langchain") continue;
289
+ // keep provider packages if they were present
290
+ if (report.packages.some((p) => p.name === name) || declared[name]) {
291
+ reinstall.add(`${name}@^1`);
292
+ }
293
+ }
294
+
295
+ const plan = {
296
+ packageManager: pm,
297
+ uninstall: toRemove,
298
+ install: [...reinstall],
299
+ };
300
+
301
+ if (opts.dryRun) {
302
+ return { ok: true, dryRun: true, plan, report };
303
+ }
304
+
305
+ if (!opts.yes) {
306
+ const confirmed = await confirm(
307
+ `\n[stratos-inspect] About to:\n` +
308
+ ` 1) uninstall: ${toRemove.join(", ")}\n` +
309
+ ` 2) install: ${[...reinstall].join(", ")}\n` +
310
+ `in ${cwd}\n\nProceed? [y/N] `
311
+ );
312
+ if (!confirmed) {
313
+ return { ok: false, cancelled: true, plan, report };
314
+ }
315
+ }
316
+
317
+ console.log(`\n[stratos-inspect] Using package manager: ${pm}`);
318
+ console.log(`[stratos-inspect] Uninstalling old LangChain packages…`);
319
+
320
+ let uninstallOk = true;
321
+ if (toRemove.length) {
322
+ if (pm === "npm") {
323
+ uninstallOk = runPm("npm", ["uninstall", ...toRemove], cwd);
324
+ } else if (pm === "pnpm") {
325
+ uninstallOk = runPm("pnpm", ["remove", ...toRemove], cwd);
326
+ } else if (pm === "yarn") {
327
+ uninstallOk = runPm("yarn", ["remove", ...toRemove], cwd);
328
+ } else if (pm === "bun") {
329
+ uninstallOk = runPm("bun", ["remove", ...toRemove], cwd);
330
+ }
331
+ }
332
+
333
+ if (!uninstallOk) {
334
+ return {
335
+ ok: false,
336
+ step: "uninstall",
337
+ plan,
338
+ report,
339
+ message: "Uninstall failed — fix manually and re-run stratos-inspect --fix",
340
+ };
341
+ }
342
+
343
+ console.log(`[stratos-inspect] Installing LangChain 1.x packages…`);
344
+ const installList = [...reinstall];
345
+ let installOk = false;
346
+ if (pm === "npm") {
347
+ installOk = runPm("npm", ["install", ...installList], cwd);
348
+ } else if (pm === "pnpm") {
349
+ installOk = runPm("pnpm", ["add", ...installList], cwd);
350
+ } else if (pm === "yarn") {
351
+ installOk = runPm("yarn", ["add", ...installList], cwd);
352
+ } else if (pm === "bun") {
353
+ installOk = runPm("bun", ["add", ...installList], cwd);
354
+ }
355
+
356
+ const after = inspectLangChain({ cwd });
357
+ return {
358
+ ok: installOk && after.status === "ok",
359
+ step: "install",
360
+ plan,
361
+ reportBefore: report,
362
+ reportAfter: after,
363
+ message: after.message,
364
+ };
365
+ }
366
+
367
+ function confirm(question) {
368
+ if (!process.stdin.isTTY) {
369
+ console.warn(
370
+ "[stratos-inspect] Non-interactive terminal — pass --yes to apply fix, or run with a TTY."
371
+ );
372
+ return Promise.resolve(false);
373
+ }
374
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
375
+ return new Promise((resolve) => {
376
+ rl.question(question, (answer) => {
377
+ rl.close();
378
+ const a = String(answer || "").trim().toLowerCase();
379
+ resolve(a === "y" || a === "yes");
380
+ });
381
+ });
382
+ }
383
+
384
+ /** Human-readable print for CLI / postinstall */
385
+ export function printInspectReport(report, { json = false } = {}) {
386
+ if (json) {
387
+ console.log(JSON.stringify(report, null, 2));
388
+ return;
389
+ }
390
+
391
+ console.log("\n[stratos-inspect] LangChain compatibility");
392
+ console.log(` project: ${report.cwd}`);
393
+ console.log(` pm: ${report.packageManager}`);
394
+ console.log(` status: ${report.status}`);
395
+ console.log(` required: ${report.required.peer}`);
396
+
397
+ if (report.core) {
398
+ console.log(
399
+ ` core: installed=${report.core.installed || "—"} declared=${report.core.declared || "—"}`
400
+ );
401
+ }
402
+
403
+ if (report.packages.length) {
404
+ console.log(" packages:");
405
+ for (const p of report.packages) {
406
+ console.log(
407
+ ` - ${p.name}: ${p.installed || p.declared || "?"}${p.installed && p.declared ? ` (declared ${p.declared})` : ""}`
408
+ );
409
+ }
410
+ }
411
+
412
+ console.log(`\n ${report.message}`);
413
+
414
+ if (report.needsUpgrade) {
415
+ console.log(`
416
+ Fix (interactive):
417
+ npx stratos-inspect --fix
418
+
419
+ Fix (non-interactive):
420
+ npx stratos-inspect --fix --yes
421
+
422
+ Or manually:
423
+ npm uninstall ${report.packages.map((p) => p.name).join(" ")}
424
+ npm install ${report.required.recommendedInstall.join(" ")}
425
+ `);
426
+ }
427
+ console.log("");
428
+ }
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // Runs after `npm install @intelli-1113/stratos-sdk`.
3
+ // Warns only — never auto-uninstalls (use: npx stratos-inspect --fix).
4
+ //
5
+ // npm sets INIT_CWD to the project where the user ran `npm install`
6
+ // (not node_modules/@intelli-1113/stratos-sdk).
7
+
8
+ import { inspectLangChain, printInspectReport } from "./inspect-langchain.js";
9
+
10
+ try {
11
+ if (process.env.STRATOS_SKIP_POSTINSTALL === "1") process.exit(0);
12
+
13
+ // Prefer the consumer project root
14
+ const cwd = process.env.INIT_CWD || process.cwd();
15
+ const report = inspectLangChain({ cwd });
16
+ if (!report.hasLangChain) process.exit(0);
17
+ if (report.compatible) process.exit(0);
18
+
19
+ console.warn(
20
+ "\n[stratos] ⚠ LangChain version is not compatible with Stratos auto-metrics."
21
+ );
22
+ printInspectReport(report);
23
+ console.warn(
24
+ "[stratos] In your project, run:\n npx stratos-inspect --fix\n"
25
+ );
26
+ } catch {
27
+ // Never fail the host install
28
+ process.exit(0);
29
+ }
package/src/register.js CHANGED
@@ -1,5 +1,41 @@
1
- // Side-effect entry so telemetry starts before your app code loads.
2
- // node --import @intelli-1113/stratos-sdk/register server.js (zero code changes)
3
- // import "@intelli-1113/stratos-sdk/register"; (first line of entrypoint)
4
- import { start } from "./index.js";
5
- start();
1
+ // Side-effect entry so telemetry starts before your app code loads.
2
+ // node --import @intelli-1113/stratos-sdk/register server.js (zero code changes)
3
+ // import "@intelli-1113/stratos-sdk/register"; (first line of entrypoint)
4
+ //
5
+ // Order matters:
6
+ // 1) Load .env from cwd (so --import sees STRATOS_TOKEN before start)
7
+ // 2) Register Vercel AI ESM loader (intercepts `import … from "ai"`)
8
+ // 3) start() → Traceloop + OTLP export to Stratos
9
+
10
+ import { register as registerModuleHook } from "node:module";
11
+ import { pathToFileURL } from "node:url";
12
+ import { dirname, join } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { existsSync } from "node:fs";
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+
18
+ // So `node --import …/register app.js` picks up STRATOS_* without client code
19
+ try {
20
+ const envPath = join(process.cwd(), ".env");
21
+ if (existsSync(envPath) && typeof process.loadEnvFile === "function") {
22
+ process.loadEnvFile(envPath);
23
+ }
24
+ } catch {
25
+ /* optional */
26
+ }
27
+
28
+ // Must run before any consumer imports of `ai`
29
+ try {
30
+ registerModuleHook(
31
+ pathToFileURL(join(__dirname, "vercel-ai-loader.js")).href
32
+ );
33
+ } catch (err) {
34
+ console.warn(
35
+ "[stratos] could not register Vercel AI loader:",
36
+ err?.message || err
37
+ );
38
+ }
39
+
40
+ import { start } from "./index.js";
41
+ start();
@@ -0,0 +1,78 @@
1
+ // Pure helpers for Vercel AI telemetry injection (no side effects).
2
+ // Used by the ESM loader shim and optional call-site wrappers.
3
+
4
+ export function flagEnabled(name, defaultOn = true) {
5
+ const v = process.env[name];
6
+ if (v == null || v === "") return defaultOn;
7
+ return !["0", "false", "off", "no"].includes(String(v).toLowerCase());
8
+ }
9
+
10
+ export function defaultTelemetrySettings() {
11
+ return {
12
+ isEnabled: true,
13
+ recordInputs: flagEnabled("STRATOS_RECORD_INPUTS", true),
14
+ recordOutputs: flagEnabled("STRATOS_RECORD_OUTPUTS", true),
15
+ functionId: process.env.STRATOS_FUNCTION_ID || "stratos-vercel-ai",
16
+ metadata: {
17
+ framework: "vercel-ai",
18
+ agent:
19
+ process.env.STRATOS_APP_NAME ||
20
+ process.env.npm_package_name ||
21
+ "agent",
22
+ },
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Merge Stratos defaults into AI SDK call options.
28
+ * Respects client opt-out: experimental_telemetry/telemetry isEnabled: false.
29
+ */
30
+ export function injectTelemetryOptions(opts) {
31
+ if (!opts || typeof opts !== "object" || Array.isArray(opts)) return opts;
32
+
33
+ const defaults = defaultTelemetrySettings();
34
+
35
+ if (opts.experimental_telemetry?.isEnabled === false) return opts;
36
+ if (opts.telemetry?.isEnabled === false) return opts;
37
+
38
+ const next = { ...opts };
39
+
40
+ next.experimental_telemetry = {
41
+ ...defaults,
42
+ ...(opts.experimental_telemetry || {}),
43
+ isEnabled: opts.experimental_telemetry?.isEnabled ?? true,
44
+ metadata: {
45
+ ...defaults.metadata,
46
+ ...(opts.experimental_telemetry?.metadata || {}),
47
+ },
48
+ };
49
+
50
+ if (opts.telemetry == null || typeof opts.telemetry === "object") {
51
+ next.telemetry = {
52
+ ...defaults,
53
+ ...(opts.telemetry || {}),
54
+ isEnabled: opts.telemetry?.isEnabled ?? true,
55
+ metadata: {
56
+ ...defaults.metadata,
57
+ ...(opts.telemetry?.metadata || {}),
58
+ },
59
+ };
60
+ }
61
+
62
+ return next;
63
+ }
64
+
65
+ /** Functions whose first argument is call options that accept telemetry. */
66
+ export const TELEMETRY_CALL_NAMES = [
67
+ "generateText",
68
+ "streamText",
69
+ "generateObject",
70
+ "streamObject",
71
+ "embed",
72
+ "embedMany",
73
+ "experimental_generateImage",
74
+ "experimental_generateSpeech",
75
+ "generateImage",
76
+ "generateSpeech",
77
+ "experimental_transcribe",
78
+ ];
@@ -0,0 +1,99 @@
1
+ // Node custom module loader: rewrite bare specifier `ai` → Stratos shim
2
+ // that re-exports the real package with telemetry injected on generate*/stream*/embed*.
3
+ //
4
+ // Registered from register.js BEFORE start(), so client code needs no changes:
5
+ // import "@intelli-1113/stratos-sdk/register";
6
+ // import { generateText } from "ai"; // ← gets wrapped exports
7
+
8
+ import { createRequire } from "node:module";
9
+ import { pathToFileURL } from "node:url";
10
+ import { dirname, join } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { TELEMETRY_CALL_NAMES } from "./vercel-ai-inject.js";
13
+
14
+ const SHIM_URL = "stratos-sdk:ai";
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const INJECT_URL = pathToFileURL(join(__dirname, "vercel-ai-inject.js")).href;
17
+
18
+ function flagEnabled(name, defaultOn = true) {
19
+ const v = process.env[name];
20
+ if (v == null || v === "") return defaultOn;
21
+ return !["0", "false", "off", "no"].includes(String(v).toLowerCase());
22
+ }
23
+
24
+ export async function resolve(specifier, context, nextResolve) {
25
+ if (!flagEnabled("STRATOS_VERCEL_AI", true)) {
26
+ return nextResolve(specifier, context);
27
+ }
28
+
29
+ // Only rewrite the bare package name. Absolute/file imports of real `ai` pass through
30
+ // (used by this shim to load the original module without recursion).
31
+ if (specifier === "ai") {
32
+ return {
33
+ shortCircuit: true,
34
+ url: SHIM_URL,
35
+ format: "module",
36
+ };
37
+ }
38
+ return nextResolve(specifier, context);
39
+ }
40
+
41
+ export async function load(url, context, nextLoad) {
42
+ if (url !== SHIM_URL) {
43
+ return nextLoad(url, context);
44
+ }
45
+
46
+ const req = createRequire(join(process.cwd(), "package.json"));
47
+ let realPath;
48
+ try {
49
+ realPath = req.resolve("ai");
50
+ } catch (err) {
51
+ // Consumer doesn't have `ai` — fall through to a clear error on use
52
+ return {
53
+ format: "module",
54
+ shortCircuit: true,
55
+ source: `throw new Error("[stratos] package \\"ai\\" (Vercel AI SDK) is not installed in this project");\n`,
56
+ };
57
+ }
58
+
59
+ const realUrl = pathToFileURL(realPath).href;
60
+ // Load real module via absolute URL (does not match specifier === "ai")
61
+ const real = await import(realUrl);
62
+ const wrapSet = new Set(TELEMETRY_CALL_NAMES);
63
+ const keys = Object.keys(real).filter(
64
+ (k) => k !== "default" && k !== "__esModule" && /^[A-Za-z_$][\w$]*$/.test(k)
65
+ );
66
+
67
+ let source = "";
68
+ source += `import * as __real from ${JSON.stringify(realUrl)};\n`;
69
+ source += `import { injectTelemetryOptions } from ${JSON.stringify(INJECT_URL)};\n`;
70
+ source += `
71
+ function __stratosWrap(fn) {
72
+ if (typeof fn !== "function" || fn.__stratosWrapped) return fn;
73
+ const wrapped = function stratosVercelAiWrap(opts, ...rest) {
74
+ return fn.call(this, injectTelemetryOptions(opts), ...rest);
75
+ };
76
+ wrapped.__stratosWrapped = true;
77
+ try { Object.defineProperty(wrapped, "name", { value: fn.name || "stratosVercelAiWrap" }); } catch {}
78
+ return wrapped;
79
+ }
80
+ `;
81
+
82
+ for (const k of keys) {
83
+ if (wrapSet.has(k)) {
84
+ source += `export const ${k} = __stratosWrap(__real[${JSON.stringify(k)}]);\n`;
85
+ } else {
86
+ source += `export const ${k} = __real[${JSON.stringify(k)}];\n`;
87
+ }
88
+ }
89
+
90
+ if (Object.prototype.hasOwnProperty.call(real, "default")) {
91
+ source += `export default __real.default;\n`;
92
+ }
93
+
94
+ return {
95
+ format: "module",
96
+ shortCircuit: true,
97
+ source,
98
+ };
99
+ }