@sentry/junior 0.65.0 → 0.65.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 (69) hide show
  1. package/dist/app.d.ts +3 -0
  2. package/dist/app.js +3299 -1343
  3. package/dist/chat/agent-dispatch/heartbeat.d.ts +8 -0
  4. package/dist/chat/agent-dispatch/store.d.ts +2 -2
  5. package/dist/chat/agent-dispatch/types.d.ts +8 -22
  6. package/dist/chat/agent-dispatch/validation.d.ts +3 -1
  7. package/dist/chat/app/production.d.ts +11 -5
  8. package/dist/chat/capabilities/factory.d.ts +2 -1
  9. package/dist/chat/capabilities/router.d.ts +3 -2
  10. package/dist/chat/config.d.ts +5 -0
  11. package/dist/chat/credentials/broker.d.ts +2 -1
  12. package/dist/chat/credentials/context.d.ts +28 -0
  13. package/dist/chat/credentials/subject.d.ts +20 -0
  14. package/dist/chat/ingress/slack-webhook.d.ts +19 -0
  15. package/dist/chat/mcp/errors.d.ts +1 -1
  16. package/dist/chat/plugins/github-permissions.d.ts +11 -0
  17. package/dist/chat/plugins/types.d.ts +2 -0
  18. package/dist/chat/prompt.d.ts +2 -0
  19. package/dist/chat/respond-helpers.d.ts +4 -2
  20. package/dist/chat/respond.d.ts +23 -13
  21. package/dist/chat/runtime/processing-reaction.d.ts +1 -0
  22. package/dist/chat/runtime/reply-executor.d.ts +5 -0
  23. package/dist/chat/runtime/request-deadline.d.ts +8 -0
  24. package/dist/chat/runtime/slack-resume.d.ts +4 -5
  25. package/dist/chat/runtime/slack-runtime.d.ts +10 -0
  26. package/dist/chat/runtime/thread-context.d.ts +5 -2
  27. package/dist/chat/runtime/timeout-resume-runner.d.ts +19 -0
  28. package/dist/chat/runtime/turn-preparation.d.ts +1 -0
  29. package/dist/chat/runtime/turn.d.ts +13 -0
  30. package/dist/chat/sandbox/egress-policy.d.ts +1 -1
  31. package/dist/chat/sandbox/egress-session.d.ts +13 -12
  32. package/dist/chat/sandbox/sandbox.d.ts +2 -3
  33. package/dist/chat/services/timeout-resume.d.ts +9 -4
  34. package/dist/chat/services/turn-session-record.d.ts +19 -2
  35. package/dist/chat/slack/adapter-context.d.ts +25 -0
  36. package/dist/chat/slack/conversation-context.d.ts +28 -0
  37. package/dist/chat/state/turn-session.d.ts +1 -1
  38. package/dist/chat/task-execution/heartbeat.d.ts +13 -0
  39. package/dist/chat/task-execution/queue-signing.d.ts +12 -0
  40. package/dist/chat/task-execution/queue.d.ts +13 -0
  41. package/dist/chat/task-execution/slack-work.d.ts +32 -0
  42. package/dist/chat/task-execution/store.d.ts +153 -0
  43. package/dist/chat/task-execution/vercel-callback.d.ts +21 -0
  44. package/dist/chat/task-execution/vercel-queue.d.ts +16 -0
  45. package/dist/chat/task-execution/worker.d.ts +28 -0
  46. package/dist/{chunk-EDHJIYHS.js → chunk-657CJCUO.js} +218 -62
  47. package/dist/chunk-6YY4Q3D4.js +12 -0
  48. package/dist/chunk-75UZ4JLC.js +213 -0
  49. package/dist/{chunk-PYU2YB35.js → chunk-F57QSMOJ.js} +1 -1
  50. package/dist/{chunk-K3JNVV4Q.js → chunk-HRQQS63X.js} +2 -2
  51. package/dist/{chunk-SCQPBJAU.js → chunk-QUXPUKBH.js} +1 -7
  52. package/dist/{chunk-7KZXQNA6.js → chunk-X2HJLKQT.js} +368 -13
  53. package/dist/{chunk-OMQ5X5QH.js → chunk-ZOV3XJAH.js} +3 -2
  54. package/dist/cli/check.js +115 -2
  55. package/dist/cli/init.js +13 -1
  56. package/dist/cli/snapshot-warmup.js +3 -3
  57. package/dist/deployment.d.ts +4 -0
  58. package/dist/handlers/heartbeat.d.ts +5 -1
  59. package/dist/handlers/turn-resume.d.ts +3 -2
  60. package/dist/handlers/webhooks.d.ts +11 -9
  61. package/dist/nitro.d.ts +3 -1
  62. package/dist/nitro.js +48 -4
  63. package/dist/reporting.js +13 -16
  64. package/dist/vercel.d.ts +1 -1
  65. package/dist/vercel.js +1 -1
  66. package/package.json +5 -4
  67. package/dist/chat/services/turn-continuation-response.d.ts +0 -2
  68. package/dist/chat/slack/turn-continuation-notice.d.ts +0 -8
  69. package/dist/chunk-5VDO6LSG.js +0 -104
