@sentry/junior 0.119.0 → 0.121.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.
Files changed (52) hide show
  1. package/dist/{agent-hooks-NZSV4QQR.js → agent-hooks-CQDGDBCP.js} +7 -7
  2. package/dist/api.js +58 -79
  3. package/dist/app.js +21 -21
  4. package/dist/build/copy-build-content.d.ts +2 -2
  5. package/dist/{catalog-runtime-TAOYIZIZ.js → catalog-runtime-AD3YNTTM.js} +7 -7
  6. package/dist/chat/discovery.d.ts +2 -0
  7. package/dist/chat/mcp/tool-manager.d.ts +14 -1
  8. package/dist/chat/plugins/types.d.ts +3 -9
  9. package/dist/chat/plugins/user-pages.d.ts +2 -1
  10. package/dist/chat/sandbox/noninteractive-command.d.ts +1 -0
  11. package/dist/chat/sandbox/session.d.ts +2 -2
  12. package/dist/chat/sandbox/snapshot/install.d.ts +2 -2
  13. package/dist/chat/sandbox/snapshot/resolve.d.ts +1 -0
  14. package/dist/chat/sandbox/workspace.d.ts +3 -1
  15. package/dist/{chunk-D3N4BPSL.js → chunk-254MFDOF.js} +2 -2
  16. package/dist/{chunk-4UIUDOOV.js → chunk-3DBMANUI.js} +7 -7
  17. package/dist/{chunk-BLQZACHV.js → chunk-7HYV7KFM.js} +92 -6
  18. package/dist/{chunk-JLJLZS2N.js → chunk-D2EMOH3N.js} +4 -3
  19. package/dist/{chunk-R53UT2OI.js → chunk-ET2SDCNV.js} +9 -5
  20. package/dist/chunk-H4HBKISO.js +417 -0
  21. package/dist/{chunk-VLTWQQKH.js → chunk-IDXLBKPS.js} +1 -1
  22. package/dist/{chunk-NUENIPQL.js → chunk-IK4QSS5H.js} +1 -1
  23. package/dist/{chunk-GKFDU3ZI.js → chunk-ITMEXX5H.js} +44 -23
  24. package/dist/{chunk-ZFX4M743.js → chunk-JYDIBXFY.js} +6 -6
  25. package/dist/{chunk-ZJ6IVZVO.js → chunk-JZ47AFSH.js} +1 -1
  26. package/dist/{chunk-YLYJIQRX.js → chunk-KLUMVJRQ.js} +12 -12
  27. package/dist/{chunk-TAGV4Z3J.js → chunk-LE77HHF3.js} +1 -1
  28. package/dist/{chunk-B3IT5END.js → chunk-OOUNJNMR.js} +16 -152
  29. package/dist/{chunk-QZ7XRJDB.js → chunk-TRECK7RV.js} +3 -3
  30. package/dist/{chunk-VJTWKJWQ.js → chunk-UURFXX66.js} +1 -1
  31. package/dist/{chunk-6ZR455DC.js → chunk-UXQ5XSA2.js} +1 -1
  32. package/dist/{chunk-TGIQN7QW.js → chunk-VWBKDPMQ.js} +242 -69
  33. package/dist/{chunk-JSTGI7TX.js → chunk-WUP7NWMP.js} +1 -1
  34. package/dist/{chunk-JXRAOOXQ.js → chunk-XDRGYDED.js} +1 -1
  35. package/dist/{chunk-BNJEPYTC.js → chunk-Z2XXPUZG.js} +4 -4
  36. package/dist/cli/chat.js +23 -23
  37. package/dist/cli/check.js +8 -8
  38. package/dist/cli/plugins.js +12 -12
  39. package/dist/cli/snapshot-create.js +10 -10
  40. package/dist/cli/upgrade.js +7 -7
  41. package/dist/{db-FWGP5CKY.js → db-7HQT2SXQ.js} +5 -5
  42. package/dist/{discovery-RMHYSRL7.js → discovery-GUFTFBTL.js} +3 -1
  43. package/dist/instrumentation.js +2 -2
  44. package/dist/nitro.js +24 -7
  45. package/dist/{oauth-callback-server-PM3QNXJQ.js → oauth-callback-server-VHTUCNHK.js} +16 -16
  46. package/dist/{runner-DV55N6ZB.js → runner-3ONPZRED.js} +9 -9
  47. package/dist/{skills-MANV5ZK6.js → skills-WGAYGGPA.js} +8 -8
  48. package/dist/{validation-QGB4INTI.js → validation-ZIWNBG4D.js} +8 -8
  49. package/dist/version.js +1 -1
  50. package/package.json +8 -7
  51. package/skills/jr-rpc/SKILL.md +40 -0
  52. package/dist/chunk-3V7OFJZC.js +0 -264
