@sentry/junior 0.63.0 → 0.64.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.
@@ -2,12 +2,12 @@ import {
2
2
  SANDBOX_WORKSPACE_ROOT,
3
3
  getStateAdapter,
4
4
  toOptionalTrimmed
5
- } from "./chunk-PEF6UXTY.js";
5
+ } from "./chunk-IGVHCX2U.js";
6
6
  import {
7
7
  getPluginRuntimeDependencies,
8
8
  getPluginRuntimePostinstall,
9
9
  withSpan
10
- } from "./chunk-H652GMDH.js";
10
+ } from "./chunk-WDPWFMCE.js";
11
11
 
12
12
  // src/chat/sandbox/runtime-dependency-snapshots.ts
13
13
  import { createHash } from "crypto";
package/dist/cli/check.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  parseSkillFile
3
- } from "../chunk-ITOW4DED.js";
3
+ } from "../chunk-D23WCM66.js";
4
4
  import {
5
5
  parsePluginManifest
6
- } from "../chunk-H652GMDH.js";
6
+ } from "../chunk-WDPWFMCE.js";
7
7
  import "../chunk-Z3YD6NHK.js";
8
- import "../chunk-5LUISFEY.js";
8
+ import "../chunk-KVZL5NZS.js";
9
9
  import "../chunk-2KG3PWR4.js";
10
10
 
11
11
  // src/cli/check.ts
@@ -396,7 +396,12 @@ async function validateAppSourceFiles(rootDir, registeredConfigKeys) {
396
396
  }
397
397
  if (/\bpluginPackages\s*:/.test(source)) {
398
398
  errors.push(
399
- `${sourcePath}: pluginPackages is no longer supported. Use plugins: { packages: [...] }.`
399
+ `${sourcePath}: pluginPackages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`
400
+ );
401
+ }
402
+ if (/\bplugins\s*:\s*\{\s*packages\s*:/.test(source)) {
403
+ errors.push(
404
+ `${sourcePath}: plugins.packages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`
400
405
  );
401
406
  }
402
407
  for (const defaultsBlock of source.matchAll(
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  resolveRuntimeDependencySnapshot
3
- } from "../chunk-LRVKJAR2.js";
3
+ } from "../chunk-WZFQQ6SP.js";
4
4
  import {
5
5
  disconnectStateAdapter
6
- } from "../chunk-PEF6UXTY.js";
6
+ } from "../chunk-IGVHCX2U.js";
7
7
  import {
8
8
  getPluginProviders,
9
9
  getPluginRuntimeDependencies,
10
10
  getPluginRuntimePostinstall
11
- } from "../chunk-H652GMDH.js";
11
+ } from "../chunk-WDPWFMCE.js";
12
12
  import "../chunk-Z3YD6NHK.js";
13
- import "../chunk-5LUISFEY.js";
13
+ import "../chunk-KVZL5NZS.js";
14
14
  import "../chunk-2KG3PWR4.js";
15
15
 
16
16
  // src/cli/snapshot-warmup.ts