@@ -1363,6 +1363,11 @@ function inlineCredentialsSource(credentials) {
1363
1363
  setDefined(result, "app-id-env", credentials.appIdEnv);
1364
1364
  setDefined(result, "private-key-env", credentials.privateKeyEnv);
1365
1365
  setDefined(result, "installation-id-env", credentials.installationIdEnv);
1366
+ setDefined(
1367
+ result,
1368
+ "system-read-permissions",
1369
+ credentials.systemReadPermissions
1370
+ );
1366
1371
  }
1367
1372
  return result;
1368
1373
  }
@@ -1441,6 +1446,103 @@ function inlineManifestSource(manifest) {
1441
1446
  return result;
1442
1447
  }
1443
1448
 
1449
+ // src/chat/plugins/github-permissions.ts
1450
+ var KNOWN_GITHUB_PERMISSION_SCOPES = /* @__PURE__ */ new Set([
1451
+ "actions",
1452
+ "administration",
1453
+ "checks",
1454
+ "codespaces",
1455
+ "contents",
1456
+ "deployments",
1457
+ "environments",
1458
+ "issues",
1459
+ "metadata",
1460
+ "packages",
1461
+ "pages",
1462
+ "pull_requests",
1463
+ "repository_hooks",
1464
+ "repository_projects",
1465
+ "secret_scanning_alerts",
1466
+ "secrets",
1467
+ "security_events",
1468
+ "statuses",
1469
+ "vulnerability_alerts",
1470
+ "workflows"
1471
+ ]);
1472
+ var DEFAULT_GITHUB_SYSTEM_READ_SCOPES = /* @__PURE__ */ new Set([
1473
+ "actions",
1474
+ "checks",
1475
+ "contents",
1476
+ "issues",
1477
+ "metadata",
1478
+ "pull_requests",
1479
+ "statuses"
1480
+ ]);
1481
+ function normalizeGitHubPermissionScope(rawScope) {
1482
+ return rawScope.trim().replace(/-/g, "_");
1483
+ }
1484
+ function normalizeGitHubSystemReadPermissionScopes(scopes, context) {
1485
+ return scopes.map((rawScope) => {
1486
+ const scope = normalizeGitHubPermissionScope(rawScope);
1487
+ if (!KNOWN_GITHUB_PERMISSION_SCOPES.has(scope)) {
1488
+ throw new Error(`${context} contains unsupported scope "${rawScope}"`);
1489
+ }
1490
+ return scope;
1491
+ });
1492
+ }
1493
+ function githubCapabilitiesToPermissions(capabilities, pluginName) {
1494
+ const permissions = {};
1495
+ const prefix = `${pluginName}.`;
1496
+ for (const capability of capabilities) {
1497
+ if (!capability.startsWith(prefix)) {
1498
+ throw new Error(`Unsupported GitHub capability: ${capability}`);
1499
+ }
1500
+ const suffix = capability.slice(prefix.length);
1501
+ const lastDot = suffix.lastIndexOf(".");
1502
+ if (lastDot === -1) {
1503
+ throw new Error(`Unsupported GitHub capability: ${capability}`);
1504
+ }
1505
+ const scopeRaw = suffix.slice(0, lastDot);
1506
+ const level = suffix.slice(lastDot + 1);
1507
+ if (level !== "read" && level !== "write") {
1508
+ throw new Error(`Unsupported GitHub capability: ${capability}`);
1509
+ }
1510
+ const scope = normalizeGitHubPermissionScope(scopeRaw);
1511
+ if (!KNOWN_GITHUB_PERMISSION_SCOPES.has(scope)) {
1512
+ throw new Error(`Unsupported GitHub capability: ${capability}`);
1513
+ }
1514
+ const existing = permissions[scope];
1515
+ permissions[scope] = existing === "write" || level === "write" ? "write" : "read";
1516
+ }
1517
+ return permissions;
1518
+ }
1519
+ function githubSystemReadPermissionsFromScopes(scopes) {
1520
+ const readOnly = {
1521
+ metadata: "read"
1522
+ };
1523
+ for (const scope of normalizeGitHubSystemReadPermissionScopes(
1524
+ scopes,
1525
+ "GitHub system read permissions"
1526
+ )) {
1527
+ readOnly[scope] = "read";
1528
+ }
1529
+ return readOnly;
1530
+ }
1531
+ function githubInstallationReadPermissions(permissions, allowedScopes) {
1532
+ const readOnly = {
1533
+ metadata: "read"
1534
+ };
1535
+ for (const [scope, level] of Object.entries(permissions ?? {})) {
1536
+ if (!allowedScopes.has(scope) || !KNOWN_GITHUB_PERMISSION_SCOPES.has(scope)) {
1537
+ continue;
1538
+ }
1539
+ if (level === "read" || level === "write" || level === "admin") {
1540
+ readOnly[scope] = "read";
1541
+ }
1542
+ }
1543
+ return readOnly;
1544
+ }
1545
+
1444
1546
  // src/chat/plugins/manifest.ts