@@ -1,21 +1,23 @@
1
1
  import {
2
2
  getDb
3
- } from "./chunk-D3N4BPSL.js";
3
+ } from "./chunk-254MFDOF.js";
4
4
  import {
5
- juniorConversationAnnotations
5
+ juniorConversationAnnotations,
6
+ juniorIdentities,
7
+ juniorUsers
6
8
  } from "./chunk-GCGJGSQL.js";
7
9
  import {
8
10
  SANDBOX_WORKSPACE_ROOT,
9
11
  getStateAdapter,
10
12
  sleep
11
- } from "./chunk-VJTWKJWQ.js";
13
+ } from "./chunk-UURFXX66.js";
12
14
  import {
13
15
  botConfig,
14
16
  completeObject,
15
17
  embedTexts,
16
18
  getSlackBotToken,
17
19
  standardModelId
18
- } from "./chunk-VLTWQQKH.js";
20
+ } from "./chunk-IDXLBKPS.js";
19
21
  import {
20
22
  isActorUserId,
21
23
  parseActorUserId
@@ -24,10 +26,11 @@ import {
24
26
  logException,
25
27
  logInfo,
26
28
  logWarn,
29
+ normalizeIdentityEmail,
27
30
  setSpanAttributes,
28
31
  setSpanStatus,
29
32
  withSpan
30
- } from "./chunk-ZJ6IVZVO.js";
33
+ } from "./chunk-JZ47AFSH.js";
31
34
  import {
32
35
  parseSlackChannelReferenceId,
33
36
  parseSlackMessageTs,
@@ -321,6 +324,55 @@ function getSlackToolContext(context) {
321
324
  };
322
325
  }
323
326
 
327
+ // src/chat/plugins/viewer-actors.ts
328
+ import { and as and2, eq as eq2 } from "drizzle-orm";
329
+ import {
330
+ actorSchema
331
+ } from "@sentry/junior-plugin-api";
332
+ async function readViewerActorsFromSql(db, email) {
333
+ const normalizedEmail = normalizeIdentityEmail(email);
334
+ if (!normalizedEmail) return [];
335
+ const rows = await db.select({
336
+ displayName: juniorIdentities.displayName,
337
+ email: juniorUsers.primaryEmail,
338
+ handle: juniorIdentities.handle,
339
+ provider: juniorIdentities.provider,
340
+ providerSubjectId: juniorIdentities.providerSubjectId,
341
+ providerTenantId: juniorIdentities.providerTenantId
342
+ }).from(juniorIdentities).innerJoin(juniorUsers, eq2(juniorUsers.id, juniorIdentities.userId)).where(
343
+ and2(
344
+ eq2(juniorUsers.primaryEmailNormalized, normalizedEmail),
345
+ eq2(juniorIdentities.kind, "user")
346
+ )
347
+ );
348
+ const actors = [];
349
+ for (const row of rows) {
350
+ const candidate = row.provider === "slack" ? {
351
+ platform: "slack",
352
+ teamId: row.providerTenantId,
353
+ userId: row.providerSubjectId,
354
+ email: row.email,
355
+ ...row.displayName ? { fullName: row.displayName } : {},
356
+ ...row.handle ? { userName: row.handle } : {}
357
+ } : row.provider === "local" ? {
358
+ platform: "local",
359
+ userId: row.providerSubjectId,
360
+ email: row.email,
361
+ ...row.displayName ? { fullName: row.displayName } : {},
362
+ ...row.handle ? { userName: row.handle } : {}
363
+ } : void 0;
364
+ if (!candidate) continue;
365
+ const parsed = actorSchema.safeParse(candidate);
366
+ if (parsed.success && parsed.data.platform !== "system") {
367
+ actors.push(parsed.data);
368
+ }
369
+ }
370
+ return actors;
371
+ }
372
+ async function readViewerActors(email) {
373
+ return await readViewerActorsFromSql(getDb(), email);
374
+ }
375
+
324
376
  // src/chat/credentials/subject.ts