package/dist/nitro.d.ts CHANGED
@@ -1,9 +1,16 @@
1
- import type { PluginConfig } from "@/chat/plugins/types";
1
+ import { type JuniorPluginSet } from "@/plugins";
2
+ export interface JuniorPluginModuleReference {
3
+ /** Runtime-safe module that exports a `defineJuniorPlugins(...)` set. */
4
+ module: string;
5
+ /** Named export to import from `module`. Defaults to `plugins`. */
6
+ exportName?: string;
7
+ }
8
+ export type JuniorNitroPluginSource = JuniorPluginModuleReference | JuniorPluginSet | string;
2
9
  export interface JuniorNitroOptions {
3
10
  cwd?: string;
4
11
  maxDuration?: number;
5
- /** Plugin packages and manifest overrides bundled into the app. */
6
- plugins?: PluginConfig;
12
+ /** Plugin catalog set or runtime-safe plugin module. Direct sets must not include trusted hooks. */
13
+ plugins?: JuniorNitroPluginSource;
7
14
  /**
8
15
  * Extra file patterns to copy into the server output for files that the
9
16
  * bundler cannot trace (e.g. dynamically imported providers).
package/dist/nitro.js CHANGED
@@ -1,12 +1,19 @@
1
+ import {
2
+ pluginCatalogConfigFromPluginSet,
3
+ trustedPluginRegistrationsFromPluginSet
4
+ } from "./chunk-5VDO6LSG.js";
1
5
  import {
2
6
  discoverInstalledPluginPackageContent,
3
7
  isValidPackageName,
4
8
  resolvePackageDir
5
- } from "./chunk-5LUISFEY.js";
9
+ } from "./chunk-KVZL5NZS.js";
6
10
  import "./chunk-2KG3PWR4.js";
7
11
 
8
12
  // src/nitro.ts
9
13
  import path2 from "path";
14
+ import { statSync as statSync2 } from "fs";
15
+ import { createRequire } from "module";
16
+ import { pathToFileURL } from "url";
10
17
 
11
18
  // src/build/rolldown-workarounds.ts
12
19
  function applyRolldownTreeshakeWorkaround(nitro) {
@@ -166,11 +173,135 @@ function nodeModulesRelativePath(sourcePath) {
166
173
  }
167
174
 
168
175
  // src/build/virtual-config.ts
169
- function injectVirtualConfig(nitro, plugins) {
170
- nitro.options.virtual["#junior/config"] = `export const plugins = ${JSON.stringify(plugins ?? { packages: [] })};`;
176
+ function renderRuntimePluginImport(module) {
177
+ if (module.exportName === "default") {
178
+ return `import juniorRuntimePluginSet from ${JSON.stringify(module.specifier)};`;
179
+ }
180
+ return `import { ${module.exportName} as juniorRuntimePluginSet } from ${JSON.stringify(module.specifier)};`;
181
+ }
182
+ function renderVirtualConfig(options) {
183
+ const lines = [
184
+ ...options.pluginModule ? [
185
+ renderRuntimePluginImport(options.pluginModule),
186
+ "export const pluginSet = juniorRuntimePluginSet;"
187
+ ] : ["export const pluginSet = undefined;"],
188
+ `export const plugins = ${JSON.stringify(options.plugins ?? { packages: [] })};`,
189
+ `export const trustedPluginRegistrations = ${JSON.stringify(options.trustedPluginRegistrations ?? [])};`
190
+ ];
191
+ return lines.join("\n");
192
+ }
193
+ function injectVirtualConfig(nitro, options = {}) {
194
+ nitro.options.virtual["#junior/config"] = async () => {
195
+ if (!options.loadPluginSet) {
196
+ return renderVirtualConfig(options);
197
+ }
198
+ const pluginSet = await options.loadPluginSet();
199
+ return renderVirtualConfig({
200
+ pluginModule: options.pluginModule,
201
+ plugins: pluginCatalogConfigFromPluginSet(pluginSet),
202
+ trustedPluginRegistrations: trustedPluginRegistrationsFromPluginSet(
203
+ pluginSet
204
+ ).map((plugin) => plugin.name)
205
+ });
206
+ };
171
207
  }
172
208
 
173
209
  // src/nitro.ts
210
+ var PLUGIN_MODULE_EXTENSIONS = [
211
+ "",
212
+ ".ts",
213
+ ".tsx",
214
+ ".mts",
215
+ ".mjs",
216
+ ".js",
217
+ ".cjs"
218
+ ];
219
+ function isPluginModuleReference(value) {
220
+ return typeof value === "string" || Boolean(value && "module" in value);
221
+ }
222
+ function isPluginSet(value) {
223
+ if (!value || typeof value !== "object") {
224
+ return false;
225
+ }
226
+ return "packageNames" in value && "registrations" in value;
227
+ }
228
+ function resolveRelativePluginModule(cwd, specifier) {
229
+ const basePath = path2.resolve(cwd, specifier);
230
+ for (const extension of PLUGIN_MODULE_EXTENSIONS) {
231
+ const candidate = `${basePath}${extension}`;
232
+ try {
233
+ if (statSync2(candidate).isFile()) {
234
+ return candidate;
235
+ }
236
+ } catch {
237
+ }
238
+ }
239
+ for (const extension of PLUGIN_MODULE_EXTENSIONS) {
240
+ const candidate = path2.join(basePath, `index${extension}`);
241
+ try {
242
+ if (statSync2(candidate).isFile()) {
243
+ return candidate;
244
+ }
245
+ } catch {
246
+ }
247
+ }
248
+ throw new Error(`Plugin module "${specifier}" could not be resolved`);
249
+ }
250
+ function resolvePluginModule(cwd, input) {
251
+ const moduleSpecifier = typeof input === "string" ? input : input.module;
252
+ const exportName = typeof input === "string" ? "plugins" : input.exportName ?? "plugins";
253
+ if (!moduleSpecifier.trim()) {
254
+ throw new Error("Plugin module specifier must not be empty");
255
+ }
256
+ if (moduleSpecifier.startsWith(".") || path2.isAbsolute(moduleSpecifier)) {
257
+ const resolvedPath2 = resolveRelativePluginModule(cwd, moduleSpecifier);
258
+ return {
259
+ exportName,
260
+ importUrl: pathToFileURL(resolvedPath2).href,
261
+ runtimeModule: {
262
+ exportName,
263
+ specifier: resolvedPath2.split(path2.sep).join("/")
264
+ }
265
+ };
266
+ }
267
+ const requireFromApp = createRequire(path2.join(cwd, "package.json"));
268
+ const resolvedPath = requireFromApp.resolve(moduleSpecifier);
269
+ return {
270
+ exportName,
271
+ importUrl: pathToFileURL(resolvedPath).href,
272
+ runtimeModule: {
273
+ exportName,
274
+ specifier: moduleSpecifier
275
+ }
276
+ };
277
+ }
278
+ function assertPluginSet(value, source) {
279
+ if (!value || typeof value !== "object" || !Array.isArray(value.packageNames) || !Array.isArray(value.registrations)) {
280
+ throw new Error(
281
+ `Plugin module ${source} must export a defineJuniorPlugins(...) set`
282
+ );
283
+ }
284
+ return value;
285
+ }
286
+ async function loadPluginSetFromModule(moduleRef) {
287
+ const mod = await import(moduleRef.importUrl);
288
+ const value = moduleRef.exportName === "default" ? mod.default : mod[moduleRef.exportName];
289
+ return assertPluginSet(
290
+ value,
291
+ `${moduleRef.importUrl}#${moduleRef.exportName}`
292
+ );
293
+ }
294
+ function assertSerializableDirectPluginSet(pluginSet) {
295
+ const trustedPluginNames = trustedPluginRegistrationsFromPluginSet(
296
+ pluginSet
297
+ ).map((plugin) => plugin.name);
298
+ if (trustedPluginNames.length === 0) {
299
+ return;
300
+ }
301
+ throw new Error(
302
+ `juniorNitro({ plugins }) cannot receive a direct defineJuniorPlugins(...) set with trusted plugin registration(s): ${trustedPluginNames.join(", ")}. Export the set from a runtime-safe plugin module and pass juniorNitro({ plugins: "./plugins" }) so createApp() can import the same hooks at runtime.`
303
+ );
304
+ }
174
305
  function juniorNitro(options = {}) {
175
306
  return {
176
307
  nitro: {
@@ -182,12 +313,36 @@ function juniorNitro(options = {}) {
182
313
  nitro.options.vercel.functions ??= {};
183
314
  nitro.options.vercel.functions.maxDuration ??= options.maxDuration ?? 800;
184
315
  applyRolldownTreeshakeWorkaround(nitro);
185
- injectVirtualConfig(nitro, options.plugins);
186
- nitro.hooks.hook("compiled", () => {
316
+ const pluginSource = options.plugins;
317
+ const pluginModule = isPluginModuleReference(pluginSource) ? resolvePluginModule(cwd, pluginSource) : void 0;
318
+ const directPluginSet = isPluginSet(pluginSource) ? pluginSource : void 0;
319
+ if (directPluginSet) {
320
+ assertSerializableDirectPluginSet(directPluginSet);
321
+ }
322
+ let pluginSetPromise;
323
+ const loadConfiguredPluginSet = () => {
324
+ pluginSetPromise ??= pluginModule ? loadPluginSetFromModule(pluginModule) : Promise.resolve(directPluginSet);
325
+ return pluginSetPromise;
326
+ };
327
+ const pluginCatalogConfig = pluginCatalogConfigFromPluginSet(directPluginSet);
328
+ const trustedPluginRegistrations = trustedPluginRegistrationsFromPluginSet(directPluginSet).map(
329
+ (plugin) => plugin.name
330
+ );
331
+ injectVirtualConfig(nitro, {
332
+ ...pluginModule ? {
333
+ loadPluginSet: loadConfiguredPluginSet,
334
+ pluginModule: pluginModule.runtimeModule
335
+ } : {},
336
+ plugins: pluginCatalogConfig,
337
+ trustedPluginRegistrations
338
+ });
339
+ nitro.hooks.hook("compiled", async () => {
340
+ const pluginSet = await loadConfiguredPluginSet();
341
+ const compiledPluginCatalogConfig = pluginCatalogConfigFromPluginSet(pluginSet);
187
342
  copyAppAndPluginContent(
188
343
  cwd,
189
344
  nitro.options.output.serverDir,
190
- options.plugins?.packages
345
+ compiledPluginCatalogConfig?.packages
191
346
  );
192
347
  copyIncludedFiles(
193
348
  cwd,
@@ -0,0 +1,22 @@
1
+ import type { JuniorPluginRegistration } from "@sentry/junior-plugin-api";
2
+ import type { PluginCatalogConfig, PluginManifestConfig } from "@/chat/plugins/types";
3
+ export type JuniorPluginInput = JuniorPluginRegistration | string;
4
+ export interface JuniorPluginSetOptions {
5
+ /** Install-level manifest overrides applied before validation. */
6
+ manifests?: Record<string, PluginManifestConfig>;
7
+ }
8
+ /** Reusable plugin registrations and manifest overrides. */
9
+ export interface JuniorPluginSet {
10
+ /** Install-level manifest overrides applied before validation. */
11
+ manifests?: Record<string, PluginManifestConfig>;
12
+ /** Manifest-only plugin packages included by package name. */
13
+ packageNames: string[];
14
+ /** JavaScript plugin definitions included by package factories. */
15
+ registrations: JuniorPluginRegistration[];
16
+ }
17
+ /** Define package-name plugins and JS plugin definitions for one app. */
18
+ export declare function defineJuniorPlugins(inputs: JuniorPluginInput[], options?: JuniorPluginSetOptions): JuniorPluginSet;
19
+ /** Build the manifest catalog config implied by one plugin set. */
20
+ export declare function pluginCatalogConfigFromPluginSet(pluginSet: JuniorPluginSet | undefined): PluginCatalogConfig | undefined;
21
+ /** Return registrations that expose trusted in-process runtime behavior. */
22
+ export declare function trustedPluginRegistrationsFromPluginSet(pluginSet: JuniorPluginSet | undefined): JuniorPluginRegistration[];
@@ -36,7 +36,7 @@ export interface DashboardRequesterIdentity {
36
36
  }
