@sentry/junior 0.263.0 → 0.265.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{adapter-BIacI0Bt.js → adapter-cUkPH3Om.js} +1 -1
- package/dist/{admin-C7vUk7s9.js → admin-Cf0rDmWV.js} +1 -1
- package/dist/{agent-runner-BUBas7Yy.js → agent-runner-C6MwHJEs.js} +296 -238
- package/dist/api/schema.d.ts +213 -16
- package/dist/api/schema.js +3 -3
- package/dist/api.js +107 -40
- package/dist/{app-Bo0kB987.d.ts → app-Csbi9dW7.d.ts} +4 -3
- package/dist/app.d.ts +1 -1
- package/dist/app.js +101 -393
- package/dist/{assistant-reply-BFmjgJjD.js → assistant-reply-UPA8vKlC.js} +1 -1
- package/dist/{automation-CWX0qL8U.js → automation-2RbKzpMT.js} +30 -9
- package/dist/{briefs-h55v5lX9.js → briefs-DRjx8kQ9.js} +3 -3
- package/dist/{catalog-runtime-Suf-aLlh.js → catalog-runtime-D3qbJlYz.js} +1 -1
- package/dist/cli/chat.js +15 -15
- package/dist/cli/check.js +3 -3
- package/dist/cli/init.js +4 -18
- package/dist/cli/main.js +4 -4
- package/dist/cli/plugins.js +4 -4
- package/dist/cli/snapshot-create.js +4 -4
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-DZBCBj1M.js → client-DX34VNtH.js} +3 -8
- package/dist/{config-Dnv8KmNs.js → config-BayQ9hHH.js} +100 -3
- package/dist/{connections-Br0_xv27.js → connections-mpN5Lsnj.js} +11 -10
- package/dist/{conversation-privacy-FY8yc2q3.js → conversation-privacy-frjQCJa9.js} +1 -1
- package/dist/{credential-sync-BvkiyWF5.js → credential-sync-CZmyDNdR.js} +20 -20
- package/dist/{agent-hooks-CqnZfuCt.js → credentialed-C6pKlB1p.js} +3197 -66
- package/dist/{executor-J9k2sp9m.js → executor-CAr7xQr2.js} +10 -300
- package/dist/{images-DV2FlEGL.js → images-B2yA3HWE.js} +2 -2
- package/dist/instrumentation.js +41 -25
- package/dist/{job-queue-B4yTgklN.js → job-queue-Cjo6Yg_b.js} +53 -14
- package/dist/{logging-Dk_FpF36.js → logging-D2QBriUo.js} +19 -32
- package/dist/{message-BzseAFH1.js → message-C9ymnpBS.js} +4 -4
- package/dist/nitro.d.ts +17 -2
- package/dist/nitro.js +26 -27
- package/dist/{oauth-callback-server-bTspTMCC.js → oauth-callback-server-BtMf3TPZ.js} +2 -2
- package/dist/{prompt-CxatQVaj.js → prompt-CvaDdSX3.js} +1 -1
- package/dist/{registry-BFzQFV_y.js → registry-COTeZ2cV.js} +1 -1
- package/dist/{resolve-CnRHib0U.js → resolve-g9PKz9pH.js} +7 -106
- package/dist/{result-DczfmNBa.js → result-COnC42GR.js} +8 -8
- package/dist/{runner-CJd9Wec-.js → runner-BnTNS7x_.js} +10 -10
- package/dist/{sandbox-egress-signals-DUmVkOx4.js → sandbox-egress-signals-C_IIdLFk.js} +1 -1
- package/dist/{schema-QidbykYl.js → schema-D5Amlzkp.js} +44 -3
- package/dist/{sentry-BBUlHih9.js → sentry-MDkYAVPV.js} +3 -6
- package/dist/{service-account-BI2kQDrb.js → service-account-CwvToidT.js} +3 -3
- package/dist/{service-account-x2QZXm4X.js → service-account-YdYftwV2.js} +5 -5
- package/dist/{session-stop-CtyJsnEj.js → session-stop-CpCUy1ZJ.js} +121 -81
- package/dist/{skills-Dbzx9-rS.js → skills-Bb2KaJ3m.js} +2 -2
- package/dist/{spawn-D8WU99UV.js → spawn-mbTcrLb3.js} +2 -2
- package/dist/{store-TKg1HVKx.js → store-CqRnaZSq.js} +3 -3
- package/dist/{store-UlOrW6JC.js → store-DdT74Lvq.js} +18 -9
- package/dist/{task-queue-BRB49H-J.js → task-queue-8XjDIORF.js} +14 -19
- package/dist/{task-runner-C575f2Dd.js → task-runner-6mLmG6g_.js} +46 -28
- package/dist/{tasks-LOItiYgL.js → tasks-DGbrur6x.js} +14 -2075
- package/dist/{thread-state-IDHdZA7R.js → thread-state-D45lz-oC.js} +5 -5
- package/dist/{turn-failure-response-h_LSXJ-M.js → turn-failure-response-CaEPLSBn.js} +7 -7
- package/dist/{turn-lifecycle-Cvi6Sb8Y.js → turn-lifecycle-C35IyRGp.js} +1 -1
- package/dist/{turn-session-routing-5Nyv4fso.js → turn-session-routing-BKx6rfVd.js} +1 -1
- package/dist/{validation-BeTriWC2.js → validation-C0X3CX51.js} +1 -1
- package/dist/{version-BQX_hLDC.js → version-BPHgJwnm.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{viewer-DmNRM88V.js → viewer-B2wXNspU.js} +21 -5
- package/dist/{web-input-BHcGXSox.js → web-input-Y6aXvmfa.js} +145 -46
- package/dist/{work-DLJMV02b.js → work-DMbqK0kT.js} +10 -10
- package/dist/{worker-vfG2ESg1.js → worker-B-rDJ3XN.js} +8 -6
- package/package.json +10 -9
- package/dist/profile-Dq5I7TV1.js +0 -582
package/dist/app.js
CHANGED
|
@@ -1,47 +1,46 @@
|
|
|
1
|
-
import { C as automationVersionOf, Cn as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { C as automationVersionOf, Cn as getConfigDefaults, Q as projectConversationMessages, Y as appendConversationMessages, _n as isExperimentalFeatureEnabled, b as admitEventAutomationRun, c as withScheduledAutomationLock, fn as getThreadStopDecision, gn as getExperimentalFeatures, i as readScheduledAutomation, l as blockEventAutomation, ln as SubscribedReplyReason, n as listScheduledAutomationsForTeam, o as saveScheduledAutomationInLock, p as findMatchingEventAutomations, s as scheduledRunId, tt as settleConversationTitleWork, u as collectEventAutomationMatchKeys, vn as setExperimentalFeatures, wn as setConfigDefaults, x as eventAutomationRunLimitReason } from "./tasks-DGbrur6x.js";
|
|
2
|
+
import { _ as snapshotRuntimeConfig, d as restoreRuntimeConfig, f as setBotModelConfig, g as setSlackReactionConfig, h as setRuntimeLimits, i as getChatConfig, m as setProfiles, n as botConfig, o as getSlackBotToken, p as setCrossActorMidRunMode, u as resetRuntimeConfig } from "./config-BayQ9hHH.js";
|
|
3
|
+
import { C as withContext, M as JUNIOR_WORKSPACE_SNAPSHOT_JOB_CALLBACK_ROUTE, R as normalizeIdentityEmail, T as withSpan, f as logException, k as JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE, m as logWarn, p as logInfo, r as createRequestContext, w as withLogContext, y as setSpanAttributes } from "./logging-D2QBriUo.js";
|
|
4
|
+
import { n as captureMessage } from "./sentry-MDkYAVPV.js";
|
|
5
|
+
import { t as JUNIOR_VERSION } from "./version-BPHgJwnm.js";
|
|
6
6
|
import { _ as slackMessageTsSchema, d as parseSlackTeamId, f as parseSlackUserId, h as slackUserIdSchema, m as slackTeamIdSchema, o as parseActorUserId, p as slackChannelIdSchema } from "./actor-tlxzqaRe.js";
|
|
7
|
-
import {
|
|
7
|
+
import { P as recordGoogleSignIn, d as getConversationStore, f as getDb, h as withConversationEventLock, l as getConversationEventStore, p as getSqlExecutor, r as readActorIdentity, v as withConversationMutationLock } from "./viewer-B2wXNspU.js";
|
|
8
8
|
import { a as juniorAgentInvocations, d as juniorDestinations, i as juniorAgentBindings, s as juniorConversations } from "./agent-invocations-CxKldAeB.js";
|
|
9
|
-
import { a as juniorAutomationRuns, f as juniorConversationBriefs, h as juniorConversationEvents, s as juniorAutomations } from "./executor-
|
|
10
|
-
import {
|
|
11
|
-
import { n as
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { r as getStateAdapter, s as withLock$1 } from "./adapter-BIacI0Bt.js";
|
|
9
|
+
import { a as juniorAutomationRuns, f as juniorConversationBriefs, h as juniorConversationEvents, s as juniorAutomations } from "./executor-CAr7xQr2.js";
|
|
10
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-D3qbJlYz.js";
|
|
11
|
+
import { C as requestAttachmentDeletion, H as recordSubagentEnded, J as processWorkspaceSnapshotJob, L as effectiveTaskOutcomes, M as logScheduledAutomationRunSkipped, N as scheduledAutomationRunAttributes, T as parseSlackThreadId, V as stopConversationSession, Y as ingestEvent, _ as unpublishedArtifactHeaders, g as readPublicArtifact, h as publicArtifactHeaders, n as executeAgentRun, t as createAgentRunner, v as lookupSlackUserProfile, x as collectAttachmentGarbage } from "./agent-runner-C6MwHJEs.js";
|
|
12
|
+
import { An as createPluginLogger, Dn as createPluginState, Ft as collectEventMatchKeys, G as isVercelSandboxEgressRequest, Gt as bindSlackDirectCredentialSubject, Ht as takeDueTimerWatches, J as consumeSandboxEgressAuthRequiredSignal, Jn as resolveRootVisibility, Jt as verifySlackDirectCredentialSubject, K as proxyVercelSandboxEgressRequest, Kt as verifyEventAutomationCredentialSubject, St as slackExternalRefIdSchema, Ut as bindEventAutomationCredentialSubject, Wt as bindScheduledAutomationCredentialSubject, Y as consumeSandboxEgressPermissionDeniedSignal, Z as parseSandboxEgressCredentialToken, Zn as listConversationAnnotations, at as formatProviderLabel, c as getPluginRoutes, cn as runWithWorkspaceTeamId, ct as startOAuthFlow, en as getSlackClient, fn as getDashboardTaskLink, h as validatePlugins, jn as readConversationAccessFromSql, m as setPlugins, n as createUserTokenStore, nn as isDmChannel, nr as createPluginCache, p as getPlugins, pn as setDashboardConversationLinkOptions, pt as slackMention, q as clearSandboxEgressSignals, qt as verifyScheduledAutomationCredentialSubject, rr as createCache, rt as normalizeSandboxEgressTracePropagationDomains, s as getPluginApiRoutes, sn as getWorkspaceTeamId, st as resolveBaseUrl, un as getDashboardConversationLink, v as setBriefsConfig, xt as slackEntitySchema, zt as findMatchingWatches } from "./credentialed-C6pKlB1p.js";
|
|
13
|
+
import { r as getStateAdapter, s as withLock$1 } from "./adapter-cUkPH3Om.js";
|
|
15
14
|
import { n as getNextRunAtMs } from "./cadence-R7Em4RjE.js";
|
|
16
15
|
import { M as annotationCard } from "./conversation-B0baEwoA.js";
|
|
17
|
-
import { b as listPendingAgentInvocationMailboxAppends, x as listPendingAgentInvocationResults } from "./store-
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { a as getVercelConversationWorkQueue, g as isConversationQueueMessageRejectedError, h as conversationQueueMessageSchema, i as verifyWorkspaceSnapshotJobMessage, o as resolveConversationWorkQueueTopic, s as verifyConversationQueueMessage, t as WORKSPACE_SNAPSHOT_JOB_QUEUE_TOPIC } from "./job-queue-
|
|
21
|
-
import { A as hasRunnableConversationWork$1, M as isInvalidConversationRecordError, O as CONVERSATION_WORK_STALE_ENQUEUE_MS, S as removeActiveConversation, _ as hasRunnableConversationWork, c as clearExpiredConversationLease, g as getConversationWorkState, h as getConversation, m as ensureConversationWake, r as appendAndEnqueueInboundMessage, v as listActiveConversationIds } from "./store-
|
|
22
|
-
import { D as scheduledRunSchema } from "./automation-
|
|
23
|
-
import { n as validatePluginRegistrations, t as validatePluginEgressCredentialHooks } from "./validation-
|
|
16
|
+
import { b as listPendingAgentInvocationMailboxAppends, x as listPendingAgentInvocationResults } from "./store-CqRnaZSq.js";
|
|
17
|
+
import { C as eventGuidance, N as renderTaskInput, d as getEventCatalog, f as SLACK_EVENT_NAMESPACE, g as slackChannelMessageRunsAutomation, h as slackChannelMessageReplyThreadTs, m as slackChannelMessageEvent } from "./session-stop-CpCUy1ZJ.js";
|
|
18
|
+
import { c as setSandboxResourceConfig, o as getSandboxResourceConfig } from "./resolve-g9PKz9pH.js";
|
|
19
|
+
import { a as getVercelConversationWorkQueue, d as getQueueProvider, g as isConversationQueueMessageRejectedError, h as conversationQueueMessageSchema, i as verifyWorkspaceSnapshotJobMessage, o as resolveConversationWorkQueueTopic, s as verifyConversationQueueMessage, t as WORKSPACE_SNAPSHOT_JOB_QUEUE_TOPIC } from "./job-queue-Cjo6Yg_b.js";
|
|
20
|
+
import { A as hasRunnableConversationWork$1, M as isInvalidConversationRecordError, O as CONVERSATION_WORK_STALE_ENQUEUE_MS, S as removeActiveConversation, _ as hasRunnableConversationWork, c as clearExpiredConversationLease, g as getConversationWorkState, h as getConversation, m as ensureConversationWake, r as appendAndEnqueueInboundMessage, v as listActiveConversationIds } from "./store-DdT74Lvq.js";
|
|
21
|
+
import { D as scheduledRunSchema } from "./automation-2RbKzpMT.js";
|
|
22
|
+
import { n as validatePluginRegistrations, t as validatePluginEgressCredentialHooks } from "./validation-C0X3CX51.js";
|
|
24
23
|
import { a as pluginRuntimeRegistrationsFromPluginSet, n as pluginCatalogConfigFromEnv, r as pluginCatalogConfigFromPluginSet, t as defineJuniorPlugins } from "./plugins-DOm9Y6qI.js";
|
|
25
|
-
import { A as THREAD_OPTOUT_ACK, C as buildSlackInboundMessage, D as normalizeIncomingSlackThreadId, E as stopSlackThread, F as renderSlackObjectCard, I as
|
|
26
|
-
import { C as existingConversationId, N as recordAutomationExecution, c as getDispatchConversationId, d as getDispatchRecord, h as listPendingDispatchMailboxAppends, i as buildDispatchId, m as isTerminalDispatchStatus, o as confirmDispatchMailboxAppend, p as getPluginDispatchProjection, s as createOrGetDispatch, t as processPluginTask, x as notifyAutomationBlocked, y as markDispatchFailed } from "./task-runner-
|
|
27
|
-
import { o as getAuthPausedConversationTurnId, t as enqueueAgentInvocationResult } from "./result-
|
|
28
|
-
import { r as enqueueAgentInvocation } from "./work-
|
|
29
|
-
import { s as coerceThreadConversationState } from "./thread-state-
|
|
30
|
-
import { o as unlinkProvider } from "./connections-
|
|
31
|
-
import { a as GET$4, i as publishAppHomeView, r as GET$5, t as receiveLocalOAuthCredential } from "./credential-sync-
|
|
32
|
-
import { a as registerVercelPluginTaskDevConsumer, n as createVercelPluginTaskCallback, s as queueCallback } from "./task-queue-
|
|
33
|
-
import { t as processConversationWork } from "./worker-
|
|
34
|
-
import { t as bindMessageAgent } from "./message-
|
|
35
|
-
import { t as bindSpawnAgent } from "./spawn-
|
|
24
|
+
import { A as THREAD_OPTOUT_ACK, C as buildSlackInboundMessage, D as normalizeIncomingSlackThreadId, E as stopSlackThread, F as renderSlackObjectCard, I as getAttachmentStorage, M as parseContent, N as runWithSlackInstallation, O as withNormalizedThreadId, P as verifySlackSignature, R as enqueueAgentDispatch, S as recordSkippedConversationMessage, T as isSlackMessageStopped, a as recordWebConversationActivity, c as createProductionSlackWebhookServices, f as createSlackWebApiStatusSender, j as startProcessingReaction, k as stopConversationTurn, l as getProductionSlackAdapter, n as conversationTurnIdForMessage, o as webActorFromEmail, s as createProductionConversationWorkOptions, t as appendAndEnqueueWebMessage, v as readViewerAutomationSummary, w as clearSlackPendingReactions, z as GET$3 } from "./web-input-Y6aXvmfa.js";
|
|
25
|
+
import { C as existingConversationId, N as recordAutomationExecution, c as getDispatchConversationId, d as getDispatchRecord, h as listPendingDispatchMailboxAppends, i as buildDispatchId, m as isTerminalDispatchStatus, o as confirmDispatchMailboxAppend, p as getPluginDispatchProjection, s as createOrGetDispatch, t as processPluginTask, x as notifyAutomationBlocked, y as markDispatchFailed } from "./task-runner-6mLmG6g_.js";
|
|
26
|
+
import { o as getAuthPausedConversationTurnId, t as enqueueAgentInvocationResult } from "./result-COnC42GR.js";
|
|
27
|
+
import { r as enqueueAgentInvocation } from "./work-DMbqK0kT.js";
|
|
28
|
+
import { s as coerceThreadConversationState } from "./thread-state-D45lz-oC.js";
|
|
29
|
+
import { o as unlinkProvider } from "./connections-mpN5Lsnj.js";
|
|
30
|
+
import { a as GET$4, i as publishAppHomeView, r as GET$5, t as receiveLocalOAuthCredential } from "./credential-sync-CZmyDNdR.js";
|
|
31
|
+
import { a as registerVercelPluginTaskDevConsumer, n as createVercelPluginTaskCallback, s as queueCallback } from "./task-queue-8XjDIORF.js";
|
|
32
|
+
import { t as processConversationWork } from "./worker-B-rDJ3XN.js";
|
|
33
|
+
import { t as bindMessageAgent } from "./message-C9ymnpBS.js";
|
|
34
|
+
import { t as bindSpawnAgent } from "./spawn-mbTcrLb3.js";
|
|
36
35
|
import { createRequire } from "node:module";
|
|
37
36
|
import { Hono } from "hono";
|
|
38
37
|
import { z } from "zod";
|
|
39
38
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
40
39
|
import { and, asc, eq, inArray, isNotNull, isNull, lte, or, sql } from "drizzle-orm";
|
|
41
|
-
import { EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, dispatchOptionsSchema, eventSchema, isSlackDestination, userSchema } from "@sentry/junior-plugin-api";
|
|
40
|
+
import { EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, dispatchOptionsSchema, eventSchema, eventTriggerFiltersText, isSlackDestination, userSchema } from "@sentry/junior-plugin-api";
|
|
42
41
|
import { pathToFileURL } from "node:url";
|
|
43
42
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
44
|
-
import {
|
|
43
|
+
import { decodeJwt } from "jose";
|
|
45
44
|
import { ChannelImpl, ThreadImpl } from "chat";
|
|
46
45
|
import * as acp from "@agentclientprotocol/sdk";
|
|
47
46
|
import { createAuthMiddleware } from "better-auth/api";
|
|
@@ -1414,7 +1413,7 @@ async function GET$1(request, options = {}) {
|
|
|
1414
1413
|
if (!verifyRetentionRequest(request)) return new Response("Unauthorized", { status: 401 });
|
|
1415
1414
|
try {
|
|
1416
1415
|
const result = await runRetentionPurge(getSqlExecutor(), {
|
|
1417
|
-
attachmentStorage: options.attachmentStorage ??
|
|
1416
|
+
attachmentStorage: options.attachmentStorage ?? getAttachmentStorage(),
|
|
1418
1417
|
nowMs: Date.now()
|
|
1419
1418
|
});
|
|
1420
1419
|
return Response.json(result, { status: 200 });
|
|
@@ -1423,317 +1422,10 @@ async function GET$1(request, options = {}) {
|
|
|
1423
1422
|
return new Response("Retention purge failed", { status: 500 });
|
|
1424
1423
|
}
|
|
1425
1424
|
}
|
|
1426
|
-
// src/chat/sandbox/egress/oidc.ts
|
|
1427
|
-
const OIDC_DISCOVERY_CACHE_TTL_MS = 36e5;
|
|
1428
|
-
const OIDC_DISCOVERY_CACHE_MAX_ENTRIES = 8;
|
|
1429
|
-
const jwksByIssuer = /* @__PURE__ */ new Map();
|
|
1430
|
-
function buildDiscoveryUrl(issuer) {
|
|
1431
|
-
const url = new URL(issuer);
|
|
1432
|
-
if (url.protocol !== "https:" || url.hostname !== "oidc.vercel.com") throw new Error("Unexpected Vercel OIDC issuer");
|
|
1433
|
-
url.pathname = `${url.pathname.replace(/\/$/, "")}/.well-known/openid-configuration`;
|
|
1434
|
-
url.search = "";
|
|
1435
|
-
url.hash = "";
|
|
1436
|
-
return url;
|
|
1437
|
-
}
|
|
1438
|
-
function buildJwksUrl(value) {
|
|
1439
|
-
const url = new URL(value);
|
|
1440
|
-
if (url.protocol !== "https:") throw new Error("Vercel OIDC discovery jwks_uri must use HTTPS");
|
|
1441
|
-
return url;
|
|
1442
|
-
}
|
|
1443
|
-
async function getJwks(issuer) {
|
|
1444
|
-
const now = Date.now();
|
|
1445
|
-
const cached = jwksByIssuer.get(issuer);
|
|
1446
|
-
if (cached && cached.expiresAtMs > now) return cached.jwks;
|
|
1447
|
-
if (cached) jwksByIssuer.delete(issuer);
|
|
1448
|
-
const response = await fetch(buildDiscoveryUrl(issuer), { redirect: "error" });
|
|
1449
|
-
if (!response.ok) throw new Error("Unable to load Vercel OIDC discovery metadata");
|
|
1450
|
-
const config = await response.json();
|
|
1451
|
-
if (!config.jwks_uri) throw new Error("Vercel OIDC discovery metadata did not include jwks_uri");
|
|
1452
|
-
const jwks = createRemoteJWKSet(buildJwksUrl(config.jwks_uri));
|
|
1453
|
-
if (!jwksByIssuer.has(issuer) && jwksByIssuer.size >= OIDC_DISCOVERY_CACHE_MAX_ENTRIES) {
|
|
1454
|
-
const oldestIssuer = jwksByIssuer.keys().next().value;
|
|
1455
|
-
if (oldestIssuer) jwksByIssuer.delete(oldestIssuer);
|
|
1456
|
-
}
|
|
1457
|
-
jwksByIssuer.set(issuer, {
|
|
1458
|
-
jwks,
|
|
1459
|
-
expiresAtMs: now + OIDC_DISCOVERY_CACHE_TTL_MS
|
|
1460
|
-
});
|
|
1461
|
-
return jwks;
|
|
1462
|
-
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Verify Vercel signed this Sandbox firewall proxy request for the active VM session.
|
|
1465
|
-
*
|
|
1466
|
-
* `currentDate` is the time for the `exp` and `nbf` checks. It defaults to
|
|
1467
|
-
* now. Evals pass the real time because they replace `Date`.
|
|
1468
|
-
*/
|
|
1469
|
-
async function verifyVercelSandboxOidcToken(token, options = {}) {
|
|
1470
|
-
const unverified = decodeJwt(token);
|
|
1471
|
-
if (typeof unverified.iss !== "string") throw new Error("Vercel OIDC token did not include an issuer");
|
|
1472
|
-
const jwks = await getJwks(unverified.iss);
|
|
1473
|
-
return (await jwtVerify(token, jwks, {
|
|
1474
|
-
issuer: unverified.iss,
|
|
1475
|
-
...options.currentDate ? { currentDate: options.currentDate } : void 0
|
|
1476
|
-
})).payload;
|
|
1477
|
-
}
|
|
1478
|
-
// src/chat/sandbox/egress/proxy.ts
|
|
1479
|
-
const OIDC_TOKEN_HEADER = "vercel-sandbox-oidc-token";
|
|
1480
|
-
const FORWARDED_HOST_HEADER = "vercel-forwarded-host";
|
|
1481
|
-
const FORWARDED_SCHEME_HEADER = "vercel-forwarded-scheme";
|
|
1482
|
-
const FORWARDED_PORT_HEADER = "vercel-forwarded-port";
|
|
1483
|
-
const FORWARDED_PATH_HEADER = "vercel-forwarded-path";
|
|
1484
|
-
function jsonError(message, status) {
|
|
1485
|
-
return Response.json({ error: message }, { status });
|
|
1486
|
-
}
|
|
1487
|
-
function egressAttributes(input) {
|
|
1488
|
-
return {
|
|
1489
|
-
...input.egressId ? { "app.sandbox.egress_id": input.egressId } : void 0,
|
|
1490
|
-
...input.provider ? { "app.provider.name": input.provider } : void 0,
|
|
1491
|
-
...input.host ? { "server.address": input.host } : void 0,
|
|
1492
|
-
...input.method ? { "http.request.method": input.method } : void 0,
|
|
1493
|
-
...input.path ? { "url.path": input.path } : void 0,
|
|
1494
|
-
...input.status ? { "http.response.status_code": input.status } : void 0
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
|
-
function credentialTokenFromRequest(request) {
|
|
1498
|
-
const pathname = new URL(request.url).pathname;
|
|
1499
|
-
const prefix = `${SANDBOX_EGRESS_PROXY_PATH}/`;
|
|
1500
|
-
if (!pathname.startsWith(prefix)) return;
|
|
1501
|
-
const token = pathname.slice(prefix.length).split("/")[0];
|
|
1502
|
-
if (!token) return;
|
|
1503
|
-
try {
|
|
1504
|
-
return decodeURIComponent(token);
|
|
1505
|
-
} catch {
|
|
1506
|
-
return;
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
|
-
function redactedProxyPath(pathname) {
|
|
1510
|
-
if (pathname.startsWith(`/api/internal/sandbox-egress/`)) return `${SANDBOX_EGRESS_PROXY_PATH}/<token>`;
|
|
1511
|
-
return pathname;
|
|
1512
|
-
}
|
|
1513
|
-
function routingAttributes(request, upstreamUrl) {
|
|
1514
|
-
const attributes = { "app.sandbox.egress.proxy_path": redactedProxyPath(new URL(request.url).pathname) };
|
|
1515
|
-
if (upstreamUrl) {
|
|
1516
|
-
attributes["app.sandbox.egress.upstream_path"] = upstreamUrl.pathname;
|
|
1517
|
-
const gitService = upstreamUrl.searchParams.get("service");
|
|
1518
|
-
if (upstreamUrl.hostname.toLowerCase() === "github.com" && (gitService === "git-upload-pack" || gitService === "git-receive-pack")) attributes["app.sandbox.egress.git_service"] = gitService;
|
|
1519
|
-
}
|
|
1520
|
-
return attributes;
|
|
1521
|
-
}
|
|
1522
|
-
function normalizeHost(value) {
|
|
1523
|
-
const trimmed = value.trim().toLowerCase();
|
|
1524
|
-
if (!trimmed || trimmed.includes("/") || trimmed.includes("\\") || trimmed.includes(":")) return;
|
|
1525
|
-
return trimmed.replace(/\.$/, "");
|
|
1526
|
-
}
|
|
1527
|
-
function normalizeScheme(value) {
|
|
1528
|
-
return value.trim().toLowerCase() === "https" ? "https" : void 0;
|
|
1529
|
-
}
|
|
1530
|
-
function normalizePort(value) {
|
|
1531
|
-
if (!value) return;
|
|
1532
|
-
const trimmed = value.trim();
|
|
1533
|
-
if (!/^\d{1,5}$/.test(trimmed)) return;
|
|
1534
|
-
const port = Number.parseInt(trimmed, 10);
|
|
1535
|
-
return port >= 1 && port <= 65535 ? trimmed : void 0;
|
|
1536
|
-
}
|
|
1537
|
-
function sandboxIdFromPayload(payload) {
|
|
1538
|
-
return typeof payload.sandbox_id === "string" ? payload.sandbox_id : void 0;
|
|
1539
|
-
}
|
|
1540
|
-
function normalizedForwardedPath(path) {
|
|
1541
|
-
if (!path.startsWith("/") || path.startsWith("//") || path.includes("#") || /[\r\n]/.test(path)) return {
|
|
1542
|
-
ok: false,
|
|
1543
|
-
error: "Invalid forwarded path"
|
|
1544
|
-
};
|
|
1545
|
-
try {
|
|
1546
|
-
const url = new URL(path, "https://sandbox-forwarded.local");
|
|
1547
|
-
return {
|
|
1548
|
-
ok: true,
|
|
1549
|
-
path: `${url.pathname}${url.search}`
|
|
1550
|
-
};
|
|
1551
|
-
} catch {
|
|
1552
|
-
return {
|
|
1553
|
-
ok: false,
|
|
1554
|
-
error: "Invalid forwarded path"
|
|
1555
|
-
};
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
function upstreamPath(request) {
|
|
1559
|
-
const forwardedPath = request.headers.get(FORWARDED_PATH_HEADER);
|
|
1560
|
-
if (!forwardedPath?.trim()) return {
|
|
1561
|
-
ok: false,
|
|
1562
|
-
error: "Missing forwarded path"
|
|
1563
|
-
};
|
|
1564
|
-
return normalizedForwardedPath(forwardedPath.trim());
|
|
1565
|
-
}
|
|
1566
|
-
/**
|
|
1567
|
-
* Rebuild the HTTPS upstream URL from Vercel forwarding headers.
|
|
1568
|
-
*
|
|
1569
|
-
* This is the proxy's wire-format boundary for forwarded requests: host, path,
|
|
1570
|
-
* scheme, and port must normalize before the request can reach provider egress.
|
|
1571
|
-
*/
|
|
1572
|
-
function buildUpstreamUrl(request) {
|
|
1573
|
-
const forwardedHost = request.headers.get(FORWARDED_HOST_HEADER);
|
|
1574
|
-
if (!forwardedHost?.trim()) return {
|
|
1575
|
-
ok: false,
|
|
1576
|
-
error: "Missing forwarded host"
|
|
1577
|
-
};
|
|
1578
|
-
const host = normalizeHost(forwardedHost);
|
|
1579
|
-
if (!host) return {
|
|
1580
|
-
ok: false,
|
|
1581
|
-
error: "Invalid forwarded host"
|
|
1582
|
-
};
|
|
1583
|
-
const forwardedScheme = request.headers.get(FORWARDED_SCHEME_HEADER);
|
|
1584
|
-
if (!forwardedScheme?.trim()) return {
|
|
1585
|
-
ok: false,
|
|
1586
|
-
error: "Missing forwarded scheme"
|
|
1587
|
-
};
|
|
1588
|
-
const scheme = normalizeScheme(forwardedScheme);
|
|
1589
|
-
if (!scheme) return {
|
|
1590
|
-
ok: false,
|
|
1591
|
-
error: "Forwarded scheme must be https"
|
|
1592
|
-
};
|
|
1593
|
-
const forwardedPort = request.headers.get(FORWARDED_PORT_HEADER);
|
|
1594
|
-
const port = normalizePort(forwardedPort);
|
|
1595
|
-
if (forwardedPort && !port) return {
|
|
1596
|
-
ok: false,
|
|
1597
|
-
error: "Invalid forwarded port"
|
|
1598
|
-
};
|
|
1599
|
-
const path = upstreamPath(request);
|
|
1600
|
-
if (!path.ok) return {
|
|
1601
|
-
ok: false,
|
|
1602
|
-
error: path.error
|
|
1603
|
-
};
|
|
1604
|
-
try {
|
|
1605
|
-
return {
|
|
1606
|
-
ok: true,
|
|
1607
|
-
url: new URL(`${scheme}://${host}${port ? `:${port}` : ""}${path.path}`)
|
|
1608
|
-
};
|
|
1609
|
-
} catch {
|
|
1610
|
-
return {
|
|
1611
|
-
ok: false,
|
|
1612
|
-
error: "Invalid forwarded URL"
|
|
1613
|
-
};
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
/**
|
|
1617
|
-
* Name the Conversation and the accountable person from the verified token.
|
|
1618
|
-
* Each proxy request is a new HTTP request with no run log context, so the
|
|
1619
|
-
* proxy binds the same fields that the run binds.
|
|
1620
|
-
*/
|
|
1621
|
-
function sandboxEgressLogContext(context) {
|
|
1622
|
-
return {
|
|
1623
|
-
...context.conversationId ? { conversationId: context.conversationId } : void 0,
|
|
1624
|
-
...credentialLogContext(context.credentials)
|
|
1625
|
-
};
|
|
1626
|
-
}
|
|
1627
|
-
/** Continue inbound trace context only after sandbox egress verification succeeds. */
|
|
1628
|
-
function continueSandboxEgressTrace(request, upstreamHost, tracePropagation, credentialContext, callback) {
|
|
1629
|
-
const sentryTrace = request.headers.get("sentry-trace") ?? void 0;
|
|
1630
|
-
const baggage = request.headers.get("baggage") ?? void 0;
|
|
1631
|
-
const logContext = sandboxEgressLogContext(credentialContext);
|
|
1632
|
-
const run = () => withSpan("sandbox.egress", "http.server", logContext, async () => {
|
|
1633
|
-
setSentryUser(logContext.userId ? { id: logContext.userId } : void 0);
|
|
1634
|
-
return await callback();
|
|
1635
|
-
}, {
|
|
1636
|
-
"http.request.method": request.method,
|
|
1637
|
-
"url.path": redactedProxyPath(new URL(request.url).pathname)
|
|
1638
|
-
});
|
|
1639
|
-
if (!shouldPropagateSandboxEgressTrace(upstreamHost, tracePropagation) || !sentryTrace && !baggage) return run();
|
|
1640
|
-
return continueTrace({
|
|
1641
|
-
sentryTrace,
|
|
1642
|
-
baggage
|
|
1643
|
-
}, run);
|
|
1644
|
-
}
|
|
1645
|
-
/**
|
|
1646
|
-
* Return whether a request carries the forwarding headers expected from Vercel.
|
|
1647
|
-
*
|
|
1648
|
-
* This is only a cheap classifier for routing into the proxy handler. Real
|
|
1649
|
-
* authentication happens in `proxySandboxEgressRequest` by verifying the OIDC
|
|
1650
|
-
* token and signed Junior credential context.
|
|
1651
|
-
*/
|
|
1652
|
-
function isSandboxEgressForwardedRequest(request) {
|
|
1653
|
-
return Boolean(request.headers.get(OIDC_TOKEN_HEADER)?.trim() && request.headers.get(FORWARDED_HOST_HEADER)?.trim() && request.headers.get(FORWARDED_SCHEME_HEADER)?.trim());
|
|
1654
|
-
}
|
|
1655
|
-
/**
|
|
1656
|
-
* Handle one forwarded sandbox egress request.
|
|
1657
|
-
*
|
|
1658
|
-
* This is the public proxy boundary: it rejects unauthenticated VM requests,
|
|
1659
|
-
* rejects forwarded URLs outside plugin-owned provider domains, rejects signed
|
|
1660
|
-
* credential contexts that do not match the active VM id, and only then asks
|
|
1661
|
-
* `execute.ts` to issue credentials and contact the upstream provider.
|
|
1662
|
-
*/
|
|
1663
|
-
async function proxySandboxEgressRequest(request, deps = {}) {
|
|
1664
|
-
const oidcToken = request.headers.get(OIDC_TOKEN_HEADER)?.trim();
|
|
1665
|
-
if (!oidcToken) return jsonError("Missing Vercel Sandbox OIDC token", 401);
|
|
1666
|
-
let oidcPayload;
|
|
1667
|
-
try {
|
|
1668
|
-
oidcPayload = await (deps.verifyOidc ?? verifyVercelSandboxOidcToken)(oidcToken);
|
|
1669
|
-
} catch (error) {
|
|
1670
|
-
logWarn("sandbox.egress.oidc_verification.failed", { "app.sandbox.oidc_error": error instanceof Error ? error.message : String(error) });
|
|
1671
|
-
return jsonError("Invalid Vercel Sandbox OIDC token", 401);
|
|
1672
|
-
}
|
|
1673
|
-
const activeEgressId = sandboxIdFromPayload(oidcPayload);
|
|
1674
|
-
if (!activeEgressId) {
|
|
1675
|
-
logWarn("sandbox.egress.oidc_session.missing", {
|
|
1676
|
-
"http.request.method": request.method,
|
|
1677
|
-
"url.path": redactedProxyPath(new URL(request.url).pathname)
|
|
1678
|
-
});
|
|
1679
|
-
return jsonError("Vercel Sandbox OIDC token did not include sandbox_id", 401);
|
|
1680
|
-
}
|
|
1681
|
-
const upstreamResult = buildUpstreamUrl(request);
|
|
1682
|
-
if (!upstreamResult.ok) {
|
|
1683
|
-
logWarn("sandbox.egress.upstream_url.invalid", {
|
|
1684
|
-
...egressAttributes({
|
|
1685
|
-
egressId: activeEgressId,
|
|
1686
|
-
method: request.method,
|
|
1687
|
-
path: redactedProxyPath(new URL(request.url).pathname),
|
|
1688
|
-
status: 400
|
|
1689
|
-
}),
|
|
1690
|
-
...routingAttributes(request)
|
|
1691
|
-
});
|
|
1692
|
-
return jsonError(upstreamResult.error, 400);
|
|
1693
|
-
}
|
|
1694
|
-
const upstreamUrl = upstreamResult.url;
|
|
1695
|
-
const provider = resolveSandboxEgressProviderForHost(upstreamUrl.hostname);
|
|
1696
|
-
if (!provider) {
|
|
1697
|
-
logWarn("sandbox.egress.provider.unresolved", {
|
|
1698
|
-
...egressAttributes({
|
|
1699
|
-
egressId: activeEgressId,
|
|
1700
|
-
host: upstreamUrl.hostname,
|
|
1701
|
-
method: request.method,
|
|
1702
|
-
path: upstreamUrl.pathname,
|
|
1703
|
-
status: 403
|
|
1704
|
-
}),
|
|
1705
|
-
...routingAttributes(request, upstreamUrl)
|
|
1706
|
-
});
|
|
1707
|
-
return jsonError("No provider owns forwarded host", 403);
|
|
1708
|
-
}
|
|
1709
|
-
const credentialContext = parseSandboxEgressCredentialToken(credentialTokenFromRequest(request));
|
|
1710
|
-
if (!credentialContext || credentialContext.egressId !== activeEgressId) {
|
|
1711
|
-
logWarn("sandbox.egress.credential_context.unauthorized", {
|
|
1712
|
-
...egressAttributes({
|
|
1713
|
-
egressId: activeEgressId,
|
|
1714
|
-
host: upstreamUrl.hostname,
|
|
1715
|
-
method: request.method,
|
|
1716
|
-
path: upstreamUrl.pathname,
|
|
1717
|
-
provider,
|
|
1718
|
-
status: 403
|
|
1719
|
-
}),
|
|
1720
|
-
...routingAttributes(request, upstreamUrl)
|
|
1721
|
-
});
|
|
1722
|
-
return jsonError("Sandbox egress credential context is not authorized", 403);
|
|
1723
|
-
}
|
|
1724
|
-
return await continueSandboxEgressTrace(request, upstreamUrl.hostname, deps.tracePropagation ?? {}, credentialContext, async () => await executeCredentialedEgressRequest({
|
|
1725
|
-
activeEgressId,
|
|
1726
|
-
credentialContext,
|
|
1727
|
-
deps,
|
|
1728
|
-
provider,
|
|
1729
|
-
request,
|
|
1730
|
-
upstreamUrl
|
|
1731
|
-
}));
|
|
1732
|
-
}
|
|
1733
1425
|
// src/handlers/sandbox-egress-proxy.ts
|
|
1734
1426
|
/** Handles Vercel Sandbox firewall egress proxy requests. */
|
|
1735
1427
|
async function ALL(request, options = {}) {
|
|
1736
|
-
return await
|
|
1428
|
+
return await proxyVercelSandboxEgressRequest(request, {
|
|
1737
1429
|
interceptHttp: options.interceptHttp,
|
|
1738
1430
|
tracePropagation: options.tracePropagation,
|
|
1739
1431
|
verifyOidc: options.verifyOidc
|
|
@@ -1741,7 +1433,7 @@ async function ALL(request, options = {}) {
|
|
|
1741
1433
|
}
|
|
1742
1434
|
/** Return whether a request should be routed through sandbox egress proxying. */
|
|
1743
1435
|
function isSandboxEgressRequest(request) {
|
|
1744
|
-
return
|
|
1436
|
+
return isVercelSandboxEgressRequest(request);
|
|
1745
1437
|
}
|
|
1746
1438
|
// src/handlers/sandbox-egress-route.ts
|
|
1747
1439
|
/**
|
|
@@ -2454,7 +2146,7 @@ function dateField(key, label, value, empty) {
|
|
|
2454
2146
|
function renderAutomationDetails(automation) {
|
|
2455
2147
|
const url = getDashboardTaskLink(automation.id);
|
|
2456
2148
|
if (!url) return void 0;
|
|
2457
|
-
const fields = [textField("status", "Status", automation.kind === "scheduled" ? automation.status : automation.
|
|
2149
|
+
const fields = [textField("status", "Status", automation.kind === "scheduled" ? automation.status : automation.trigger.problems.length === 0 ? "ready" : "unavailable"), {
|
|
2458
2150
|
...textField("description", "Instruction", automation.instruction),
|
|
2459
2151
|
format: "markdown",
|
|
2460
2152
|
long: true
|
|
@@ -2464,15 +2156,19 @@ function renderAutomationDetails(automation) {
|
|
|
2464
2156
|
long: true
|
|
2465
2157
|
}, dateField("next_run", "Next run", automation.nextRunAt, "None"));
|
|
2466
2158
|
else {
|
|
2159
|
+
const { trigger } = automation;
|
|
2467
2160
|
fields.push(textField("source", "Source", automation.source), {
|
|
2468
2161
|
...textField("resource", "Resource", automation.resource),
|
|
2469
2162
|
long: true
|
|
2470
2163
|
}, {
|
|
2471
|
-
...textField("events", "Events",
|
|
2164
|
+
...textField("events", "Events", trigger.events.join(", ")),
|
|
2165
|
+
long: true
|
|
2166
|
+
}, {
|
|
2167
|
+
...textField("filters", "Filters", trigger.filters.length > 0 ? eventTriggerFiltersText(trigger.filters) : "No filters. Runs on every selected event."),
|
|
2472
2168
|
long: true
|
|
2473
2169
|
});
|
|
2474
|
-
if (
|
|
2475
|
-
...textField("warning", "Needs attention", "Trigger unavailable.
|
|
2170
|
+
if (trigger.problems.length > 0) fields.push({
|
|
2171
|
+
...textField("warning", "Needs attention", ["Trigger unavailable.", ...trigger.problems].join(" ")),
|
|
2476
2172
|
long: true
|
|
2477
2173
|
});
|
|
2478
2174
|
}
|
|
@@ -3013,40 +2709,27 @@ async function handleSlackWebhook(args) {
|
|
|
3013
2709
|
return new Response("ok", { status: 200 });
|
|
3014
2710
|
}
|
|
3015
2711
|
// src/handlers/webhooks.ts
|
|
3016
|
-
/** Run a platform webhook with shared request
|
|
2712
|
+
/** Run a platform webhook with shared request log context and error logging. */
|
|
3017
2713
|
async function handleWebhookRequest(request, platform, run) {
|
|
3018
2714
|
const requestContext = createRequestContext(request, { platform });
|
|
3019
|
-
const requestUrl = new URL(request.url);
|
|
3020
2715
|
return await withContext(requestContext, async () => {
|
|
3021
2716
|
try {
|
|
3022
|
-
|
|
2717
|
+
const response = await run();
|
|
2718
|
+
if (response.status >= 400) {
|
|
2719
|
+
let responseBodySnippet;
|
|
3023
2720
|
try {
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
try {
|
|
3028
|
-
responseBodySnippet = (await response.clone().text()).slice(0, 300);
|
|
3029
|
-
} catch {
|
|
3030
|
-
responseBodySnippet = void 0;
|
|
3031
|
-
}
|
|
3032
|
-
logWarn("webhook.response.unsuccessful", {
|
|
3033
|
-
"http.response.status_code": response.status,
|
|
3034
|
-
"http.request.header.x_slack_signature": request.headers.get("x-slack-signature") ?? void 0,
|
|
3035
|
-
"http.request.header.x_slack_request_timestamp": request.headers.get("x-slack-request-timestamp") ?? void 0,
|
|
3036
|
-
...responseBodySnippet ? { "app.webhook.response_body": responseBodySnippet } : void 0
|
|
3037
|
-
});
|
|
3038
|
-
}
|
|
3039
|
-
setSpanAttributes({ "http.response.status_code": response.status });
|
|
3040
|
-
setSpanStatus(response.status >= 500 ? "error" : "ok");
|
|
3041
|
-
return response;
|
|
3042
|
-
} catch (error) {
|
|
3043
|
-
setSpanStatus("error");
|
|
3044
|
-
throw error;
|
|
2721
|
+
responseBodySnippet = (await response.clone().text()).slice(0, 300);
|
|
2722
|
+
} catch {
|
|
2723
|
+
responseBodySnippet = void 0;
|
|
3045
2724
|
}
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
2725
|
+
logWarn("webhook.response.unsuccessful", {
|
|
2726
|
+
"http.response.status_code": response.status,
|
|
2727
|
+
"http.request.header.x_slack_signature": request.headers.get("x-slack-signature") ?? void 0,
|
|
2728
|
+
"http.request.header.x_slack_request_timestamp": request.headers.get("x-slack-request-timestamp") ?? void 0,
|
|
2729
|
+
...responseBodySnippet ? { "app.webhook.response_body": responseBodySnippet } : void 0
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
return response;
|
|
3050
2733
|
} catch (error) {
|
|
3051
2734
|
logException(error, "webhook.handler.failed");
|
|
3052
2735
|
throw error;
|
|
@@ -3073,25 +2756,52 @@ function parseConversationQueueMessage(message) {
|
|
|
3073
2756
|
function resolveConversationWorkVisibilityTimeoutSeconds(functionMaxDurationSeconds = getChatConfig().functionMaxDurationSeconds) {
|
|
3074
2757
|
return functionMaxDurationSeconds + 30;
|
|
3075
2758
|
}
|
|
3076
|
-
/**
|
|
3077
|
-
|
|
2759
|
+
/**
|
|
2760
|
+
* Sentry queue attributes for the worker span. The span always sets the
|
|
2761
|
+
* messaging system and destination so the inherited Slack values do not win.
|
|
2762
|
+
*/
|
|
2763
|
+
function queueProcessAttributes(metadata, nowMs) {
|
|
2764
|
+
return {
|
|
2765
|
+
"messaging.destination.name": metadata?.topicName ?? resolveConversationWorkQueueTopic(),
|
|
2766
|
+
"messaging.system": getQueueProvider().messagingSystem,
|
|
2767
|
+
...metadata && {
|
|
2768
|
+
"messaging.message.id": metadata.messageId,
|
|
2769
|
+
"messaging.message.receive.latency": nowMs - metadata.createdAt.getTime(),
|
|
2770
|
+
"messaging.message.retry.count": metadata.deliveryCount - 1
|
|
2771
|
+
}
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Process one queue payload with the generic conversation worker.
|
|
2776
|
+
* Pass the queue metadata to record time in queue and retries on the span.
|
|
2777
|
+
*/
|
|
2778
|
+
async function processConversationQueueMessage(message, options, metadata) {
|
|
3078
2779
|
const parsed = parseConversationQueueMessage(message);
|
|
3079
2780
|
try {
|
|
3080
|
-
return await
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
2781
|
+
return await withSpan("conversation.work.process", "queue.process", { conversationId: parsed.conversationId }, async (setSpanAttributes) => {
|
|
2782
|
+
try {
|
|
2783
|
+
const result = await processConversationWork(parsed, {
|
|
2784
|
+
checkInIntervalMs: options.checkInIntervalMs,
|
|
2785
|
+
conversationStore: options.conversationStore,
|
|
2786
|
+
nowMs: options.nowMs,
|
|
2787
|
+
queue: options.queue ?? getVercelConversationWorkQueue(),
|
|
2788
|
+
run: options.run,
|
|
2789
|
+
softYieldAfterMs: options.softYieldAfterMs,
|
|
2790
|
+
state: options.state
|
|
2791
|
+
});
|
|
2792
|
+
setSpanAttributes({ "app.worker.outcome": result.status });
|
|
2793
|
+
return result;
|
|
2794
|
+
} catch (error) {
|
|
2795
|
+
if (isConversationQueueMessageRejectedError(error)) setSpanAttributes({ "app.worker.outcome": error.reason });
|
|
2796
|
+
throw error;
|
|
2797
|
+
}
|
|
2798
|
+
}, queueProcessAttributes(metadata, options.nowMs?.() ?? Date.now()));
|
|
3089
2799
|
} finally {
|
|
3090
2800
|
options.waitUntil?.(settleConversationTitleWork(parsed.conversationId));
|
|
3091
2801
|
}
|
|
3092
2802
|
}
|
|
3093
2803
|
/**
|
|
3094
|
-
* Run one queue message through the worker without a
|
|
2804
|
+
* Run one queue message through the worker without a queue push callback.
|
|
3095
2805
|
* In-process queues use this. The callback's kill switch, request deadline,
|
|
3096
2806
|
* and permanent rejection rules apply; signature checks do not.
|
|
3097
2807
|
*/
|
|
@@ -3101,9 +2811,7 @@ async function consumeConversationQueueMessage(message, options) {
|
|
|
3101
2811
|
consumerGroup: "in_process",
|
|
3102
2812
|
createdAt: new Date(nowMs),
|
|
3103
2813
|
deliveryCount: 1,
|
|
3104
|
-
expiresAt: new Date(nowMs + 3e4),
|
|
3105
2814
|
messageId: options.messageId,
|
|
3106
|
-
region: "local",
|
|
3107
2815
|
topicName: resolveConversationWorkQueueTopic(options)
|
|
3108
2816
|
});
|
|
3109
2817
|
}
|
|
@@ -3135,8 +2843,8 @@ function conversationWorkCallback(options) {
|
|
|
3135
2843
|
});
|
|
3136
2844
|
return true;
|
|
3137
2845
|
},
|
|
3138
|
-
run: async (message) => {
|
|
3139
|
-
await processConversationQueueMessage(message, options);
|
|
2846
|
+
run: async (message, metadata) => {
|
|
2847
|
+
await processConversationQueueMessage(message, options, metadata);
|
|
3140
2848
|
},
|
|
3141
2849
|
topic: resolveConversationWorkQueueTopic(options),
|
|
3142
2850
|
verify: verifyConversationQueueMessage,
|
|
@@ -3152,6 +2860,7 @@ function registerVercelConversationWorkDevConsumer(options) {
|
|
|
3152
2860
|
return conversationWorkCallback(options).registerDev();
|
|
3153
2861
|
}
|
|
3154
2862
|
// src/chat/sandbox/snapshot/job-callback.ts
|
|
2863
|
+
/** Receive queue messages for Workspace snapshot builds. */
|
|
3155
2864
|
const WORKSPACE_SNAPSHOT_JOB_DEV_CONSUMER_GROUP = "junior_workspace_snapshots_dev";
|
|
3156
2865
|
function logWorkspaceSnapshotJobRejected(reason, metadata) {
|
|
3157
2866
|
logWarn("workspace.snapshot.job.queue_message.rejected", {
|
|
@@ -3187,7 +2896,7 @@ async function publicArtifactGET(args) {
|
|
|
3187
2896
|
const artifact = await readPublicArtifact({
|
|
3188
2897
|
db: args.db ?? getSqlExecutor(),
|
|
3189
2898
|
filename: args.filename,
|
|
3190
|
-
storage: args.storage ??
|
|
2899
|
+
storage: args.storage ?? getAttachmentStorage()
|
|
3191
2900
|
});
|
|
3192
2901
|
if (!artifact) return new Response("Not Found", {
|
|
3193
2902
|
headers: unpublishedArtifactHeaders(),
|
|
@@ -5256,7 +4965,6 @@ function dashboardHostRoutePaths(dashboard, authenticatedRoutes = []) {
|
|
|
5256
4965
|
pagePath("settings"),
|
|
5257
4966
|
`${pagePath("settings")}/*`
|
|
5258
4967
|
];
|
|
5259
|
-
if (dashboard.componentGallery) pagePaths.push(pagePath("dev"), `${pagePath("dev")}/*`);
|
|
5260
4968
|
const loginPath = basePath === "/" ? "/auth/login" : `${basePath}/auth/login`;
|
|
5261
4969
|
return [
|
|
5262
4970
|
...pagePaths,
|
|
@@ -5514,7 +5222,7 @@ async function createApp(options) {
|
|
|
5514
5222
|
messageId: delivery.messageId
|
|
5515
5223
|
})) ?? getVercelConversationWorkQueue();
|
|
5516
5224
|
const pluginTaskQueue = options?.pluginTaskQueue?.(processPluginTask);
|
|
5517
|
-
const attachmentStorage =
|
|
5225
|
+
const attachmentStorage = getAttachmentStorage();
|
|
5518
5226
|
const agentRunner = createAgentRunner(executeAgentRun, {
|
|
5519
5227
|
attachmentStorage,
|
|
5520
5228
|
bindMessageAgent: (request) => bindMessageAgent(request, { queue: conversationWorkQueue }),
|