325
377
  import { createHmac, timingSafeEqual } from "crypto";
326
378
 
@@ -941,6 +993,7 @@ function invocationPluginContext(plugin, context) {
941
993
  ...base,
942
994
  conversationId: context.conversationId,
943
995
  embedder: createPluginEmbedder(plugin.manifest.name),
996
+ model: createPluginModel(plugin.manifest.name, plugin.model),
944
997
  source: context.source,
945
998
  text: context.userText ?? "",
946
999
  state: createPluginState(plugin.manifest.name)
@@ -968,6 +1021,32 @@ function invocationPluginContext(plugin, context) {
968
1021
  actor: context.actor?.platform === "local" ? context.actor : void 0
969
1022
  };
970
1023
  }
1024
+ function pluginMcpContext(plugin, context) {
1025
+ const manager = context.mcpToolManager;
1026
+ const wrappedTools = new Set(plugin.manifest.mcp?.wrappedTools ?? []);
1027
+ if (!manager || wrappedTools.size === 0) {
1028
+ return void 0;
1029
+ }
1030
+ const provider = plugin.manifest.name;
1031
+ const prepare = async () => {
1032
+ await manager.activateProvider(provider);
1033
+ return manager.getActiveProviders().includes(provider) ? "ready" : "authorization_pending";
1034
+ };
1035
+ return {
1036
+ prepare,
1037
+ async callTool(input) {
1038
+ if (await prepare() === "authorization_pending") {
1039
+ return { status: "authorization_pending" };
1040
+ }
1041
+ return await manager.callWrappedTool(
1042
+ provider,
1043
+ input.name,
1044
+ input.arguments ?? {},
1045
+ input.toolCallId ? { toolCallId: input.toolCallId } : void 0
1046
+ );
1047
+ }
1048
+ };
1049
+ }
971
1050
  function safeErrorMessage(error) {
972
1051
  return error instanceof Error ? error.message : String(error);
973
1052
  }
@@ -1239,6 +1318,7 @@ function getPluginTools(context) {
1239
1318
  db: getDb(),
1240
1319
  plugin: pluginName
1241
1320
  }) : void 0;
1321
+ const mcp = pluginMcpContext(plugin, context);
1242
1322
  let pluginContext;