37
37
  export interface DashboardSessionReport {
38
38
  conversationTitle?: string;
39
- cumulativeDurationMs?: number;
39
+ cumulativeDurationMs: number;
40
40
  cumulativeUsage?: DashboardTurnUsage;
41
41
  conversationId: string;
42
42
  id: string;
package/dist/reporting.js CHANGED
@@ -6,24 +6,24 @@ import {
6
6
  getAgentTurnSessionRecord,
7
7
  listAgentTurnSessionSummaries,
8
8
  listAgentTurnSessionSummariesForConversation
9
- } from "./chunk-ITZ2F7UE.js";
9
+ } from "./chunk-4CRYMG7M.js";
10
10
  import {
11
11
  discoverSkills
12
- } from "./chunk-ITOW4DED.js";
12
+ } from "./chunk-D23WCM66.js";
13
13
  import {
14
14
  canExposeConversationPayload,
15
15
  parseSlackThreadId,
16
16
  resolveConversationPrivacy
17
- } from "./chunk-PEF6UXTY.js";
17
+ } from "./chunk-IGVHCX2U.js";
18
18
  import {
19
19
  getPluginPackageContent,
20
20
  getPluginProviders,
21
21
  isRecord
22
- } from "./chunk-H652GMDH.js";
22
+ } from "./chunk-WDPWFMCE.js";
23
23
  import "./chunk-Z3YD6NHK.js";