1445
1547
  var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
1446
1548
  var SHORT_CAPABILITY_RE = /^[a-z0-9-]+(\.[a-z0-9-]+)*$/;
@@ -1580,7 +1682,10 @@ var githubAppCredentialsSchema = baseCredentialsSchema.extend({
1580
1682
  type: z.literal("github-app"),
1581
1683
  "app-id-env": envVarString,
1582
1684
  "private-key-env": envVarString,
1583
- "installation-id-env": envVarString
1685
+ "installation-id-env": envVarString,
1686
+ "system-read-permissions": nonEmptyStringArraySchema(
1687
+ "system-read-permissions"
1688
+ ).optional()
1584
1689
  });
1585
1690
  var runtimeDependencyEntrySchema = z.object({
1586
1691
  type: z.enum(["npm", "system"]),
@@ -1704,6 +1809,11 @@ function manifestConfigPatch(config) {
1704
1809
  "installation-id-env",
1705
1810
  config.credentials.installationIdEnv
1706
1811
  );
1812
+ setDefined2(
1813
+ credentials,
1814
+ "system-read-permissions",
1815
+ config.credentials.systemReadPermissions
1816
+ );
1707
1817
  result.credentials = credentials;
1708
1818
  }
1709
1819
  }
@@ -1962,6 +2072,10 @@ function normalizeCredentials(data, name) {
1962
2072
  `Plugin ${name} credentials.api-headers`,
1963
2073
  { forbiddenKeys: FORBIDDEN_API_HEADER_NAMES }
1964
2074
  ) : void 0;
2075
+ const systemReadPermissions = result.data["system-read-permissions"] ? normalizeGitHubSystemReadPermissionScopes(
2076
+ result.data["system-read-permissions"],
2077
+ `Plugin ${name} credentials.system-read-permissions`
2078
+ ) : void 0;
1965
2079
  return {
1966
2080
  type: "github-app",
1967
2081
  domains,
@@ -1970,7 +2084,8 @@ function normalizeCredentials(data, name) {
1970
2084
  ...result.data["auth-token-placeholder"] ? { authTokenPlaceholder: result.data["auth-token-placeholder"] } : {},
1971
2085
  appIdEnv: result.data["app-id-env"],
1972
2086
  privateKeyEnv: result.data["private-key-env"],
1973
- installationIdEnv: result.data["installation-id-env"]
2087
+ installationIdEnv: result.data["installation-id-env"],
2088
+ ...systemReadPermissions ? { systemReadPermissions } : {}
1974
2089
  };
1975
2090
  }
1976
2091
  function normalizeRuntimeDependencies(entries, name) {
@@ -2620,54 +2735,6 @@ function resolveGitHubApiDomain(credentials) {
2620
2735
  }
2621
2736
  return apiDomain;
2622
2737
  }