1243
1323
  if (context.source.platform === "slack") {
1244
1324
  if (context.destination.platform !== "slack") {
@@ -1257,6 +1337,7 @@ function getPluginTools(context) {
1257
1337
  userText: context.userText,
1258
1338
  embedder: createPluginEmbedder(pluginName),
1259
1339
  egress: context.egress,
1340
+ ...mcp ? { mcp } : {},
1260
1341
  model: createPluginModel(pluginName, plugin.model),
1261
1342
  state: createPluginState(pluginName)
1262
1343
  };
@@ -1276,6 +1357,7 @@ function getPluginTools(context) {
1276
1357
  userText: context.userText,
1277
1358
  embedder: createPluginEmbedder(pluginName),
1278
1359
  egress: context.egress,
1360
+ ...mcp ? { mcp } : {},
1279
1361
  model: createPluginModel(pluginName, plugin.model),
1280
1362
  state: createPluginState(pluginName)
1281
1363
  };
@@ -1416,7 +1498,10 @@ function getPluginApiRoutes() {
1416
1498
  continue;
1417
1499
  }
1418
1500
  const app = hook({
1419
- ...basePluginContext(plugin)
1501
+ ...basePluginContext(plugin),
1502
+ viewer: {
1503
+ actors: readViewerActors
1504
+ }
1420
1505
  });
1421
1506
  if (app === void 0) {
1422
1507
  continue;
@@ -1853,6 +1938,7 @@ export {
1853
1938
  setDashboardConversationLinkOptions,
1854
1939
  getDashboardConversationLink,
1855
1940
  getSlackToolContext,
1941
+ readViewerActors,
1856
1942
  runWithWorkspaceTeamId,
1857
1943
  getWorkspaceTeamId,
1858
1944
  SlackActionError,
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@sentry/junior",
4
- version: "0.119.0",
4
+ version: "0.121.0",
5
5
  private: false,
6
6
  publishConfig: {
7
7
  access: "public"
@@ -49,7 +49,8 @@ var package_default = {
49
49
  files: [
50
50
  "dist",
51
51
  "bin",
52
- "migrations"
52
+ "migrations",
53
+ "skills"
53
54
  ],
54
55
  scripts: {
55
56
  prepare: "pnpm run build",
@@ -61,7 +62,7 @@ var package_default = {
61
62
  test: "vitest run --maxWorkers=4",
62
63
  "test:watch": "vitest",
63
64
  typecheck: "tsc --noEmit",
64
- "skills:check": "node scripts/check-skills.mjs",
65
+ "skills:check": "node --test scripts/check-skills.test.mjs && node scripts/check-skills.mjs",
65
66
  "test:coverage": "vitest run --maxWorkers=4 --coverage --reporter=default --reporter=junit --outputFile.junit=coverage/results.junit.xml"
66
67
  },
67
68
  dependencies: {
@@ -5,14 +5,14 @@ import {
5
5
  logInfo,
6
6
  logWarn,
7
7
  setSpanAttributes
8
- } from "./chunk-ZJ6IVZVO.js";
8
+ } from "./chunk-JZ47AFSH.js";
9
9
  import {
10
10
  discoverInstalledPluginPackageContent,
11
11
  normalizePluginPackageNames
12
- } from "./chunk-B3IT5END.js";
12
+ } from "./chunk-OOUNJNMR.js";
13
13
  import {
14
14
  pluginRoots
15
- } from "./chunk-3V7OFJZC.js";
15
+ } from "./chunk-H4HBKISO.js";
16
16
 
17
17
  // src/chat/plugins/manifest.ts
18
18
  import { z } from "zod";
@@ -64,6 +64,7 @@ function inlineMcpSource(mcp) {
64
64
  setDefined(result, "url", mcp.url);
65
65
  setDefined(result, "headers", mcp.headers);
66
66
  setDefined(result, "allowed-tools", mcp.allowedTools);
67
+ setDefined(result, "wrapped-tools", mcp.wrappedTools);
67
68
  return result;
68
69
  }
69
70
  function inlineOauthSource(oauth) {
@@ -300,7 +301,8 @@ var mcpSourceSchema = z.object({
300
301
  }).optional(),
301
302
  url: httpsUrlString,
302
303
  headers: stringMapSchema.optional(),
303
- "allowed-tools": nonEmptyStringArraySchema("allowed-tools").optional()
304
+ "allowed-tools": nonEmptyStringArraySchema("allowed-tools").optional(),
305
+ "wrapped-tools": nonEmptyStringArraySchema("wrapped-tools").optional()
304
306
  }).passthrough();
305
307
  var targetSourceSchema = z.object({
306
308
  type: nonEmptyTrimmedString.refine((value) => PLUGIN_NAME_RE.test(value), {
@@ -389,6 +391,7 @@ function manifestConfigPatch(config) {
389
391
  setDefined2(mcp, "url", config.mcp.url);
390
392
  setDefined2(mcp, "headers", config.mcp.headers);
391
393
  setDefined2(mcp, "allowed-tools", config.mcp.allowedTools);
394
+ setDefined2(mcp, "wrapped-tools", config.mcp.wrappedTools);
392
395
  result.mcp = mcp;
393
396
  }
394
397
  }
@@ -829,7 +832,8 @@ function normalizeMcp(data, envVars, name) {
829
832
  transport: "http",
830
833
  url: result.data.url,
831
834
  ...headers ? { headers } : {},
832
- ...result.data["allowed-tools"] ? { allowedTools: result.data["allowed-tools"] } : {}
835
+ ...result.data["allowed-tools"] ? { allowedTools: result.data["allowed-tools"] } : {},
836
+ ...result.data["wrapped-tools"] ? { wrappedTools: result.data["wrapped-tools"] } : {}
833
837
  };
834
838
  }
835
839
  function parseManifestSource(parsedSource, dir, config, options) {
@@ -0,0 +1,417 @@
1
+ // src/chat/discovery.ts
2
+ import fs, { statSync as statSync2 } from "fs";
3
+ import path2 from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ // src/package-resolution.ts
7
+ import { statSync } from "fs";
8
+ import { createRequire } from "module";
9
+ import path from "path";
10
+ function isDirectory(targetPath) {
11
+ try {
12
+ return statSync(targetPath).isDirectory();
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+ function isFile(targetPath) {
18
+ try {
19
+ return statSync(targetPath).isFile();
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ function uniqueResolvedPathsInOrder(values) {
25
+ const seen = /* @__PURE__ */ new Set();
26
+ const resolved = [];
27
+ for (const value of values) {
28
+ const normalized = path.resolve(value);
29
+ if (seen.has(normalized)) {
30
+ continue;
31
+ }
32
+ seen.add(normalized);
33
+ resolved.push(normalized);
34
+ }
35
+ return resolved;
36
+ }
37
+ function ancestorNodeModulesDirs(cwd) {
38
+ const dirs = [];
39
+ let current = path.resolve(cwd);
40
+ while (true) {
41
+ const nodeModulesDir = path.join(current, "node_modules");
42
+ if (isDirectory(nodeModulesDir)) {
43
+ dirs.push(nodeModulesDir);
44
+ }
45
+ const parent = path.dirname(current);
46
+ if (parent === current) {
47
+ break;
48
+ }
49
+ current = parent;
50
+ }
51
+ return dirs;
52
+ }
53
+ function packageDirInNodeModules(nodeModulesDir, packageName) {
54
+ const packageDir = path.join(nodeModulesDir, ...packageName.split("/"));
55
+ return isDirectory(packageDir) ? path.resolve(packageDir) : void 0;
56
+ }
57
+ function isValidPackageSegment(segment) {
58
+ return Boolean(segment) && segment !== "." && segment !== ".." && !segment.startsWith(".") && /^[A-Za-z0-9._~-]+$/.test(segment);
59
+ }
60
+ function isValidPackageName(packageName) {
61
+ if (!packageName || packageName.includes("\\") || path.isAbsolute(packageName)) {
62
+ return false;
63
+ }
64
+ const parts = packageName.split("/");
65
+ if (parts[0].startsWith("@")) {
66
+ return parts.length === 2 && parts[0].length > 1 && isValidPackageSegment(parts[0].slice(1)) && isValidPackageSegment(parts[1]);
67
+ }
68
+ return parts.length === 1 && isValidPackageSegment(parts[0]);
69
+ }
70
+ function findPackageRoot(entryPath) {
71
+ let dir = path.dirname(entryPath);
72
+ while (dir !== path.dirname(dir)) {
73
+ if (isFile(path.join(dir, "package.json"))) {
74
+ return path.resolve(dir);
75
+ }
76
+ dir = path.dirname(dir);
77
+ }
78
+ return void 0;
79
+ }
80
+ function findPackageNodeModulesDir(packageDir, packageName) {
81
+ const parts = path.resolve(packageDir).split(path.sep);
82
+ const packageParts = packageName.split("/");
83
+ for (let index = parts.length - 1; index >= 0; index -= 1) {
84
+ if (parts[index] !== "node_modules") {
85
+ continue;
86
+ }
87
+ const candidatePackageParts = parts.slice(
88
+ index + 1,
89
+ index + 1 + packageParts.length
90
+ );
91
+ if (candidatePackageParts.join("/") !== packageParts.join("/")) {
92
+ continue;
93
+ }
94
+ return path.resolve(parts.slice(0, index + 1).join(path.sep) || path.sep);
95
+ }
96
+ return void 0;
97
+ }
98
+ function resolvePackageWithNode(cwd, packageName) {
99
+ let requireFromCwd;
100
+ try {
101
+ requireFromCwd = createRequire(path.join(cwd, "package.json"));
102
+ } catch {
103
+ return void 0;
104
+ }
105
+ for (const specifier of [`${packageName}/package.json`, packageName]) {
106
+ try {
107
+ const resolved = requireFromCwd.resolve(specifier);
108
+ const dir = specifier.endsWith("/package.json") ? path.dirname(resolved) : findPackageRoot(resolved);
109
+ if (!dir) {
110
+ continue;
111
+ }
112
+ const nodeModulesDir = findPackageNodeModulesDir(dir, packageName);
113
+ return {
114
+ dir,
115
+ ...nodeModulesDir ? { nodeModulesDir } : {}
116
+ };
117
+ } catch {
118
+ }
119
+ }
120
+ return void 0;
121
+ }
122
+ function resolvePackageLocation(cwd, packageName, options) {
123
+ if (!isValidPackageName(packageName)) {
124
+ return void 0;
125
+ }
126
+ const nodeModulesDirs = uniqueResolvedPathsInOrder([
127
+ ...options?.nodeModulesDirs ?? [],
128
+ ...ancestorNodeModulesDirs(cwd)
129
+ ]);
130
+ for (const nodeModulesDir of nodeModulesDirs) {
131
+ const dir = packageDirInNodeModules(nodeModulesDir, packageName);
132
+ if (dir) {
133
+ return { dir, nodeModulesDir };
134
+ }
135
+ }
136
+ return resolvePackageWithNode(cwd, packageName);
137
+ }
138
+ function resolvePackageDir(cwd, packageName) {
139
+ return resolvePackageLocation(cwd, packageName)?.dir;
140
+ }
141
+
142
+ // src/chat/discovery.ts
143
+ function isDirectory2(targetPath) {
144
+ try {
145
+ return statSync2(targetPath).isDirectory();
146
+ } catch {
147
+ return false;
148
+ }
149
+ }
150
+ function isFile2(targetPath) {
151
+ try {
152
+ return statSync2(targetPath).isFile();
153
+ } catch {
154
+ return false;
155
+ }
156
+ }
157
+ function normalizePath(targetPath) {
158
+ return path2.resolve(targetPath);
159
+ }
160
+ function uniqueResolvedPathsInOrder2(values) {
161
+ const seen = /* @__PURE__ */ new Set();
162
+ const resolved = [];
163
+ for (const value of values) {
164
+ const normalized = normalizePath(value);
165
+ if (seen.has(normalized)) {
166
+ continue;
167
+ }
168
+ seen.add(normalized);
169
+ resolved.push(normalized);
170
+ }
171
+ return resolved;
172
+ }
173
+ function isNodeModulesPath(candidatePath) {
174
+ return path2.basename(candidatePath) === "node_modules";
175
+ }
176
+ function isInsidePnpmStore(candidatePath) {
177
+ return candidatePath.split(path2.sep).includes(".pnpm");
178
+ }
179
+ function runningFromInstalledPackage() {
180
+ const currentFile = fileURLToPath(import.meta.url);
181
+ const marker = `${path2.sep}node_modules${path2.sep}@sentry${path2.sep}junior${path2.sep}`;
182
+ return currentFile.includes(marker);
183
+ }
184
+ function listInstalledPackageNodeModulesDirs() {
185
+ if (!runningFromInstalledPackage()) {
186
+ return [];
187
+ }
188
+ const dirs = [];
189
+ let current = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)));
190
+ while (true) {
191
+ if (isNodeModulesPath(current) && !isInsidePnpmStore(current) && isDirectory2(current)) {
192
+ dirs.push(current);
193
+ }
194
+ const parent = path2.dirname(current);
195
+ if (parent === current) {
196
+ break;
197
+ }
198
+ current = parent;
199
+ }
200
+ return dirs;
201
+ }
202
+ function listCwdAncestorNodeModulesDirs(cwd) {
203
+ const resolvedCwd = normalizePath(cwd);
204
+ const dirs = [];
205
+ let current = resolvedCwd;
206
+ while (true) {
207
+ const nodeModulesDir = path2.join(current, "node_modules");
208
+ if (isDirectory2(nodeModulesDir)) {
209
+ dirs.push(nodeModulesDir);
210
+ }
211
+ if (isFile2(path2.join(current, "package.json"))) {
212
+ break;
213
+ }
214
+ const parent = path2.dirname(current);
215
+ if (parent === current) {
216
+ break;
217
+ }
218
+ current = parent;
219
+ }
220
+ return dirs;
221
+ }
222
+ function discoverNodeModulesDirs(cwd = process.cwd(), options) {
223
+ const explicit = options?.candidateDirs?.filter((dir) => isDirectory2(dir)) ?? [];
224
+ if (explicit.length > 0) {
225
+ return uniqueResolvedPathsInOrder2(explicit);
226
+ }
227
+ return uniqueResolvedPathsInOrder2([
228
+ ...listInstalledPackageNodeModulesDirs(),
229
+ ...listCwdAncestorNodeModulesDirs(cwd)
230
+ ]);
231
+ }
232
+ function discoverProjectRoots(cwd = process.cwd(), options) {
233
+ const roots = discoverNodeModulesDirs(
234
+ cwd,
235
+ options?.nodeModulesDirs ? { candidateDirs: options.nodeModulesDirs } : void 0
236
+ ).map((nodeModulesDir) => path2.dirname(nodeModulesDir));
237
+ return uniqueResolvedPathsInOrder2([cwd, ...roots]);
238
+ }
239
+ function unique(values) {
240
+ return [...new Set(values)];
241
+ }
242
+ function pathExists(targetPath) {
243
+ try {
244
+ fs.accessSync(targetPath);
245
+ return true;
246
+ } catch {
247
+ return false;
248
+ }
249
+ }
250
+ function hasAnyDataMarkers(appDir) {
251
+ return pathExists(path2.join(appDir, "SOUL.md")) || pathExists(path2.join(appDir, "WORLD.md"));
252
+ }
253
+ function scoreAppCandidate(appDir) {
254
+ let score = 0;
255
+ if (pathExists(path2.join(appDir, "SOUL.md"))) {
256
+ score += 4;
257
+ }
258
+ if (pathExists(path2.join(appDir, "WORLD.md"))) {
259
+ score += 2;
260
+ }
261
+ if (pathExists(path2.join(appDir, "skills"))) {
262
+ score += 1;
263
+ }
264
+ if (pathExists(path2.join(appDir, "plugins"))) {
265
+ score += 1;
266
+ }
267
+ return score;
268
+ }
269
+ function resolveCandidateAppDirs(cwd, projectRoots) {
270
+ const roots = projectRoots ?? discoverProjectRoots(cwd);
271
+ const resolved = [];
272
+ const seen = /* @__PURE__ */ new Set();
273
+ for (const root of roots) {
274
+ const appDir = path2.resolve(root, "app");
275
+ if (!pathExists(appDir)) {
276
+ continue;
277
+ }
278
+ if (seen.has(appDir)) {
279
+ continue;
280
+ }
281
+ seen.add(appDir);
282
+ resolved.push(appDir);
283
+ }
284
+ return resolved;
285
+ }
286
+ function homeDir() {
287
+ return resolveHomeDir();
288
+ }
289
+ function resolveHomeDir(cwd = process.cwd(), options) {
290
+ const resolvedCwd = path2.resolve(cwd);
291
+ const directApp = path2.resolve(resolvedCwd, "app");
292
+ if (pathExists(directApp) && hasAnyDataMarkers(directApp)) {
293
+ return directApp;
294
+ }
295
+ const candidates = resolveCandidateAppDirs(
296
+ resolvedCwd,
297
+ options?.projectRoots
298
+ );
299
+ if (candidates.length === 0) {
300
+ return directApp;
301
+ }
302
+ candidates.sort((left, right) => {
303
+ const leftScore = scoreAppCandidate(left);
304
+ const rightScore = scoreAppCandidate(right);
305
+ if (leftScore !== rightScore) {
306
+ return rightScore - leftScore;
307
+ }
308
+ const leftDistance = path2.relative(resolvedCwd, left).split(path2.sep).length;
309
+ const rightDistance = path2.relative(resolvedCwd, right).split(path2.sep).length;
310
+ if (leftDistance !== rightDistance) {
311
+ return leftDistance - rightDistance;
312
+ }
313
+ return left.localeCompare(right);
314
+ });
315
+ return candidates[0];
316
+ }
317
+ function resolveContentRoots(subdir) {
318
+ if (subdir === "data") {
319
+ return [homeDir()];
320
+ }
321
+ return [path2.join(homeDir(), subdir)];
322
+ }
323
+ function builtInSkillsDir() {
324
+ const moduleDir = path2.dirname(fileURLToPath(import.meta.url));
325
+ if (path2.basename(moduleDir) === "dist") {
326
+ return path2.join(path2.dirname(moduleDir), "skills");
327
+ }
328
+ if (path2.basename(moduleDir) === "chat" && path2.basename(path2.dirname(moduleDir)) === "src") {
329
+ return path2.resolve(moduleDir, "../../skills");
330
+ }
331
+ const packageDir = resolvePackageDir(process.cwd(), "@sentry/junior");
332
+ return path2.join(packageDir ?? moduleDir, "skills");
333
+ }
334
+ function dataDir() {
335
+ return resolveContentRoots("data")[0];
336
+ }
337
+ function soulPath() {
338
+ return path2.join(dataDir(), "SOUL.md");
339
+ }
340
+ function worldPath() {
341
+ return path2.join(dataDir(), "WORLD.md");
342
+ }
343
+ function descriptionPath() {
344
+ return path2.join(dataDir(), "DESCRIPTION.md");
345
+ }
346
+ function skillsDir() {
347
+ return resolveContentRoots("skills")[0];
348
+ }
349
+ function pluginsDir() {
350
+ return resolveContentRoots("plugins")[0];
351
+ }
352
+ function dataRoots() {
353
+ return unique(resolveContentRoots("data"));
354
+ }
355
+ function skillRoots() {
356
+ return unique([builtInSkillsDir(), ...resolveContentRoots("skills")]);
357
+ }
358
+ function pluginRoots() {
359
+ return unique(resolveContentRoots("plugins"));
360
+ }
361
+ function soulPathCandidates() {
362
+ const candidates = dataRoots().map((root) => path2.join(root, "SOUL.md"));
363
+ return unique(candidates);
364
+ }
365
+ function worldPathCandidates() {
366
+ const candidates = dataRoots().map((root) => path2.join(root, "WORLD.md"));
367
+ return unique(candidates);
368
+ }
369
+ function descriptionPathCandidates() {
370
+ const candidates = dataRoots().map(
371
+ (root) => path2.join(root, "DESCRIPTION.md")
372
+ );
373
+ return unique(candidates);
374
+ }
375
+ var RESERVED_APP_FILES = /* @__PURE__ */ new Set([
376
+ "SOUL.md",
377
+ "WORLD.md",
378
+ "DESCRIPTION.md",
379
+ "ABOUT.md"
380
+ ]);
381
+ function listReferenceFiles() {
382
+ const appDir = homeDir();
383
+ try {
384
+ const entries = fs.readdirSync(appDir, { withFileTypes: true });
385
+ return entries.filter(
386
+ (entry) => entry.isFile() && entry.name.endsWith(".md") && !RESERVED_APP_FILES.has(entry.name)
387
+ ).map((entry) => path2.join(appDir, entry.name)).sort();
388
+ } catch {
389
+ return [];
390
+ }
391
+ }
392
+
393
+ export {
394
+ isValidPackageName,
395
+ resolvePackageLocation,
396
+ resolvePackageDir,
397
+ isDirectory2 as isDirectory,
398
+ isFile2 as isFile,
399
+ discoverNodeModulesDirs,
400
+ discoverProjectRoots,
401
+ homeDir,
402
+ resolveHomeDir,
403
+ builtInSkillsDir,
404
+ dataDir,
405
+ soulPath,
406
+ worldPath,
407
+ descriptionPath,
408
+ skillsDir,
409
+ pluginsDir,
410
+ dataRoots,
411
+ skillRoots,
412
+ pluginRoots,
413
+ soulPathCandidates,
414
+ worldPathCandidates,
415
+ descriptionPathCandidates,
416
+ listReferenceFiles
417
+ };
@@ -12,7 +12,7 @@ import {
12
12
  normalizeGenAiFinishReason,
13
13
  serializeGenAiAttribute,
14
14
  withSpan
15
- } from "./chunk-ZJ6IVZVO.js";
15
+ } from "./chunk-JZ47AFSH.js";
16
16
 
17
17
  // src/chat/config.ts
18
18
  import { getModel as getModel2 } from "@earendil-works/pi-ai/compat";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  JUNIOR_VERSION
3
- } from "./chunk-JLJLZS2N.js";
3
+ } from "./chunk-D2EMOH3N.js";
4
4
 
5
5
  // src/deployment.ts
6
6
  var JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";