24
24
  import {
25
25
  homeDir
26
- } from "./chunk-5LUISFEY.js";
26
+ } from "./chunk-KVZL5NZS.js";
27
27
  import "./chunk-2KG3PWR4.js";
28
28
 
29
29
  // src/reporting.ts
@@ -134,7 +134,7 @@ function sessionReportFromSummary(summary) {
134
134
  lastProgressAt: new Date(summary.lastProgressAtMs).toISOString(),
135
135
  lastSeenAt: new Date(summary.updatedAtMs).toISOString(),
136
136
  ...summary.state === "completed" ? { completedAt: new Date(summary.updatedAtMs).toISOString() } : {},
137
- ...summary.cumulativeDurationMs !== void 0 ? { cumulativeDurationMs: summary.cumulativeDurationMs } : {},
137
+ cumulativeDurationMs: summary.cumulativeDurationMs,
138
138
  ...cumulativeUsage ? { cumulativeUsage } : {},
139
139
  surface: surfaceFromConversationId(summary.conversationId),
140
140
  title: titleFromSummary(summary),
@@ -342,10 +342,16 @@ function turnScopedMessages(messages) {
342
342
  for (let index = messages.length - 1; index >= 0; index -= 1) {
343
343
  const record = messages[index];
344
344
  if (record.role === "user") {
345
- return messages.slice(index);
345
+ return {
346
+ messages: messages.slice(index),
347
+ startsAtRunBoundary: index === 0
348
+ };
346
349
  }
347
350
  }
348
- return messages;
351
+ return {
352
+ messages,
353
+ startsAtRunBoundary: messages.length > 0
354
+ };
349
355
  }
350
356
  function traceIdFromTranscript(transcript) {
351
357
  for (const message of transcript) {
@@ -379,13 +385,16 @@ async function readConversation(conversationId) {
379
385
  summary.conversationId,
380
386
  summary.sessionId
381
387
  );
382
- const scopedMessages = sessionRecord?.piMessages ? turnScopedMessages(sessionRecord.piMessages) : [];
388
+ const scopedMessages = sessionRecord?.piMessages ? turnScopedMessages(sessionRecord.piMessages) : { messages: [], startsAtRunBoundary: false };
383
389
  const canExposeTranscript = canExposeConversationTranscript(summary);
384
- const normalizedTranscript = scopedMessages.map(
390
+ const normalizedTranscript = scopedMessages.messages.map(
385
391
  normalizeTranscriptMessage
386
392
  );
387
393
  const transcriptMessageCount = countConversationMessages(normalizedTranscript);
388
- const transcript = canExposeTranscript ? [systemPromptMessage(), ...normalizedTranscript] : [];
394
+ const transcript = canExposeTranscript ? [
395
+ ...scopedMessages.startsAtRunBoundary && normalizedTranscript.length > 0 ? [systemPromptMessage()] : [],
396
+ ...normalizedTranscript
397
+ ] : [];
389
398
  const transcriptMetadata = canExposeTranscript ? void 0 : normalizedTranscript.map(redactTranscriptMessage);
390
399
  const traceId = summary.traceId ?? sessionRecord?.traceId ?? (canExposeTranscript ? traceIdFromTranscript(transcript) : void 0);
391
400
  const sentryTraceUrl = traceId ? buildSentryTraceUrl(traceId) : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.63.0",
3
+ "version": "0.64.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -64,7 +64,7 @@
64
64
  "node-html-markdown": "^2.0.0",
65
65
  "yaml": "^2.9.0",
66
66
  "zod": "^4.4.3",
67
- "@sentry/junior-plugin-api": "0.63.0"
67
+ "@sentry/junior-plugin-api": "0.64.0"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/node": "^25.9.1",
@@ -76,7 +76,7 @@
76
76
  "typescript": "^6.0.3",
77
77
  "vercel": "^54.4.0",
78
78
  "vitest": "^4.1.7",
79
- "@sentry/junior-scheduler": "0.63.0"
79
+ "@sentry/junior-scheduler": "0.64.0"
80
80
  },
81
81
  "scripts": {
82
82
  "build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",