@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.
- package/dist/app.d.ts +3 -0
- package/dist/app.js +3299 -1343
- package/dist/chat/agent-dispatch/heartbeat.d.ts +8 -0
- package/dist/chat/agent-dispatch/store.d.ts +2 -2
- package/dist/chat/agent-dispatch/types.d.ts +8 -22
- package/dist/chat/agent-dispatch/validation.d.ts +3 -1
- package/dist/chat/app/production.d.ts +11 -5
- package/dist/chat/capabilities/factory.d.ts +2 -1
- package/dist/chat/capabilities/router.d.ts +3 -2
- package/dist/chat/config.d.ts +5 -0
- package/dist/chat/credentials/broker.d.ts +2 -1
- package/dist/chat/credentials/context.d.ts +28 -0
- package/dist/chat/credentials/subject.d.ts +20 -0
- package/dist/chat/ingress/slack-webhook.d.ts +19 -0
- package/dist/chat/mcp/errors.d.ts +1 -1
- package/dist/chat/plugins/github-permissions.d.ts +11 -0
- package/dist/chat/plugins/types.d.ts +2 -0
- package/dist/chat/prompt.d.ts +2 -0
- package/dist/chat/respond-helpers.d.ts +4 -2
- package/dist/chat/respond.d.ts +23 -13
- package/dist/chat/runtime/processing-reaction.d.ts +1 -0
- package/dist/chat/runtime/reply-executor.d.ts +5 -0
- package/dist/chat/runtime/request-deadline.d.ts +8 -0
- package/dist/chat/runtime/slack-resume.d.ts +4 -5
- package/dist/chat/runtime/slack-runtime.d.ts +10 -0
- package/dist/chat/runtime/thread-context.d.ts +5 -2
- package/dist/chat/runtime/timeout-resume-runner.d.ts +19 -0
- package/dist/chat/runtime/turn-preparation.d.ts +1 -0
- package/dist/chat/runtime/turn.d.ts +13 -0
- package/dist/chat/sandbox/egress-policy.d.ts +1 -1
- package/dist/chat/sandbox/egress-session.d.ts +13 -12
- package/dist/chat/sandbox/sandbox.d.ts +2 -3
- package/dist/chat/services/timeout-resume.d.ts +9 -4
- package/dist/chat/services/turn-session-record.d.ts +19 -2
- package/dist/chat/slack/adapter-context.d.ts +25 -0
- package/dist/chat/slack/conversation-context.d.ts +28 -0
- package/dist/chat/state/turn-session.d.ts +1 -1
- package/dist/chat/task-execution/heartbeat.d.ts +13 -0
- package/dist/chat/task-execution/queue-signing.d.ts +12 -0
- package/dist/chat/task-execution/queue.d.ts +13 -0
- package/dist/chat/task-execution/slack-work.d.ts +32 -0
- package/dist/chat/task-execution/store.d.ts +153 -0
- package/dist/chat/task-execution/vercel-callback.d.ts +21 -0
- package/dist/chat/task-execution/vercel-queue.d.ts +16 -0
- package/dist/chat/task-execution/worker.d.ts +28 -0
- package/dist/{chunk-EDHJIYHS.js → chunk-657CJCUO.js} +218 -62
- package/dist/chunk-6YY4Q3D4.js +12 -0
- package/dist/chunk-75UZ4JLC.js +213 -0
- package/dist/{chunk-PYU2YB35.js → chunk-F57QSMOJ.js} +1 -1
- package/dist/{chunk-K3JNVV4Q.js → chunk-HRQQS63X.js} +2 -2
- package/dist/{chunk-SCQPBJAU.js → chunk-QUXPUKBH.js} +1 -7
- package/dist/{chunk-7KZXQNA6.js → chunk-X2HJLKQT.js} +368 -13
- package/dist/{chunk-OMQ5X5QH.js → chunk-ZOV3XJAH.js} +3 -2
- package/dist/cli/check.js +115 -2
- package/dist/cli/init.js +13 -1
- package/dist/cli/snapshot-warmup.js +3 -3
- package/dist/deployment.d.ts +4 -0
- package/dist/handlers/heartbeat.d.ts +5 -1
- package/dist/handlers/turn-resume.d.ts +3 -2
- package/dist/handlers/webhooks.d.ts +11 -9
- package/dist/nitro.d.ts +3 -1
- package/dist/nitro.js +48 -4
- package/dist/reporting.js +13 -16
- package/dist/vercel.d.ts +1 -1
- package/dist/vercel.js +1 -1
- package/package.json +5 -4
- package/dist/chat/services/turn-continuation-response.d.ts +0 -2
- package/dist/chat/slack/turn-continuation-notice.d.ts +0 -8
- 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-
|
|
11
|
+
} from "./chunk-ZOV3XJAH.js";
|
|
10
12
|
import {
|
|
11
13
|
isRecord,
|
|
12
14
|
logInfo,
|
|
13
15
|
logWarn
|
|
14
|
-
} from "./chunk-
|
|
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
|
|
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
|
|
1034
|
+
await client2.rPush(
|
|
1031
1035
|
listKey,
|
|
1032
1036
|
entries.map((entry) => JSON.stringify(entry))
|
|
1033
1037
|
);
|
|
1034
1038
|
}
|
|
1035
|
-
await
|
|
1039
|
+
await client2.pExpire(listKey, Math.max(1, ttlMs));
|
|
1036
1040
|
},
|
|
1037
1041
|
async read(scope) {
|
|
1038
|
-
const values = await
|
|
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
|
|
1642
|
-
const dsn =
|
|
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
|
|
1661
|
-
const dsn =
|
|
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-
|
|
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 =
|
|
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-
|
|
3
|
+
} from "../chunk-F57QSMOJ.js";
|
|
4
4
|
import {
|
|
5
5
|
parsePluginManifest
|
|
6
|
-
} from "../chunk-
|
|
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-
|
|
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-
|
|
3
|
+
} from "../chunk-HRQQS63X.js";
|
|
4
4
|
import {
|
|
5
5
|
disconnectStateAdapter
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-ZOV3XJAH.js";
|
|
7
7
|
import {
|
|
8
8
|
getPluginProviders,
|
|
9
9
|
getPluginRuntimeDependencies,
|
|
10
10
|
getPluginRuntimePostinstall
|
|
11
|
-
} from "../chunk-
|
|
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>;
|