2623
- var KNOWN_SCOPES = /* @__PURE__ */ new Set([
2624
- "actions",
2625
- "administration",
2626
- "checks",
2627
- "codespaces",
2628
- "contents",
2629
- "deployments",
2630
- "environments",
2631
- "issues",
2632
- "metadata",
2633
- "packages",
2634
- "pages",
2635
- "pull_requests",
2636
- "repository_hooks",
2637
- "repository_projects",
2638
- "secret_scanning_alerts",
2639
- "secrets",
2640
- "security_events",
2641
- "statuses",
2642
- "vulnerability_alerts",
2643
- "workflows"
2644
- ]);
2645
- function capabilitiesToPermissions(capabilities, pluginName) {
2646
- const permissions = {};
2647
- const prefix = `${pluginName}.`;
2648
- for (const capability of capabilities) {
2649
- if (!capability.startsWith(prefix)) {
2650
- throw new Error(`Unsupported GitHub capability: ${capability}`);
2651
- }
2652
- const suffix = capability.slice(prefix.length);
2653
- const lastDot = suffix.lastIndexOf(".");
2654
- if (lastDot === -1) {
2655
- throw new Error(`Unsupported GitHub capability: ${capability}`);
2656
- }
2657
- const scopeRaw = suffix.slice(0, lastDot);
2658
- const level = suffix.slice(lastDot + 1);
2659
- if (level !== "read" && level !== "write") {
2660
- throw new Error(`Unsupported GitHub capability: ${capability}`);
2661
- }
2662
- const scope = scopeRaw.replace(/-/g, "_");
2663
- if (!KNOWN_SCOPES.has(scope)) {
2664
- throw new Error(`Unsupported GitHub capability: ${capability}`);
2665
- }
2666
- const existing = permissions[scope];
2667
- permissions[scope] = existing === "write" || level === "write" ? "write" : "read";
2668
- }
2669
- return permissions;
2670
- }
2671
2738
  function createGitHubAppBroker(manifest, credentials) {
2672
2739
  const provider = manifest.name;
2673
2740
  const {
@@ -2694,7 +2761,23 @@ function createGitHubAppBroker(manifest, credentials) {
2694
2761
  const normalizedApiDomain = apiDomain.toLowerCase();
2695
2762
  return normalizedDomain === "github.com" || normalizedApiDomain.startsWith("api.") && normalizedDomain === normalizedApiDomain.slice("api.".length);
2696
2763
  }
2697
- const permissions = manifest.capabilities?.length ? capabilitiesToPermissions(manifest.capabilities, provider) : void 0;
2764
+ const permissions = manifest.capabilities?.length ? githubCapabilitiesToPermissions(manifest.capabilities, provider) : void 0;
2765
+ const systemReadPermissions = credentials.systemReadPermissions?.length ? githubSystemReadPermissionsFromScopes(credentials.systemReadPermissions) : void 0;
2766
+ async function resolveTokenPermissions(params) {
2767
+ if (!params.systemActor) {
2768
+ return permissions;
2769
+ }
2770
+ if (systemReadPermissions) {
2771
+ return systemReadPermissions;
2772
+ }
2773
+ const installation = await githubRequest(apiBase, `/app/installations/${params.installationId}`, {
2774
+ token: params.appJwt
2775
+ });
2776
+ return githubInstallationReadPermissions(
2777
+ installation.permissions,
2778
+ DEFAULT_GITHUB_SYSTEM_READ_SCOPES
2779
+ );
2780
+ }
2698
2781
  function createLease(params) {
2699
2782
  return {
2700
2783
  id: randomUUID2(),
@@ -2734,9 +2817,14 @@ function createGitHubAppBroker(manifest, credentials) {
2734
2817
  return {
2735
2818
  async issue(input) {
2736
2819
  const installationId = resolveInstallationId();
2737
- const tokenRequestBody = permissions ? { permissions } : {};
2738
2820
  const appId = resolveAppId(appIdEnv);
2739
2821
  const appJwt = createAppJwt(appId, privateKeyEnv);
2822
+ const tokenPermissions = await resolveTokenPermissions({
2823
+ appJwt,
2824
+ installationId,
2825
+ systemActor: input.context.actor.type === "system"
2826
+ });
2827
+ const tokenRequestBody = tokenPermissions ? { permissions: tokenPermissions } : {};
2740
2828
  const accessTokenResponse = await githubRequest(apiBase, `/app/installations/${installationId}/access_tokens`, {
2741
2829
  method: "POST",
2742
2830
  token: appJwt,
@@ -2770,6 +2858,79 @@ var CredentialUnavailableError = class extends Error {
2770
2858
  }
2771
2859
  };
2772
2860
 
2861
+ // src/chat/credentials/context.ts
2862
+ function credentialUserSubjectId(context) {
2863
+ if (context.actor.type === "user") {
2864
+ return context.actor.userId;
2865
+ }
2866
+ return context.subject?.userId;
2867
+ }
2868
+ function parseCredentialContext(value) {
2869
+ if (!value || typeof value !== "object") {
2870
+ return void 0;
2871
+ }
2872
+ const record = value;
2873
+ const actor = parseActor(record.actor);
2874
+ if (!actor) {
2875
+ return void 0;
2876
+ }
2877
+ if (actor.type === "user") {
2878
+ if ("subject" in record && record.subject !== void 0) {
2879
+ return void 0;
2880
+ }
2881
+ return { actor };
2882
+ }
2883
+ if (!("subject" in record) || record.subject === void 0) {
2884
+ return { actor };
2885
+ }
2886
+ const subject = parseSubject(record.subject);
2887
+ if (!subject) {
2888
+ return void 0;
2889
+ }
2890
+ return {
2891
+ actor,
2892
+ subject
2893
+ };
2894
+ }
2895
+ function parseActor(value) {
2896
+ if (value && typeof value === "object") {
2897
+ const record = value;
2898
+ if (record.type === "user" && typeof record.userId === "string" && record.userId) {
2899
+ return { type: "user", userId: record.userId };
2900
+ }
2901
+ if (record.type === "system" && typeof record.id === "string" && record.id) {
2902
+ return { type: "system", id: record.id };
2903
+ }
2904
+ }
2905
+ return void 0;
2906
+ }
2907
+ function parseSubject(value) {
2908
+ if (value && typeof value === "object") {
2909
+ const record = value;
2910
+ if (record.type === "user" && typeof record.userId === "string" && record.userId && record.allowedWhen === "private-direct-conversation") {
2911
+ if (!record.binding || typeof record.binding !== "object") {
2912
+ return void 0;
2913
+ }
2914
+ const binding = record.binding;
2915
+ if (binding.type !== "slack-direct-conversation" || typeof binding.teamId !== "string" || !binding.teamId || typeof binding.channelId !== "string" || !binding.channelId || typeof binding.signature !== "string" || !binding.signature) {
2916
+ return void 0;
2917
+ }
2918
+ return {
2919
+ type: "user",
2920
+ userId: record.userId,
2921
+ allowedWhen: "private-direct-conversation",
2922
+ binding: {
2923
+ type: "slack-direct-conversation",
2924
+ teamId: binding.teamId,
2925
+ channelId: binding.channelId,
2926
+ signature: binding.signature
2927
+ }
2928
+ };
2929
+ }
2930
+ }
2931
+ return void 0;
2932
+ }
2933
+
2773
2934
  // src/chat/credentials/oauth-scope.ts
2774
2935
  function parseScope(scope) {
2775
2936
  if (!scope) {
@@ -2931,6 +3092,7 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
2931
3092
  async issue(input) {
2932
3093
  const envToken = process.env[authTokenEnv]?.trim();
2933
3094
  const oauth = manifest.oauth;
3095
+ const userSubjectId = credentialUserSubjectId(input.context);
2934
3096
  if (!oauth) {
2935
3097
  if (envToken) {
2936
3098
  return buildLease(envToken, Date.now() + MAX_LEASE_MS3, input.reason);
@@ -2940,11 +3102,8 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
2940
3102
  `No ${provider} credentials available.`
2941
3103
  );
2942
3104
  }
2943
- if (input.requesterId) {
2944
- const stored = await deps.userTokenStore.get(
2945
- input.requesterId,
2946
- provider
2947
- );
3105
+ if (userSubjectId) {
3106
+ const stored = await deps.userTokenStore.get(userSubjectId, provider);
2948
3107
  if (stored) {
2949
3108
  if (!hasRequiredOAuthScope(stored.scope, oauth.scope)) {
2950
3109
  throw new CredentialUnavailableError(
@@ -2966,11 +3125,7 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
2966
3125
  `Your ${provider} connection needs to be reauthorized.`
2967
3126
  );
2968
3127
  }
2969
- await deps.userTokenStore.set(
2970
- input.requesterId,
2971
- provider,
2972
- refreshed
2973
- );
3128
+ await deps.userTokenStore.set(userSubjectId, provider, refreshed);
2974
3129
  return buildLease(
2975
3130
  refreshed.accessToken,
2976
3131
  getLeaseExpiry(refreshed.expiresAt),
@@ -3462,6 +3617,7 @@ export {
3462
3617
  resolveAuthTokenPlaceholder,
3463
3618
  parsePluginManifest,
3464
3619
  CredentialUnavailableError,
3620
+ parseCredentialContext,
3465
3621
  hasRequiredOAuthScope,
3466
3622
  buildOAuthTokenRequest,
3467
3623
  parseOAuthTokenResponse,
@@ -0,0 +1,12 @@
1
+ // src/deployment.ts
2
+ var JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";
3
+ var JUNIOR_HEARTBEAT_CRON_SCHEDULE = "* * * * *";
4
+ var JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE = "/api/internal/agent/continue";
5
+ var LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION = "api/internal/agent/continue.ts";
6
+
7
+ export {
8
+ JUNIOR_HEARTBEAT_ROUTE,
9
+ JUNIOR_HEARTBEAT_CRON_SCHEDULE,
10
+ JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE,
11
+ LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
12
+ };
@@ -0,0 +1,213 @@
1
+ // src/plugins.ts
2
+ function cloneManifests(manifests) {
3
+ return manifests ? structuredClone(manifests) : void 0;
4
+ }
5
+ function cloneInlineManifests(registrations) {
6
+ const inlineManifests = registrations.flatMap(
7
+ (plugin) => plugin.manifest ? [
8
+ {
9
+ manifest: {
10
+ ...structuredClone(plugin.manifest),
11
+ capabilities: plugin.manifest.capabilities?.map(
12
+ (capability) => capability.includes(".") ? capability : `${plugin.manifest.name}.${capability}`
13
+ ) ?? [],
14
+ configKeys: plugin.manifest.configKeys?.map(
15
+ (key) => key.includes(".") ? key : `${plugin.manifest.name}.${key}`
16
+ ) ?? [],
17
+ ...plugin.manifest.target ? {
18
+ target: {
19
+ ...plugin.manifest.target,
20
+ configKey: plugin.manifest.target.configKey.includes(".") ? plugin.manifest.target.configKey : `${plugin.manifest.name}.${plugin.manifest.target.configKey}`
21
+ }
22
+ } : {}
23
+ },
24
+ ...plugin.packageName ? { packageName: plugin.packageName } : {}
25
+ }
26
+ ] : []
27
+ );
28
+ return inlineManifests.length > 0 ? inlineManifests : void 0;
29
+ }
30
+ function assertUniquePluginNames(registrations) {
31
+ const seen = /* @__PURE__ */ new Set();
32
+ for (const plugin of registrations) {
33
+ if (seen.has(plugin.name)) {
34
+ throw new Error(`Duplicate plugin registration name "${plugin.name}"`);
35
+ }
36
+ seen.add(plugin.name);
37
+ }
38
+ }
39
+ function assertUniquePackageNames(packageNames) {
40
+ const seen = /* @__PURE__ */ new Set();
41
+ for (const packageName of packageNames) {
42
+ if (seen.has(packageName)) {
43
+ throw new Error(`Duplicate plugin package name "${packageName}"`);
44
+ }
45
+ seen.add(packageName);
46
+ }
47
+ }
48
+ function normalizePluginInput(input) {
49
+ if (typeof input === "string") {
50
+ return { packageName: input };
51
+ }
52
+ return { registration: input };
53
+ }
54
+ function defineJuniorPlugins(inputs, options = {}) {
55
+ const normalized = inputs.map(normalizePluginInput);
56
+ const packageNames = normalized.flatMap(
57
+ (input) => input.packageName ? [input.packageName] : []
58
+ );
59
+ const registrations = normalized.flatMap(
60
+ (input) => input.registration ? [input.registration] : []
61
+ );
62
+ assertUniquePackageNames(packageNames);
63
+ assertUniquePluginNames(registrations);
64
+ const manifests = cloneManifests(options.manifests);
65
+ return {
66
+ packageNames,
67
+ registrations: registrations.map((plugin) => ({ ...plugin })),
68
+ ...manifests ? { manifests } : {}
69
+ };
70
+ }
71
+ function pluginCatalogConfigFromPluginSet(pluginSet) {
72
+ if (!pluginSet) {
73
+ return void 0;
74
+ }
75
+ const packages = [
76
+ .../* @__PURE__ */ new Set([
77
+ ...pluginSet.packageNames,
78
+ ...pluginSet.registrations.flatMap(
79
+ (plugin) => plugin.packageName ? [plugin.packageName] : []
80
+ )
81
+ ])
82
+ ];
83
+ const manifests = cloneManifests(pluginSet.manifests);
84
+ const inlineManifests = cloneInlineManifests(pluginSet.registrations);
85
+ if (packages.length === 0 && !manifests && !inlineManifests) {
86
+ return void 0;
87
+ }
88
+ return {
89
+ ...inlineManifests ? { inlineManifests } : {},
90
+ ...packages.length > 0 ? { packages } : {},
91
+ ...manifests ? { manifests } : {}
92
+ };
93
+ }
94
+ function trustedPluginRegistrationsFromPluginSet(pluginSet) {
95
+ return pluginSet?.registrations.filter(
96
+ (plugin) => plugin.hooks || plugin.legacyStatePrefixes
97
+ ) ?? [];
98
+ }
99
+
100
+ // src/chat/task-execution/vercel-queue.ts
101
+ import { QueueClient } from "@vercel/queue";
102
+
103
+ // src/chat/task-execution/queue-signing.ts
104
+ import { createHmac, timingSafeEqual } from "crypto";
105
+ var CONVERSATION_WORK_QUEUE_SIGNATURE_CONTEXT = "junior.conversation_work_queue.v1";
106
+ var CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION = "v1";
107
+ var CONVERSATION_WORK_QUEUE_SIGNATURE_MAX_SKEW_MS = 60 * 60 * 1e3;
108
+ function getConversationWorkQueueSecret() {
109
+ return process.env.JUNIOR_SECRET?.trim() || void 0;
110
+ }
111
+ function buildSignedPayload(message, signedAtMs) {
112
+ return [
113
+ CONVERSATION_WORK_QUEUE_SIGNATURE_CONTEXT,
114
+ signedAtMs,
115
+ message.conversationId
116
+ ].join(":");
117
+ }
118
+ function signPayload(message, signedAtMs, secret) {
119
+ return createHmac("sha256", secret).update(buildSignedPayload(message, signedAtMs)).digest("hex");
120
+ }
121
+ function timingSafeMatch(expected, actual) {
122
+ const expectedBuffer = Buffer.from(expected);
123
+ const actualBuffer = Buffer.from(actual);
124
+ if (expectedBuffer.length !== actualBuffer.length) {
125
+ return false;
126
+ }
127
+ return timingSafeEqual(expectedBuffer, actualBuffer);
128
+ }
129
+ function parseSignedConversationQueueMessage(value) {
130
+ if (!value || typeof value !== "object") {
131
+ return void 0;
132
+ }
133
+ const record = value;
134
+ if (typeof record.conversationId !== "string" || !record.conversationId.trim() || record.signatureVersion !== CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION || typeof record.signedAtMs !== "number" || !Number.isFinite(record.signedAtMs) || typeof record.signature !== "string" || !record.signature.trim()) {
135
+ return void 0;
136
+ }
137
+ return {
138
+ conversationId: record.conversationId,
139
+ signature: record.signature,
140
+ signatureVersion: CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION,
141
+ signedAtMs: record.signedAtMs
142
+ };
143
+ }
144
+ function signConversationQueueMessage(message, nowMs = Date.now()) {
145
+ const secret = getConversationWorkQueueSecret();
146
+ if (!secret) {
147
+ throw new Error(
148
+ "Cannot sign conversation queue message without JUNIOR_SECRET"
149
+ );
150
+ }
151
+ return {
152
+ ...message,
153
+ signedAtMs: nowMs,
154
+ signatureVersion: CONVERSATION_WORK_QUEUE_SIGNATURE_VERSION,
155
+ signature: signPayload(message, nowMs, secret)
156
+ };
157
+ }
158
+ function verifySignedConversationQueueMessage(value, nowMs = Date.now()) {
159
+ const message = parseSignedConversationQueueMessage(value);
160
+ const secret = getConversationWorkQueueSecret();
161
+ if (!message || !secret || !Number.isFinite(nowMs) || Math.abs(nowMs - message.signedAtMs) > CONVERSATION_WORK_QUEUE_SIGNATURE_MAX_SKEW_MS) {
162
+ return void 0;
163
+ }
164
+ const expected = signPayload(message, message.signedAtMs, secret);
165
+ if (!timingSafeMatch(expected, message.signature)) {
166
+ return void 0;
167
+ }
168
+ return { conversationId: message.conversationId };
169
+ }
170
+
171
+ // src/chat/task-execution/vercel-queue.ts
172
+ var DEFAULT_CONVERSATION_WORK_QUEUE_TOPIC = "junior_conversation_work";
173
+ var defaultQueue;
174
+ function getTopic(options) {
175
+ return options.topic || process.env.JUNIOR_CONVERSATION_WORK_QUEUE_TOPIC?.trim() || DEFAULT_CONVERSATION_WORK_QUEUE_TOPIC;
176
+ }
177
+ function toDelaySeconds(options) {
178
+ if (!options?.delayMs || options.delayMs <= 0) {
179
+ return void 0;
180
+ }
181
+ return Math.ceil(options.delayMs / 1e3);
182
+ }
183
+ function createVercelConversationWorkQueue(options = {}) {
184
+ const topic = getTopic(options);
185
+ const client = options.client ?? new QueueClient();
186
+ return {
187
+ async send(message, sendOptions) {
188
+ const result = await client.send(
189
+ topic,
190
+ signConversationQueueMessage(message),
191
+ {
192
+ idempotencyKey: sendOptions?.idempotencyKey,
193
+ delaySeconds: toDelaySeconds(sendOptions),
194
+ retentionSeconds: options.retentionSeconds
195
+ }
196
+ );
197
+ return result.messageId ? { messageId: result.messageId } : {};
198
+ }
199
+ };
200
+ }
201
+ function getVercelConversationWorkQueue() {
202
+ defaultQueue ??= createVercelConversationWorkQueue();
203
+ return defaultQueue;
204
+ }
205
+
206
+ export {
207
+ defineJuniorPlugins,
208
+ pluginCatalogConfigFromPluginSet,
209
+ trustedPluginRegistrationsFromPluginSet,
210
+ verifySignedConversationQueueMessage,
211
+ DEFAULT_CONVERSATION_WORK_QUEUE_TOPIC,
212
+ getVercelConversationWorkQueue
213
+ };
@@ -2,7 +2,7 @@ import {
2
2
  getPluginForSkillPath,
3
3
  getPluginSkillRoots,
4
4
  logWarn
5
- } from "./chunk-EDHJIYHS.js";
5
+ } from "./chunk-657CJCUO.js";
6
6
  import {
7
7
  skillRoots
8
8
  } from "./chunk-KVZL5NZS.js";
@@ -2,12 +2,12 @@ import {
2
2
  SANDBOX_WORKSPACE_ROOT,
3
3
  getStateAdapter,
4
4
  toOptionalTrimmed
5
- } from "./chunk-OMQ5X5QH.js";
5
+ } from "./chunk-ZOV3XJAH.js";
6
6
  import {
7
7
  getPluginRuntimeDependencies,
8
8
  getPluginRuntimePostinstall,
9
9
  withSpan
10
- } from "./chunk-EDHJIYHS.js";
10
+ } from "./chunk-657CJCUO.js";
11
11
 
12
12
  // src/chat/sandbox/runtime-dependency-snapshots.ts
13
13
  import { createHash } from "crypto";
@@ -2,13 +2,7 @@
2
2
  function juniorVercelConfig(options = {}) {
3
3
  const buildCommand = options.buildCommand === void 0 ? "pnpm build" : options.buildCommand;
4
4
  const config = {
5
- framework: "nitro",
6
- crons: [
7
- {
8
- path: "/api/internal/heartbeat",
9
- schedule: "* * * * *"
10
- }
11
- ]
5
+ framework: "nitro"
12
6
  };
13
7
  if (buildCommand !== null) {
14
8
  config.buildCommand = buildCommand;