@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
@@ -4,14 +4,16 @@ import {
4
4
  botConfig,
5
5
  getChatConfig,
6
6
  getConnectedStateContext,
7
+ getSlackBotToken,
7
8
  getStateAdapter,
9
+ parseSlackThreadId,
8
10
  sandboxSkillDir
9
- } from "./chunk-OMQ5X5QH.js";
11
+ } from "./chunk-ZOV3XJAH.js";
10
12
  import {
11
13
  isRecord,
12
14
  logInfo,
13
15
  logWarn
14
- } from "./chunk-EDHJIYHS.js";
16
+ } from "./chunk-657CJCUO.js";
15
17
  import {
16
18
  sentry_exports
17
19
  } from "./chunk-Z3YD6NHK.js";
@@ -656,7 +658,9 @@ function buildWorldSection() {
656
658
  }
657
659
  function buildRuntimeSection(params) {
658
660
  const lines = [
659
- params.conversationId ? `- gen_ai.conversation.id: ${escapeXml(params.conversationId)}` : ""
661
+ params.conversationId ? `- gen_ai.conversation.id: ${escapeXml(params.conversationId)}` : "",
662
+ params.slackConversation?.type ? `- slack.conversation.type: ${escapeXml(params.slackConversation.type)}` : "",
663
+ params.slackConversation?.name ? `- slack.conversation.name: ${escapeXml(params.slackConversation.name)}` : ""
660
664
  ].filter(Boolean);
661
665
  if (lines.length === 0) {
662
666
  return null;
@@ -1022,20 +1026,20 @@ function commitEntries(existingMessages, nextMessages, sessionId, entries) {
1022
1026
  return [resetEntry(nextMessages, nextSessionId(entries))];
1023
1027
  }
1024
1028
  function redisStore(redisStateAdapter) {
1025
- const client = redisStateAdapter.getClient();
1029
+ const client2 = redisStateAdapter.getClient();
1026
1030
  return {
1027
1031
  async append({ entries, scope, ttlMs }) {
1028
1032
  const listKey = key(scope);
1029
1033
  if (entries.length > 0) {
1030
- await client.rPush(
1034
+ await client2.rPush(
1031
1035
  listKey,
1032
1036
  entries.map((entry) => JSON.stringify(entry))
1033
1037
  );
1034
1038
  }
1035
- await client.pExpire(listKey, Math.max(1, ttlMs));
1039
+ await client2.pExpire(listKey, Math.max(1, ttlMs));
1036
1040
  },
1037
1041
  async read(scope) {
1038
- const values = await client.lRange(key(scope), 0, -1);
1042
+ const values = await client2.lRange(key(scope), 0, -1);
1039
1043
  return values.map(decode);
1040
1044
  }
1041
1045
  };
@@ -1280,7 +1284,7 @@ function parseAgentTurnSessionFields(parsed) {
1280
1284
  (value) => typeof value === "string"
1281
1285
  )
1282
1286
  } : {},
1283
- ...parsed.resumeReason === "timeout" || parsed.resumeReason === "auth" ? { resumeReason: parsed.resumeReason } : {},
1287
+ ...parsed.resumeReason === "timeout" || parsed.resumeReason === "auth" || parsed.resumeReason === "yield" ? { resumeReason: parsed.resumeReason } : {},
1284
1288
  ...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {},
1285
1289
  ...typeof parsed.resumedFromSliceId === "number" ? { resumedFromSliceId: parsed.resumedFromSliceId } : {},
1286
1290
  ...typeof parsed.traceId === "string" ? { traceId: parsed.traceId } : {}
@@ -1638,8 +1642,8 @@ function buildSentryWebBaseUrl(dsn) {
1638
1642
  return `${dsn.protocol}://${dsn.host}${port}${path2}`;
1639
1643
  }
1640
1644
  function buildSentryConversationUrl(conversationId) {
1641
- const client = sentry_exports.getClient();
1642
- const dsn = client?.getDsn();
1645
+ const client2 = sentry_exports.getClient();
1646
+ const dsn = client2?.getDsn();
1643
1647
  if (!dsn?.host || !dsn.projectId) {
1644
1648
  return void 0;
1645
1649
  }
@@ -1657,8 +1661,8 @@ function buildSentryConversationUrl(conversationId) {
1657
1661
  return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${path2}`;
1658
1662
  }
1659
1663
  function buildSentryTraceUrl(traceId) {
1660
- const client = sentry_exports.getClient();
1661
- const dsn = client?.getDsn();
1664
+ const client2 = sentry_exports.getClient();
1665
+ const dsn = client2?.getDsn();
1662
1666
  if (!dsn?.host || !dsn.projectId) {
1663
1667
  return void 0;
1664
1668
  }
@@ -1676,7 +1680,354 @@ function buildSentryTraceUrl(traceId) {
1676
1680
  return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${path2}`;
1677
1681
  }
1678
1682
 
1683
+ // src/chat/slack/client.ts
1684
+ import { WebClient } from "@slack/web-api";
1685
+ var SlackActionError = class extends Error {
1686
+ code;
1687
+ apiError;
1688
+ needed;
1689
+ provided;
1690
+ statusCode;
1691
+ requestId;
1692
+ errorData;
1693
+ retryAfterSeconds;
1694
+ detail;
1695
+ detailLine;
1696
+ detailRule;
1697
+ constructor(message, code, options = {}) {
1698
+ super(message);
1699
+ this.name = "SlackActionError";
1700
+ this.code = code;
1701
+ this.apiError = options.apiError;
1702
+ this.needed = options.needed;
1703
+ this.provided = options.provided;
1704
+ this.statusCode = options.statusCode;
1705
+ this.requestId = options.requestId;
1706
+ this.errorData = options.errorData;
1707
+ this.retryAfterSeconds = options.retryAfterSeconds;
1708
+ this.detail = options.detail;
1709
+ this.detailLine = options.detailLine;
1710
+ this.detailRule = options.detailRule;
1711
+ }
1712
+ };
1713
+ function serializeSlackErrorData(data) {
1714
+ if (!data || typeof data !== "object") {
1715
+ return void 0;
1716
+ }
1717
+ const filtered = Object.fromEntries(
1718
+ Object.entries(data).filter(
1719
+ ([key2]) => key2 !== "error"
1720
+ )
1721
+ );
1722
+ if (Object.keys(filtered).length === 0) {
1723
+ return void 0;
1724
+ }
1725
+ try {
1726
+ const serialized = JSON.stringify(filtered);
1727
+ return serialized.length <= 600 ? serialized : `${serialized.slice(0, 597)}...`;
1728
+ } catch {
1729
+ return void 0;
1730
+ }
1731
+ }
1732
+ function getHeaderString(headers, name) {
1733
+ if (!headers || typeof headers !== "object") {
1734
+ return void 0;
1735
+ }
1736
+ const key2 = name.toLowerCase();
1737
+ const entries = headers;
1738
+ for (const [entryKey, value] of Object.entries(entries)) {
1739
+ if (entryKey.toLowerCase() !== key2) continue;
1740
+ if (typeof value === "string") return value;
1741
+ if (Array.isArray(value)) {
1742
+ const first = value.find((entry) => typeof entry === "string");
1743
+ return typeof first === "string" ? first : void 0;
1744
+ }
1745
+ }
1746
+ return void 0;
1747
+ }
1748
+ function parseSlackCanvasDetail(detail) {
1749
+ if (typeof detail !== "string") {
1750
+ return {};
1751
+ }
1752
+ const trimmed = detail.trim();
1753
+ if (!trimmed) {
1754
+ return {};
1755
+ }
1756
+ const parsed = {
1757
+ detail: trimmed
1758
+ };
1759
+ const lineMatch = trimmed.match(/line\s+(\d+):/i);
1760
+ if (lineMatch) {
1761
+ const line = Number.parseInt(lineMatch[1] ?? "", 10);
1762
+ if (Number.isFinite(line)) {
1763
+ parsed.detailLine = line;
1764
+ }
1765
+ }
1766
+ if (/unsupported heading depth/i.test(trimmed)) {
1767
+ parsed.detailRule = "unsupported_heading_depth";
1768
+ }
1769
+ return parsed;
1770
+ }
1771
+ var client = null;
1772
+ function normalizeSlackConversationId(channelId) {
1773
+ if (!channelId) return void 0;
1774
+ const trimmed = channelId.trim();
1775
+ if (!trimmed) return void 0;
1776
+ if (!trimmed.startsWith("slack:")) {
1777
+ return trimmed;
1778
+ }
1779
+ const parts = trimmed.split(":");
1780
+ return parts[1]?.trim() || void 0;
1781
+ }
1782
+ function getClient2() {
1783
+ if (client) return client;
1784
+ const token = getSlackBotToken();
1785
+ if (!token) {
1786
+ throw new SlackActionError(
1787
+ "SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack canvas/list actions in this service",
1788
+ "missing_token"
1789
+ );
1790
+ }
1791
+ client = new WebClient(token);
1792
+ return client;
1793
+ }
1794
+ function mapSlackError(error) {
1795
+ if (error instanceof SlackActionError) {
1796
+ return error;
1797
+ }
1798
+ const candidate = error;
1799
+ const apiError = candidate.data?.error;
1800
+ const message = candidate.message ?? "Slack action failed";
1801
+ const baseOptions = {
1802
+ apiError,
1803
+ statusCode: candidate.statusCode,
1804
+ requestId: getHeaderString(candidate.headers, "x-slack-req-id"),
1805
+ errorData: serializeSlackErrorData(candidate.data),
1806
+ ...parseSlackCanvasDetail(candidate.data?.detail)
1807
+ };
1808
+ if (apiError === "missing_scope") {
1809
+ return new SlackActionError(message, "missing_scope", {
1810
+ ...baseOptions,
1811
+ needed: candidate.data?.needed,
1812
+ provided: candidate.data?.provided
1813
+ });
1814
+ }
1815
+ if (apiError === "not_in_channel") {
1816
+ return new SlackActionError(message, "not_in_channel", baseOptions);
1817
+ }
1818
+ if (apiError === "already_reacted") {
1819
+ return new SlackActionError(message, "already_reacted", baseOptions);
1820
+ }
1821
+ if (apiError === "no_reaction") {
1822
+ return new SlackActionError(message, "no_reaction", baseOptions);
1823
+ }
1824
+ if (apiError === "invalid_arguments") {
1825
+ return new SlackActionError(message, "invalid_arguments", baseOptions);
1826
+ }
1827
+ if (apiError === "invalid_cursor") {
1828
+ return new SlackActionError(message, "invalid_arguments", baseOptions);
1829
+ }
1830
+ if (apiError === "invalid_name") {
1831
+ return new SlackActionError(message, "invalid_arguments", baseOptions);
1832
+ }
1833
+ if (apiError === "not_found" || apiError === "channel_not_found" || apiError === "message_not_found") {
1834
+ return new SlackActionError(message, "not_found", baseOptions);
1835
+ }
1836
+ if (apiError === "feature_not_enabled" || apiError === "not_allowed_token_type") {
1837
+ return new SlackActionError(message, "feature_unavailable", baseOptions);
1838
+ }
1839
+ if (apiError === "canvas_creation_failed") {
1840
+ return new SlackActionError(message, "canvas_creation_failed", baseOptions);
1841
+ }
1842
+ if (apiError === "canvas_editing_failed") {
1843
+ return new SlackActionError(message, "canvas_editing_failed", baseOptions);
1844
+ }
1845
+ if (candidate.code === "slack_webapi_rate_limited_error" || candidate.statusCode === 429) {
1846
+ return new SlackActionError(message, "rate_limited", {
1847
+ ...baseOptions,
1848
+ retryAfterSeconds: candidate.retryAfter
1849
+ });
1850
+ }
1851
+ return new SlackActionError(message, "internal_error", baseOptions);
1852
+ }
1853
+ function sleep(ms) {
1854
+ return new Promise((resolve) => setTimeout(resolve, ms));
1855
+ }
1856
+ async function withSlackRetries(task, maxAttempts = 3, context = {}) {
1857
+ let attempt = 0;
1858
+ while (attempt < maxAttempts) {
1859
+ attempt += 1;
1860
+ try {
1861
+ return await task();
1862
+ } catch (error) {
1863
+ const mapped = mapSlackError(error);
1864
+ const isRetryable = mapped.code === "rate_limited";
1865
+ const baseLogAttributes = {
1866
+ "app.slack.action": context.action ?? "unknown",
1867
+ "app.slack.error_code": mapped.code,
1868
+ ...mapped.apiError ? { "app.slack.api_error": mapped.apiError } : {},
1869
+ ...mapped.detail ? { "app.slack.detail": mapped.detail } : {},
1870
+ ...mapped.detailLine !== void 0 ? { "app.slack.detail_line": mapped.detailLine } : {},
1871
+ ...mapped.detailRule ? { "app.slack.detail_rule": mapped.detailRule } : {},
1872
+ ...mapped.requestId ? { "app.slack.request_id": mapped.requestId } : {},
1873
+ ...mapped.statusCode !== void 0 ? { "http.response.status_code": mapped.statusCode } : {},
1874
+ ...context.attributes ?? {}
1875
+ };
1876
+ if (!isRetryable || attempt >= maxAttempts) {
1877
+ logWarn(
1878
+ "slack_action_failed",
1879
+ {},
1880
+ {
1881
+ ...baseLogAttributes,
1882
+ ...mapped.errorData ? { "app.slack.error_data": mapped.errorData } : {}
1883
+ },
1884
+ "Slack action failed"
1885
+ );
1886
+ throw mapped;
1887
+ }
1888
+ logWarn(
1889
+ "slack_action_retrying",
1890
+ {},
1891
+ {
1892
+ ...baseLogAttributes,
1893
+ "app.slack.retry_attempt": attempt
1894
+ },
1895
+ "Retrying Slack action after transient failure"
1896
+ );
1897
+ const retryAfterMs = mapped.code === "rate_limited" && mapped.retryAfterSeconds && mapped.retryAfterSeconds > 0 ? mapped.retryAfterSeconds * 1e3 : void 0;
1898
+ const backoffMs = Math.min(2e3, 250 * 2 ** (attempt - 1));
1899
+ await sleep(retryAfterMs ?? backoffMs);
1900
+ }
1901
+ }
1902
+ throw new SlackActionError(
1903
+ "Slack action exhausted retries",
1904
+ "internal_error"
1905
+ );
1906
+ }
1907
+ function getSlackClient() {
1908
+ return getClient2();
1909
+ }
1910
+ function isDmChannel(channelId) {
1911
+ const normalized = normalizeSlackConversationId(channelId);
1912
+ return Boolean(normalized && normalized.startsWith("D"));
1913
+ }
1914
+ function isConversationScopedChannel(channelId) {
1915
+ const normalized = normalizeSlackConversationId(channelId);
1916
+ if (!normalized) return false;
1917
+ return normalized.startsWith("C") || normalized.startsWith("G") || normalized.startsWith("D");
1918
+ }
1919
+ function isConversationChannel(channelId) {
1920
+ const normalized = normalizeSlackConversationId(channelId);
1921
+ if (!normalized) return false;
1922
+ return normalized.startsWith("C") || normalized.startsWith("G");
1923
+ }
1924
+ async function getFilePermalink(fileId) {
1925
+ const client2 = getClient2();
1926
+ const response = await withSlackRetries(
1927
+ () => client2.files.info({
1928
+ file: fileId
1929
+ })
1930
+ );
1931
+ return response.file?.permalink;
1932
+ }
1933
+ async function downloadPrivateSlackFile(url) {
1934
+ const token = getSlackBotToken();
1935
+ if (!token) {
1936
+ throw new SlackActionError(
1937
+ "SLACK_BOT_TOKEN (or SLACK_BOT_USER_TOKEN) is required for Slack file downloads in this service",
1938
+ "missing_token"
1939
+ );
1940
+ }
1941
+ const response = await fetch(url, {
1942
+ headers: {
1943
+ Authorization: `Bearer ${token}`
1944
+ }
1945
+ });
1946
+ if (!response.ok) {
1947
+ throw new Error(`Slack file download failed: ${response.status}`);
1948
+ }
1949
+ return Buffer.from(await response.arrayBuffer());
1950
+ }
1951
+
1952
+ // src/chat/slack/conversation-context.ts
1953
+ function normalizeConversationName(type, channelName) {
1954
+ const trimmed = channelName?.trim();
1955
+ if (!trimmed) return void 0;
1956
+ if (type === "public_channel" || type === "private_channel" || type === "private_channel_or_group_dm") {
1957
+ return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
1958
+ }
1959
+ return trimmed;
1960
+ }
1961
+ function typeFromSlackChannelType(channelType) {
1962
+ if (channelType === "channel") return "public_channel";
1963
+ if (channelType === "group") return "private_channel";
1964
+ if (channelType === "mpim") return "group_dm";
1965
+ if (channelType === "im") return "direct_message";
1966
+ return void 0;
1967
+ }
1968
+ function typeFromChannelId(channelId, channelName) {
1969
+ const normalized = normalizeSlackConversationId(channelId);
1970
+ if (!normalized) return void 0;
1971
+ if (normalized.startsWith("C")) return "public_channel";
1972
+ if (normalized.startsWith("D")) return "direct_message";
1973
+ if (normalized.startsWith("G")) {
1974
+ return channelName?.trim().startsWith("mpdm-") ? "group_dm" : "private_channel_or_group_dm";
1975
+ }
1976
+ return void 0;
1977
+ }
1978
+ function toSlackEventChannelType(channelType) {
1979
+ if (channelType === "channel" || channelType === "group" || channelType === "mpim" || channelType === "im") {
1980
+ return channelType;
1981
+ }
1982
+ return void 0;
1983
+ }
1984
+ function resolveSlackChannelTypeFromMessage(message) {
1985
+ const raw = message.raw;
1986
+ if (!raw || typeof raw !== "object") {
1987
+ return void 0;
1988
+ }
1989
+ const channelType = raw.channel_type;
1990
+ return typeof channelType === "string" ? toSlackEventChannelType(channelType.trim()) : void 0;
1991
+ }
1992
+ function resolveSlackConversationContext(input) {
1993
+ const type = typeFromSlackChannelType(input.channelType) ?? typeFromChannelId(input.channelId, input.channelName);
1994
+ if (!type) return void 0;
1995
+ const name = normalizeConversationName(type, input.channelName);
1996
+ return {
1997
+ type,
1998
+ ...name ? { name } : {}
1999
+ };
2000
+ }
2001
+ function resolveSlackConversationContextFromThreadId(input) {
2002
+ const slackThread = parseSlackThreadId(input.threadId);
2003
+ return resolveSlackConversationContext({
2004
+ channelId: slackThread?.channelId,
2005
+ channelName: input.channelName
2006
+ });
2007
+ }
2008
+ function formatSlackConversationTypeLabel(type) {
2009
+ if (type === "public_channel") return "Public Channel";
2010
+ if (type === "private_channel") return "Private Channel";
2011
+ if (type === "group_dm") return "Group DM";
2012
+ if (type === "direct_message") return "Direct Message";
2013
+ return "Private Channel or Group DM";
2014
+ }
2015
+ function formatSlackConversationRedactedLabel(context) {
2016
+ if (!context) return void 0;
2017
+ return formatSlackConversationTypeLabel(context.type);
2018
+ }
2019
+
1679
2020
  export {
2021
+ SlackActionError,
2022
+ getHeaderString,
2023
+ normalizeSlackConversationId,
2024
+ withSlackRetries,
2025
+ getSlackClient,
2026
+ isDmChannel,
2027
+ isConversationScopedChannel,
2028
+ isConversationChannel,
2029
+ getFilePermalink,
2030
+ downloadPrivateSlackFile,
1680
2031
  GET,
1681
2032
  TURN_CONTEXT_TAG,
1682
2033
  getInterruptionMarker,
@@ -1702,5 +2053,9 @@ export {
1702
2053
  abandonAgentTurnSessionRecord,
1703
2054
  failAgentTurnSessionRecord,
1704
2055
  buildSentryConversationUrl,
1705
- buildSentryTraceUrl
2056
+ buildSentryTraceUrl,
2057
+ resolveSlackChannelTypeFromMessage,
2058
+ resolveSlackConversationContext,
2059
+ resolveSlackConversationContextFromThreadId,
2060
+ formatSlackConversationRedactedLabel
1706
2061
  };
@@ -6,7 +6,7 @@ import {
6
6
  setSpanAttributes,
7
7
  toOptionalString,
8
8
  withSpan
9
- } from "./chunk-EDHJIYHS.js";
9
+ } from "./chunk-657CJCUO.js";
10
10
 
11
11
  // src/chat/slack/context.ts
12
12
  function toTrimmedSlackString(value) {
@@ -499,7 +499,7 @@ async function completeObject(params) {
499
499
  // src/chat/config.ts
500
500
  var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
501
501
  var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
502
- var DEFAULT_FUNCTION_MAX_DURATION_SECONDS = 800;
502
+ var DEFAULT_FUNCTION_MAX_DURATION_SECONDS = 300;
503
503
  var ADVISOR_THINKING_LEVELS = [
504
504
  "minimal",
505
505
  "low",
@@ -938,6 +938,7 @@ export {
938
938
  resolveGatewayModel,
939
939
  completeText,
940
940
  completeObject,
941
+ FUNCTION_TIMEOUT_BUFFER_SECONDS,
941
942
  getChatConfig,
942
943
  botConfig,
943
944
  getSlackBotToken,
package/dist/cli/check.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import {
2
2
  parseSkillFile
3
- } from "../chunk-PYU2YB35.js";
3
+ } from "../chunk-F57QSMOJ.js";
4
4
  import {
5
5
  parsePluginManifest
6
- } from "../chunk-EDHJIYHS.js";
6
+ } from "../chunk-657CJCUO.js";
7
7
  import "../chunk-Z3YD6NHK.js";
8
+ import {
9
+ JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE,
10
+ JUNIOR_HEARTBEAT_ROUTE,
11
+ LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
12
+ } from "../chunk-6YY4Q3D4.js";
8
13
  import "../chunk-KVZL5NZS.js";
9
14
  import "../chunk-2KG3PWR4.js";
10
15
 
@@ -65,6 +70,13 @@ async function readJsonFile(targetPath) {
65
70
  return void 0;
66
71
  }
67
72
  }
73
+ async function readTextFile(targetPath) {
74
+ try {
75
+ return await fs.readFile(targetPath, "utf8");
76
+ } catch {
77
+ return void 0;
78
+ }
79
+ }
68
80
  async function readRootPackageJson(rootDir) {
69
81
  return await readJsonFile(path.join(rootDir, "package.json"));
70
82
  }
@@ -383,6 +395,94 @@ function reportSkillGroup(label, skillResults, io) {
383
395
  function reportAppSkills(skillResults, io) {
384
396
  reportSkillGroup("app skills", skillResults, io);
385
397
  }
398
+ function isRecord(value) {
399
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
400
+ }
401
+ async function hasJuniorDeploymentMarkers(rootDir, packages, nitroConfig) {
402
+ if (packages.some(
403
+ (declaredPackage) => declaredPackage.name === "@sentry/junior"
404
+ )) {
405
+ return true;
406
+ }
407
+ for (const source of [
408
+ nitroConfig,
409
+ await readTextFile(path.join(rootDir, "server.ts")),
410
+ await readTextFile(path.join(rootDir, "server.js"))
411
+ ]) {
412
+ if (source?.includes("@sentry/junior") || /\bjuniorNitro\s*\(/.test(source ?? "")) {
413
+ return true;
414
+ }
415
+ }
416
+ const appDir = path.resolve(rootDir, "app");
417
+ return await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
418
+ }
419
+ async function validateDeploymentConfig(rootDir, packages) {
420
+ const errors = [];
421
+ const warnings = [];
422
+ let checked = false;
423
+ const nitroConfigPath = path.join(rootDir, "nitro.config.ts");
424
+ const nitroConfig = await readTextFile(nitroConfigPath);
425
+ const hasJuniorMarkers = await hasJuniorDeploymentMarkers(
426
+ rootDir,
427
+ packages,
428
+ nitroConfig
429
+ );
430
+ if (nitroConfig !== void 0 && hasJuniorMarkers) {
431
+ checked = true;
432
+ if (!/\bjuniorNitro\s*\(/.test(nitroConfig)) {
433
+ errors.push(
434
+ `${nitroConfigPath}: missing juniorNitro(). The Nitro module emits Junior's Vercel queue trigger and heartbeat cron into the deployed build output.`
435
+ );
436
+ }
437
+ }
438
+ const vercelConfigPath = path.join(rootDir, "vercel.json");
439
+ const vercelConfigText = await readTextFile(vercelConfigPath);
440
+ if (vercelConfigText === void 0) {
441
+ return { checked, errors, warnings };
442
+ }
443
+ const containsLegacyJuniorQueuePath = vercelConfigText.includes(
444
+ LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
445
+ );
446
+ if (!hasJuniorMarkers && !containsLegacyJuniorQueuePath) {
447
+ return { checked, errors, warnings };
448
+ }
449
+ let vercelConfig;
450
+ try {
451
+ vercelConfig = JSON.parse(vercelConfigText);
452
+ } catch (error) {
453
+ errors.push(
454
+ `${vercelConfigPath}: invalid JSON (${error instanceof Error ? error.message : String(error)})`
455
+ );
456
+ return { checked, errors, warnings };
457
+ }
458
+ if (hasJuniorMarkers && vercelConfig.framework !== void 0 && vercelConfig.framework !== "nitro") {
459
+ checked = true;
460
+ errors.push(
461
+ `${vercelConfigPath}: framework must be "nitro" for Junior's Vercel deployment wiring.`
462
+ );
463
+ }
464
+ if (isRecord(vercelConfig.functions)) {
465
+ const legacyFunctionConfig = vercelConfig.functions[LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION];
466
+ if (legacyFunctionConfig !== void 0) {
467
+ checked = true;
468
+ errors.push(
469
+ `${vercelConfigPath}: functions.${LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION} targets a source file that Nitro does not deploy. Remove it; juniorNitro() emits the ${JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE} queue trigger through Nitro functionRules.`
470
+ );
471
+ }
472
+ }
473
+ if (hasJuniorMarkers && Array.isArray(vercelConfig.crons)) {
474
+ const hasHeartbeatCron = vercelConfig.crons.some(
475
+ (cron) => isRecord(cron) && cron.path === JUNIOR_HEARTBEAT_ROUTE
476
+ );
477
+ if (hasHeartbeatCron) {
478
+ checked = true;
479
+ warnings.push(
480
+ `${vercelConfigPath}: ${JUNIOR_HEARTBEAT_ROUTE} cron is now emitted by juniorNitro() into Nitro's Vercel Build Output config. Remove the root cron entry to avoid deployment drift.`
481
+ );
482
+ }
483
+ }
484
+ return { checked, errors, warnings };
485
+ }
386
486
  async function validateAppSourceFiles(rootDir, registeredConfigKeys) {
387
487
  const errors = [];
388
488
  const warnings = [];
@@ -524,6 +624,12 @@ async function runCheck(rootDir = process.cwd(), io = DEFAULT_IO) {
524
624
  const pluginResults = [];
525
625
  const skillResultsByDir = /* @__PURE__ */ new Map();
526
626
  warnings.push(...packageWarnings);
627
+ const deploymentConfigResult = await validateDeploymentConfig(
628
+ resolvedRoot,
629
+ packages
630
+ );
631
+ warnings.push(...deploymentConfigResult.warnings);
632
+ errors.push(...deploymentConfigResult.errors);
527
633
  for (const { pluginDir, packageName } of pluginDirs) {
528
634
  const pluginNameMap = packageName ? duplicatePackagedPluginNames : duplicatePluginNames;
529
635
  const providerDomainMap = packageName ? duplicatePackagedProviderDomains : duplicateProviderDomains;
@@ -604,6 +710,13 @@ async function runCheck(rootDir = process.cwd(), io = DEFAULT_IO) {
604
710
  );
605
711
  io.info(formatHeading(appFileStatus, "app files"));
606
712
  }
713
+ if (deploymentConfigResult.checked) {
714
+ const deploymentConfigStatus = formatStatus(
715
+ deploymentConfigResult.errors.length,
716
+ deploymentConfigResult.warnings.length
717
+ );
718
+ io.info(formatHeading(deploymentConfigStatus, "deployment config"));
719
+ }
607
720
  for (const pluginResult of pluginResults) {
608
721
  reportPluginResult(pluginResult, io);
609
722
  }
package/dist/cli/init.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  juniorVercelConfig
3
- } from "../chunk-SCQPBJAU.js";
3
+ } from "../chunk-QUXPUKBH.js";
4
4
  import "../chunk-2KG3PWR4.js";
5
5
 
6
6
  // src/cli/init.ts
@@ -20,6 +20,17 @@ export default app;
20
20
  `
21
21
  );
22
22
  }
23
+ function writeQueueConsumerEntry(targetDir) {
24
+ const queueConsumerDir = path.join(targetDir, "api", "internal", "agent");
25
+ fs.mkdirSync(queueConsumerDir, { recursive: true });
26
+ fs.writeFileSync(
27
+ path.join(queueConsumerDir, "continue.ts"),
28
+ `import app from "../../../server.ts";
29
+
30
+ export const POST = (request: Request) => app.fetch(request);
31
+ `
32
+ );
33
+ }
23
34
  function writeNitroConfig(targetDir) {
24
35
  fs.writeFileSync(
25
36
  path.join(targetDir, "nitro.config.ts"),
@@ -186,6 +197,7 @@ SENTRY_ORG_SLUG=
186
197
  `
187
198
  );
188
199
  writeServerEntry(target);
200
+ writeQueueConsumerEntry(target);
189
201
  writeNitroConfig(target);
190
202
  writeViteConfig(target);
191
203
  writeVercelJson(target);
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  resolveRuntimeDependencySnapshot
3
- } from "../chunk-K3JNVV4Q.js";
3
+ } from "../chunk-HRQQS63X.js";
4
4
  import {
5
5
  disconnectStateAdapter
6
- } from "../chunk-OMQ5X5QH.js";
6
+ } from "../chunk-ZOV3XJAH.js";
7
7
  import {
8
8
  getPluginProviders,
9
9
  getPluginRuntimeDependencies,
10
10
  getPluginRuntimePostinstall
11
- } from "../chunk-EDHJIYHS.js";
11
+ } from "../chunk-657CJCUO.js";
12
12
  import "../chunk-Z3YD6NHK.js";
13
13
  import "../chunk-KVZL5NZS.js";
14
14
  import "../chunk-2KG3PWR4.js";
@@ -0,0 +1,4 @@
1
+ export declare const JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";
2
+ export declare const JUNIOR_HEARTBEAT_CRON_SCHEDULE = "* * * * *";
3
+ export declare const JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE = "/api/internal/agent/continue";
4
+ export declare const LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION = "api/internal/agent/continue.ts";
@@ -1,3 +1,7 @@
1
+ import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
1
2
  import type { WaitUntilFn } from "@/handlers/types";
3
+ export interface HeartbeatHandlerOptions {
4
+ conversationWorkQueue?: ConversationWorkQueue;
5
+ }
2
6
  /** Handle the authenticated internal heartbeat. */
3
- export declare function GET(request: Request, waitUntil: WaitUntilFn): Promise<Response>;
7
+ export declare function GET(request: Request, waitUntil: WaitUntilFn, options?: HeartbeatHandlerOptions): Promise<Response>;