@sentry/junior 0.76.1 → 0.78.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/dist/{agent-hooks-ZOE7RIED.js → agent-hooks-OFDNZJB2.js} +6 -4
- package/dist/api-reference.d.ts +2 -2
- package/dist/app.d.ts +27 -0
- package/dist/app.js +264 -21
- package/dist/build/copy-build-content.d.ts +2 -0
- package/dist/build/virtual-config.d.ts +3 -0
- package/dist/chat/conversations/store.d.ts +1 -1
- package/dist/chat/plugins/agent-hooks.d.ts +7 -1
- package/dist/chat/slack/dashboard-link.d.ts +9 -0
- package/dist/chat/state/adapter.d.ts +0 -1
- package/dist/chat/state/locks.d.ts +7 -0
- package/dist/chat/task-execution/state.d.ts +21 -1
- package/dist/{chunk-NYKJ3KON.js → chunk-237T7XAN.js} +3 -3
- package/dist/{chunk-FFGXUXMD.js → chunk-2MSW5BZY.js} +10 -10
- package/dist/{chunk-ZQB37HUX.js → chunk-C2YBH4S6.js} +1 -1
- package/dist/{chunk-XBBC6W45.js → chunk-KIDP757T.js} +1 -1
- package/dist/{chunk-R6Z5XWY3.js → chunk-LUNMJQ7D.js} +49 -3
- package/dist/{chunk-Y5OFBCBZ.js → chunk-LXTPBU4K.js} +14 -10
- package/dist/{chunk-JBASI5VV.js → chunk-PNGAJ75P.js} +2 -2
- package/dist/{chunk-KOIMO7S3.js → chunk-QDQVOMBA.js} +21 -2
- package/dist/{chunk-56TBVRJG.js → chunk-RARSKPVT.js} +1 -1
- package/dist/{chunk-NFTMTIP3.js → chunk-SSWBYEFH.js} +26 -2
- package/dist/{chunk-4SCWV7TJ.js → chunk-Y3EG7S7P.js} +1 -1
- package/dist/{chunk-ZLMBNBUG.js → chunk-YLVJRYTD.js} +9 -1
- package/dist/cli/chat.js +6 -6
- package/dist/cli/check.js +2 -2
- package/dist/cli/plugins.js +6 -6
- package/dist/cli/snapshot-warmup.js +3 -3
- package/dist/cli/upgrade.js +7 -7
- package/dist/{db-7A7PFRGL.js → db-NGQ3JCMF.js} +1 -1
- package/dist/nitro.d.ts +4 -0
- package/dist/nitro.js +59 -3
- package/dist/{registry-OIPAJU2O.js → registry-RRIDPJBT.js} +1 -1
- package/dist/reporting.js +13 -24
- package/dist/{runner-7Z4D6AKV.js → runner-WW4GJFUB.js} +9 -9
- package/dist/{validation-SLA6IGF7.js → validation-MDMYBRFB.js} +2 -2
- package/package.json +5 -5
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PluginHookDeniedError,
|
|
3
3
|
createPluginHookRunner,
|
|
4
|
+
getPluginDashboardRoutes,
|
|
4
5
|
getPluginOperationalReports,
|
|
5
6
|
getPluginRoutes,
|
|
6
7
|
getPluginSlackConversationLink,
|
|
@@ -10,11 +11,11 @@ import {
|
|
|
10
11
|
getPlugins,
|
|
11
12
|
setPlugins,
|
|
12
13
|
validatePlugins
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-SSWBYEFH.js";
|
|
15
|
+
import "./chunk-RARSKPVT.js";
|
|
16
|
+
import "./chunk-237T7XAN.js";
|
|
16
17
|
import "./chunk-G3E7SCME.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
+
import "./chunk-LXTPBU4K.js";
|
|
18
19
|
import "./chunk-Q6XFTRV5.js";
|
|
19
20
|
import "./chunk-T77LUIX3.js";
|
|
20
21
|
import "./chunk-VALUBQ7R.js";
|
|
@@ -25,6 +26,7 @@ import "./chunk-MLKGABMK.js";
|
|
|
25
26
|
export {
|
|
26
27
|
PluginHookDeniedError,
|
|
27
28
|
createPluginHookRunner,
|
|
29
|
+
getPluginDashboardRoutes,
|
|
28
30
|
getPluginOperationalReports,
|
|
29
31
|
getPluginRoutes,
|
|
30
32
|
getPluginSlackConversationLink,
|
package/dist/api-reference.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { createApp } from "./app";
|
|
2
|
-
export type { JuniorAppOptions } from "./app";
|
|
2
|
+
export type { JuniorAppOptions, JuniorDashboardOptions } from "./app";
|
|
3
3
|
export { initSentry } from "./instrumentation";
|
|
4
4
|
export { juniorNitro } from "./nitro";
|
|
5
|
-
export type { JuniorNitroOptions } from "./nitro";
|
|
5
|
+
export type { JuniorNitroDashboardOptions, JuniorNitroOptions } from "./nitro";
|
|
6
6
|
export { defineJuniorPlugins } from "./plugins";
|
|
7
7
|
export type { JuniorPluginInput, JuniorPluginSet, JuniorPluginSetOptions, } from "./plugins";
|
|
8
8
|
export type { PluginRunContext, PluginRunTranscriptEntry, PluginTaskContext, PluginTaskDefinition, PluginTasks, } from "@sentry/junior-plugin-api";
|
package/dist/app.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
+
import type { JuniorReporting } from "./reporting";
|
|
2
3
|
import { type JuniorPluginSet } from "./plugins";
|
|
3
4
|
import { type VercelConversationWorkCallbackOptions } from "@/chat/task-execution/vercel-callback";
|
|
4
5
|
import type { WaitUntilFn } from "@/handlers/types";
|
|
5
6
|
export { defineJuniorPlugins } from "./plugins";
|
|
6
7
|
export type { JuniorPluginInput, JuniorPluginSet, JuniorPluginSetOptions, } from "./plugins";
|
|
7
8
|
export interface JuniorAppOptions {
|
|
9
|
+
/** Authenticated dashboard mounted by core when configured. */
|
|
10
|
+
dashboard?: JuniorDashboardOptions;
|
|
8
11
|
/** Slack-specific overrides applied after env parsing. */
|
|
9
12
|
slack?: {
|
|
10
13
|
/** Slack emoji shown while Junior is processing. Defaults to `eyes`. */
|
|
@@ -29,5 +32,29 @@ export interface JuniorAppOptions {
|
|
|
29
32
|
};
|
|
30
33
|
waitUntil?: WaitUntilFn;
|
|
31
34
|
}
|
|
35
|
+
export interface JuniorDashboardOptions {
|
|
36
|
+
/** Browser auth route prefix used by Better Auth. */
|
|
37
|
+
authPath?: string;
|
|
38
|
+
/** Require a dashboard browser session before serving dashboard pages and APIs. */
|
|
39
|
+
authRequired?: boolean;
|
|
40
|
+
/** Exact Google account emails allowed to open the dashboard. */
|
|
41
|
+
allowedEmails?: string[];
|
|
42
|
+
/** Google Workspace domains allowed to open the dashboard. */
|
|
43
|
+
allowedGoogleDomains?: string[];
|
|
44
|
+
/** Browser route prefix for the dashboard shell. */
|
|
45
|
+
basePath?: string;
|
|
46
|
+
/** Public deployment origin used for auth callbacks and external links. */
|
|
47
|
+
baseURL?: string;
|
|
48
|
+
/** Disable dashboard route mounting while preserving serializable config shape. */
|
|
49
|
+
disabled?: boolean;
|
|
50
|
+
/** Overlay dashboard visual-QA fixture conversations onto real reporting data. */
|
|
51
|
+
mockConversations?: boolean;
|
|
52
|
+
/** Reporting implementation used by dashboard APIs. Defaults to core reporting. */
|
|
53
|
+
reporting?: JuniorReporting;
|
|
54
|
+
/** Browser session lifetime in seconds. */
|
|
55
|
+
sessionMaxAgeSeconds?: number;
|
|
56
|
+
/** Additional trusted origins accepted by Better Auth. */
|
|
57
|
+
trustedOrigins?: string[];
|
|
58
|
+
}
|
|
32
59
|
/** Create a Hono app with all Junior routes. */
|
|
33
60
|
export declare function createApp(options?: JuniorAppOptions): Promise<Hono>;
|
package/dist/app.js
CHANGED
|
@@ -71,7 +71,7 @@ import {
|
|
|
71
71
|
updateConversationStats,
|
|
72
72
|
uploadFilesToThread,
|
|
73
73
|
upsertConversationMessage
|
|
74
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-2MSW5BZY.js";
|
|
75
75
|
import {
|
|
76
76
|
CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
|
|
77
77
|
CONVERSATION_WORK_STALE_ENQUEUE_MS,
|
|
@@ -90,12 +90,12 @@ import {
|
|
|
90
90
|
requestConversationContinuation,
|
|
91
91
|
requestConversationWork,
|
|
92
92
|
startConversationWork
|
|
93
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-LUNMJQ7D.js";
|
|
94
94
|
import {
|
|
95
95
|
JUNIOR_THREAD_STATE_TTL_MS,
|
|
96
96
|
coerceThreadConversationState
|
|
97
97
|
} from "./chunk-Z4CIQ3EB.js";
|
|
98
|
-
import "./chunk-
|
|
98
|
+
import "./chunk-PNGAJ75P.js";
|
|
99
99
|
import {
|
|
100
100
|
getVercelConversationWorkQueue,
|
|
101
101
|
resolveConversationWorkQueueTopic,
|
|
@@ -113,7 +113,7 @@ import {
|
|
|
113
113
|
resolveSlackChannelTypeFromMessage,
|
|
114
114
|
resolveSlackConversationContext,
|
|
115
115
|
setConversationTitle
|
|
116
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-C2YBH4S6.js";
|
|
117
117
|
import {
|
|
118
118
|
abandonAgentTurnSessionRecord,
|
|
119
119
|
buildSlackOutputMessage,
|
|
@@ -129,11 +129,11 @@ import {
|
|
|
129
129
|
recordAuthorizationCompleted,
|
|
130
130
|
splitSlackReplyText,
|
|
131
131
|
truncateStatusText
|
|
132
|
-
} from "./chunk-
|
|
132
|
+
} from "./chunk-QDQVOMBA.js";
|
|
133
133
|
import {
|
|
134
134
|
validatePluginEgressCredentialHooks,
|
|
135
135
|
validatePluginRegistrations
|
|
136
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-KIDP757T.js";
|
|
137
137
|
import {
|
|
138
138
|
defineJuniorPlugins,
|
|
139
139
|
pluginCatalogConfigFromEnv,
|
|
@@ -142,26 +142,27 @@ import {
|
|
|
142
142
|
} from "./chunk-SG5WAA7H.js";
|
|
143
143
|
import {
|
|
144
144
|
bindSlackDirectCredentialSubject,
|
|
145
|
+
getPluginDashboardRoutes,
|
|
145
146
|
getPluginRoutes,
|
|
146
147
|
getPluginSlackConversationLink,
|
|
147
148
|
getPlugins,
|
|
148
149
|
setPlugins,
|
|
149
150
|
validatePlugins,
|
|
150
151
|
verifySlackDirectCredentialSubject
|
|
151
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-SSWBYEFH.js";
|
|
152
153
|
import {
|
|
153
154
|
createPluginLogger,
|
|
154
155
|
createPluginState
|
|
155
|
-
} from "./chunk-
|
|
156
|
+
} from "./chunk-RARSKPVT.js";
|
|
156
157
|
import {
|
|
157
158
|
getConversationStore,
|
|
158
159
|
getDb
|
|
159
|
-
} from "./chunk-
|
|
160
|
+
} from "./chunk-237T7XAN.js";
|
|
160
161
|
import "./chunk-G3E7SCME.js";
|
|
161
162
|
import {
|
|
162
|
-
|
|
163
|
+
acquireActiveLock,
|
|
163
164
|
getStateAdapter
|
|
164
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-LXTPBU4K.js";
|
|
165
166
|
import {
|
|
166
167
|
createSlackDestination,
|
|
167
168
|
destinationKey,
|
|
@@ -198,7 +199,7 @@ import {
|
|
|
198
199
|
} from "./chunk-T77LUIX3.js";
|
|
199
200
|
import {
|
|
200
201
|
discoverSkills
|
|
201
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-Y3EG7S7P.js";
|
|
202
203
|
import {
|
|
203
204
|
CredentialUnavailableError,
|
|
204
205
|
buildOAuthTokenRequest,
|
|
@@ -209,7 +210,7 @@ import {
|
|
|
209
210
|
isPluginProvider,
|
|
210
211
|
parseOAuthTokenResponse,
|
|
211
212
|
setPluginCatalogConfig
|
|
212
|
-
} from "./chunk-
|
|
213
|
+
} from "./chunk-YLVJRYTD.js";
|
|
213
214
|
import {
|
|
214
215
|
createRequester,
|
|
215
216
|
createSlackRequester,
|
|
@@ -247,8 +248,60 @@ import {
|
|
|
247
248
|
import "./chunk-MLKGABMK.js";
|
|
248
249
|
|
|
249
250
|
// src/app.ts
|
|
251
|
+
import { createRequire } from "module";
|
|
252
|
+
import { pathToFileURL } from "url";
|
|
250
253
|
import { Hono } from "hono";
|
|
251
254
|
|
|
255
|
+
// src/chat/slack/dashboard-link.ts
|
|
256
|
+
var dashboardConversationLinkOptions;
|
|
257
|
+
function withHttps(host) {
|
|
258
|
+
return /^https?:\/\//.test(host) ? host : `https://${host}`;
|
|
259
|
+
}
|
|
260
|
+
function stripTrailingSlashes(value) {
|
|
261
|
+
let end = value.length;
|
|
262
|
+
while (end > 1 && value.charCodeAt(end - 1) === 47) {
|
|
263
|
+
end -= 1;
|
|
264
|
+
}
|
|
265
|
+
return end === value.length ? value : value.slice(0, end);
|
|
266
|
+
}
|
|
267
|
+
function normalizeDashboardPath(path2, fallback) {
|
|
268
|
+
const value = path2?.trim() || fallback;
|
|
269
|
+
const withSlash = value.startsWith("/") ? value : `/${value}`;
|
|
270
|
+
return stripTrailingSlashes(withSlash);
|
|
271
|
+
}
|
|
272
|
+
function resolveDashboardBaseURL(config) {
|
|
273
|
+
const explicit = config.baseURL ?? process.env.BETTER_AUTH_URL ?? process.env.JUNIOR_BASE_URL;
|
|
274
|
+
if (explicit?.trim()) {
|
|
275
|
+
return stripTrailingSlashes(withHttps(explicit.trim()));
|
|
276
|
+
}
|
|
277
|
+
const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
278
|
+
if (vercelProd) {
|
|
279
|
+
return stripTrailingSlashes(withHttps(vercelProd));
|
|
280
|
+
}
|
|
281
|
+
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
282
|
+
if (vercelUrl) {
|
|
283
|
+
return stripTrailingSlashes(withHttps(vercelUrl));
|
|
284
|
+
}
|
|
285
|
+
return "http://localhost:3000";
|
|
286
|
+
}
|
|
287
|
+
function setDashboardConversationLinkOptions(options) {
|
|
288
|
+
const previous = dashboardConversationLinkOptions;
|
|
289
|
+
dashboardConversationLinkOptions = options?.disabled ? void 0 : options;
|
|
290
|
+
return previous;
|
|
291
|
+
}
|
|
292
|
+
function getDashboardConversationLink(conversationId) {
|
|
293
|
+
if (!dashboardConversationLinkOptions) {
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
const baseURL = resolveDashboardBaseURL(dashboardConversationLinkOptions);
|
|
297
|
+
const basePath = normalizeDashboardPath(
|
|
298
|
+
dashboardConversationLinkOptions.basePath,
|
|
299
|
+
"/"
|
|
300
|
+
);
|
|
301
|
+
const path2 = basePath === "/" ? `/conversations/${encodeURIComponent(conversationId)}` : `${basePath}/conversations/${encodeURIComponent(conversationId)}`;
|
|
302
|
+
return `${baseURL}${path2}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
252
305
|
// src/chat/slack/reply.ts
|
|
253
306
|
import { Buffer as Buffer2 } from "buffer";
|
|
254
307
|
|
|
@@ -267,7 +320,7 @@ function buildSlackReplyFooter(args) {
|
|
|
267
320
|
label: "ID",
|
|
268
321
|
value: conversationId
|
|
269
322
|
};
|
|
270
|
-
const conversationUrl = getPluginSlackConversationLink(conversationId)?.url ?? buildSentryConversationUrl(conversationId);
|
|
323
|
+
const conversationUrl = getPluginSlackConversationLink(conversationId)?.url ?? getDashboardConversationLink(conversationId) ?? buildSentryConversationUrl(conversationId);
|
|
271
324
|
if (conversationUrl) {
|
|
272
325
|
idItem.url = conversationUrl;
|
|
273
326
|
}
|
|
@@ -2790,7 +2843,7 @@ async function resumeSlackTurn(args) {
|
|
|
2790
2843
|
const stateAdapter = getStateAdapter();
|
|
2791
2844
|
await stateAdapter.connect();
|
|
2792
2845
|
const lockKey = args.lockKey ?? getDefaultLockKey(args.channelId, args.threadTs);
|
|
2793
|
-
const lock = await stateAdapter
|
|
2846
|
+
const lock = await acquireActiveLock(stateAdapter, lockKey);
|
|
2794
2847
|
if (!lock) {
|
|
2795
2848
|
throw new ResumeTurnBusyError(lockKey);
|
|
2796
2849
|
}
|
|
@@ -6241,6 +6294,14 @@ var IGNORED_MESSAGE_SUBTYPES = /* @__PURE__ */ new Set([
|
|
|
6241
6294
|
"ekm_access_denied",
|
|
6242
6295
|
"tombstone"
|
|
6243
6296
|
]);
|
|
6297
|
+
var SlackEventPersistenceError = class extends Error {
|
|
6298
|
+
cause;
|
|
6299
|
+
constructor(cause) {
|
|
6300
|
+
super("Slack event durable persistence failed");
|
|
6301
|
+
this.name = "SlackEventPersistenceError";
|
|
6302
|
+
this.cause = cause;
|
|
6303
|
+
}
|
|
6304
|
+
};
|
|
6244
6305
|
function enqueue(waitUntil, task) {
|
|
6245
6306
|
waitUntil(task);
|
|
6246
6307
|
}
|
|
@@ -6292,7 +6353,7 @@ async function buildThread(args) {
|
|
|
6292
6353
|
});
|
|
6293
6354
|
}
|
|
6294
6355
|
function shouldIgnoreMessage(message) {
|
|
6295
|
-
return message.author.isMe === true || isExternalSlackUser(message.raw);
|
|
6356
|
+
return message.author.isMe === true || !parseActorUserId(message.author.userId) || isExternalSlackUser(message.raw);
|
|
6296
6357
|
}
|
|
6297
6358
|
function shouldPersistBeforeAck(body) {
|
|
6298
6359
|
return body.event?.type === "app_mention" || body.event?.type === "message";
|
|
@@ -6312,6 +6373,8 @@ async function persistSlackMessage(args) {
|
|
|
6312
6373
|
conversationStore: args.conversationStore,
|
|
6313
6374
|
queue: args.queue,
|
|
6314
6375
|
state: args.state
|
|
6376
|
+
}).catch((error) => {
|
|
6377
|
+
throw new SlackEventPersistenceError(error);
|
|
6315
6378
|
});
|
|
6316
6379
|
}
|
|
6317
6380
|
async function routeParsedMessage(args) {
|
|
@@ -6660,9 +6723,16 @@ async function handleSlackWebhook(args) {
|
|
|
6660
6723
|
services: args.services
|
|
6661
6724
|
});
|
|
6662
6725
|
if (shouldPersistBeforeAck(parsed)) {
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
})
|
|
6726
|
+
try {
|
|
6727
|
+
await eventTask;
|
|
6728
|
+
} catch (error) {
|
|
6729
|
+
if (!(error instanceof SlackEventPersistenceError)) {
|
|
6730
|
+
logException(error, "slack_event_enqueue_failed");
|
|
6731
|
+
return new Response("ok", { status: 200 });
|
|
6732
|
+
}
|
|
6733
|
+
logException(error.cause, "slack_event_persist_failed");
|
|
6734
|
+
return new Response("Slack event persistence failed", { status: 503 });
|
|
6735
|
+
}
|
|
6666
6736
|
} else {
|
|
6667
6737
|
enqueue(
|
|
6668
6738
|
args.waitUntil,
|
|
@@ -11141,6 +11211,7 @@ function createProductionConversationWorkOptions(options) {
|
|
|
11141
11211
|
}
|
|
11142
11212
|
|
|
11143
11213
|
// src/app.ts
|
|
11214
|
+
var DASHBOARD_PACKAGE_NAME = "@sentry/junior-dashboard";
|
|
11144
11215
|
async function defaultWaitUntil() {
|
|
11145
11216
|
try {
|
|
11146
11217
|
const { waitUntil } = await import("@vercel/functions");
|
|
@@ -11159,6 +11230,8 @@ async function resolveVirtualConfig() {
|
|
|
11159
11230
|
try {
|
|
11160
11231
|
const mod = await import("#junior/config");
|
|
11161
11232
|
return {
|
|
11233
|
+
createDashboardApp: mod.createDashboardApp,
|
|
11234
|
+
dashboard: mod.dashboard,
|
|
11162
11235
|
pluginSet: mod.pluginSet,
|
|
11163
11236
|
plugins: mod.plugins,
|
|
11164
11237
|
pluginRuntimeRegistrations: mod.pluginRuntimeRegistrations ?? []
|
|
@@ -11221,7 +11294,161 @@ function validateBuildIncludesPluginRuntimeRegistrations(runtimeRegistrations, v
|
|
|
11221
11294
|
`createApp() is missing plugin registration(s) with runtime code bundled by juniorNitro(): ${missing.join(", ")}. Pass a runtime-safe plugin module to juniorNitro({ plugins: "./plugins" }) or pass the same defineJuniorPlugins(...) set to createApp({ plugins }).`
|
|
11222
11295
|
);
|
|
11223
11296
|
}
|
|
11224
|
-
function
|
|
11297
|
+
async function createDashboardRouteRegistrations(args) {
|
|
11298
|
+
if (!args.dashboard || args.dashboard.disabled) {
|
|
11299
|
+
return [];
|
|
11300
|
+
}
|
|
11301
|
+
const createDashboardApp = args.createDashboardApp ?? await loadDashboardAppFactory();
|
|
11302
|
+
return dashboardRouteRegistrations({
|
|
11303
|
+
dashboard: args.dashboard,
|
|
11304
|
+
createDashboardApp,
|
|
11305
|
+
pluginRoutes: args.pluginRoutes
|
|
11306
|
+
});
|
|
11307
|
+
}
|
|
11308
|
+
async function loadDashboardAppFactory() {
|
|
11309
|
+
try {
|
|
11310
|
+
const appRequire = createRequire(`${process.cwd()}/package.json`);
|
|
11311
|
+
const mod = await import(pathToFileURL(appRequire.resolve(DASHBOARD_PACKAGE_NAME)).href);
|
|
11312
|
+
return dashboardAppFactoryFromModule(mod);
|
|
11313
|
+
} catch (error) {
|
|
11314
|
+
if (isMissingDashboardPackage(error)) {
|
|
11315
|
+
throw new Error(
|
|
11316
|
+
'createApp({ dashboard }) requires installing "@sentry/junior-dashboard"',
|
|
11317
|
+
{ cause: error }
|
|
11318
|
+
);
|
|
11319
|
+
}
|
|
11320
|
+
throw error;
|
|
11321
|
+
}
|
|
11322
|
+
}
|
|
11323
|
+
function dashboardAppFactoryFromModule(mod) {
|
|
11324
|
+
if (!mod || typeof mod !== "object" || typeof mod.createDashboardApp !== "function") {
|
|
11325
|
+
throw new Error(
|
|
11326
|
+
'@sentry/junior-dashboard must export a "createDashboardApp" function'
|
|
11327
|
+
);
|
|
11328
|
+
}
|
|
11329
|
+
return mod.createDashboardApp;
|
|
11330
|
+
}
|
|
11331
|
+
function isMissingDashboardPackage(error) {
|
|
11332
|
+
if (!(error instanceof Error)) {
|
|
11333
|
+
return false;
|
|
11334
|
+
}
|
|
11335
|
+
const code = error.code;
|
|
11336
|
+
return (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") && error.message.includes("@sentry/junior-dashboard");
|
|
11337
|
+
}
|
|
11338
|
+
function stripTrailingSlashes2(value) {
|
|
11339
|
+
let end = value.length;
|
|
11340
|
+
while (end > 1 && value.charCodeAt(end - 1) === 47) {
|
|
11341
|
+
end -= 1;
|
|
11342
|
+
}
|
|
11343
|
+
return end === value.length ? value : value.slice(0, end);
|
|
11344
|
+
}
|
|
11345
|
+
function normalizeDashboardPath2(path2, fallback) {
|
|
11346
|
+
const value = path2?.trim() || fallback;
|
|
11347
|
+
const withSlash = value.startsWith("/") ? value : `/${value}`;
|
|
11348
|
+
return stripTrailingSlashes2(withSlash);
|
|
11349
|
+
}
|
|
11350
|
+
function dashboardHostRoutePaths(dashboard) {
|
|
11351
|
+
const basePath = normalizeDashboardPath2(dashboard.basePath, "/");
|
|
11352
|
+
const authPath = normalizeDashboardPath2(dashboard.authPath, "/api/auth");
|
|
11353
|
+
const pagePaths = basePath === "/" ? [
|
|
11354
|
+
"/",
|
|
11355
|
+
"/conversations",
|
|
11356
|
+
"/conversations/*",
|
|
11357
|
+
"/plugins",
|
|
11358
|
+
"/plugins/*",
|
|
11359
|
+
"/sessions",
|
|
11360
|
+
"/sessions/*"
|
|
11361
|
+
] : [basePath, `${basePath}/*`];
|
|
11362
|
+
return [
|
|
11363
|
+
...pagePaths,
|
|
11364
|
+
"/favicon.ico",
|
|
11365
|
+
"/api/dashboard",
|
|
11366
|
+
"/api/dashboard/*",
|
|
11367
|
+
authPath,
|
|
11368
|
+
`${authPath}/*`
|
|
11369
|
+
];
|
|
11370
|
+
}
|
|
11371
|
+
function routePrefixCoversPath(routePrefix, path2) {
|
|
11372
|
+
return routePrefix === "/" || path2 === routePrefix || path2.startsWith(`${routePrefix}/`);
|
|
11373
|
+
}
|
|
11374
|
+
function routeSegments(path2) {
|
|
11375
|
+
return normalizeDashboardPath2(path2, "/").split("/").filter(Boolean);
|
|
11376
|
+
}
|
|
11377
|
+
function routeSegmentMatches(pattern, value) {
|
|
11378
|
+
return pattern === value || pattern === "*" || pattern.startsWith(":");
|
|
11379
|
+
}
|
|
11380
|
+
function routePatternMatchesConcretePath(pattern, concretePath) {
|
|
11381
|
+
const patternSegments = routeSegments(pattern);
|
|
11382
|
+
const pathSegments = routeSegments(concretePath);
|
|
11383
|
+
for (let index = 0; index < patternSegments.length; index += 1) {
|
|
11384
|
+
const segment = patternSegments[index];
|
|
11385
|
+
if (segment === "**" || segment === "*") {
|
|
11386
|
+
return true;
|
|
11387
|
+
}
|
|
11388
|
+
const value = pathSegments[index];
|
|
11389
|
+
if (!value || !routeSegmentMatches(segment, value)) {
|
|
11390
|
+
return false;
|
|
11391
|
+
}
|
|
11392
|
+
}
|
|
11393
|
+
return patternSegments.length === pathSegments.length;
|
|
11394
|
+
}
|
|
11395
|
+
function routePatternExamples(routePath) {
|
|
11396
|
+
const normalized = normalizeDashboardPath2(routePath, "/");
|
|
11397
|
+
if (!normalized.endsWith("/*") && !normalized.endsWith("/**")) {
|
|
11398
|
+
return [normalized];
|
|
11399
|
+
}
|
|
11400
|
+
const prefix = normalizeDashboardPath2(
|
|
11401
|
+
normalized.endsWith("/*") ? normalized.slice(0, -2) : normalized.slice(0, -3),
|
|
11402
|
+
"/"
|
|
11403
|
+
);
|
|
11404
|
+
return [
|
|
11405
|
+
prefix,
|
|
11406
|
+
prefix === "/" ? "/__dashboard__" : `${prefix}/__dashboard__`
|
|
11407
|
+
];
|
|
11408
|
+
}
|
|
11409
|
+
function routePatternOverlaps(ownedPath, routePath) {
|
|
11410
|
+
if (ownedPath.endsWith("/*") && routePrefixCoversPath(ownedPath.slice(0, -2), routePath)) {
|
|
11411
|
+
return true;
|
|
11412
|
+
}
|
|
11413
|
+
return routePatternExamples(ownedPath).some(
|
|
11414
|
+
(example) => routePatternMatchesConcretePath(routePath, example)
|
|
11415
|
+
);
|
|
11416
|
+
}
|
|
11417
|
+
function dashboardOwnedRoutePath(routePath, dashboard) {
|
|
11418
|
+
return dashboardHostRoutePaths(dashboard).some(
|
|
11419
|
+
(path2) => routePatternOverlaps(path2, routePath)
|
|
11420
|
+
);
|
|
11421
|
+
}
|
|
11422
|
+
function dashboardRouteRegistrations(args) {
|
|
11423
|
+
let app;
|
|
11424
|
+
const fetch2 = (request) => {
|
|
11425
|
+
app ??= args.createDashboardApp({
|
|
11426
|
+
...args.dashboard,
|
|
11427
|
+
pluginRoutes: args.pluginRoutes
|
|
11428
|
+
});
|
|
11429
|
+
if (!app || typeof app.fetch !== "function") {
|
|
11430
|
+
throw new Error("createDashboardApp() must return an app with fetch()");
|
|
11431
|
+
}
|
|
11432
|
+
return app.fetch(request);
|
|
11433
|
+
};
|
|
11434
|
+
return dashboardHostRoutePaths(args.dashboard).map((path2) => ({
|
|
11435
|
+
handler: fetch2,
|
|
11436
|
+
path: path2
|
|
11437
|
+
}));
|
|
11438
|
+
}
|
|
11439
|
+
function validateDashboardRouteOwnership(args) {
|
|
11440
|
+
if (!args.dashboard || args.dashboard.disabled) {
|
|
11441
|
+
return;
|
|
11442
|
+
}
|
|
11443
|
+
for (const route of args.routes) {
|
|
11444
|
+
if (dashboardOwnedRoutePath(route.path, args.dashboard)) {
|
|
11445
|
+
throw new Error(
|
|
11446
|
+
`Plugin "${route.pluginName}" route "${route.path}" conflicts with core dashboard routes`
|
|
11447
|
+
);
|
|
11448
|
+
}
|
|
11449
|
+
}
|
|
11450
|
+
}
|
|
11451
|
+
function mountRoutes(app, routes) {
|
|
11225
11452
|
for (const route of routes) {
|
|
11226
11453
|
const handler = (c) => route.handler(c.req.raw);
|
|
11227
11454
|
const methods = Array.isArray(route.method) ? route.method : [route.method ?? "ALL"];
|
|
@@ -11237,6 +11464,7 @@ function mountPluginRoutes(app, routes) {
|
|
|
11237
11464
|
}
|
|
11238
11465
|
async function createApp(options) {
|
|
11239
11466
|
const virtualConfig = await resolveVirtualConfig();
|
|
11467
|
+
const dashboard = options?.dashboard ?? virtualConfig?.dashboard;
|
|
11240
11468
|
const configuredPlugins = options?.plugins ?? virtualConfig?.pluginSet;
|
|
11241
11469
|
const plugins = pluginRuntimeRegistrationsFromPluginSet(configuredPlugins);
|
|
11242
11470
|
const pluginConfig = configuredPlugins ? pluginCatalogConfigFromPluginSet(configuredPlugins) : virtualConfig?.plugins ?? pluginCatalogConfigFromEnv();
|
|
@@ -11251,7 +11479,9 @@ async function createApp(options) {
|
|
|
11251
11479
|
const previousPlugins = setPlugins(plugins);
|
|
11252
11480
|
const previousConfigDefaults = getConfigDefaults();
|
|
11253
11481
|
const previousSlackReactionConfig = getSlackReactionConfig();
|
|
11482
|
+
const previousDashboardLinkOptions = setDashboardConversationLinkOptions(dashboard);
|
|
11254
11483
|
let pluginRoutes = [];
|
|
11484
|
+
let pluginDashboardRoutes = [];
|
|
11255
11485
|
let sandboxEgressTracePropagationDomains = [];
|
|
11256
11486
|
try {
|
|
11257
11487
|
sandboxEgressTracePropagationDomains = normalizeSandboxEgressTracePropagationDomains(
|
|
@@ -11269,11 +11499,16 @@ async function createApp(options) {
|
|
|
11269
11499
|
);
|
|
11270
11500
|
}
|
|
11271
11501
|
pluginRoutes = getPluginRoutes();
|
|
11502
|
+
validateDashboardRouteOwnership({ dashboard, routes: pluginRoutes });
|
|
11503
|
+
if (dashboard && !dashboard.disabled) {
|
|
11504
|
+
pluginDashboardRoutes = getPluginDashboardRoutes();
|
|
11505
|
+
}
|
|
11272
11506
|
} catch (error) {
|
|
11273
11507
|
setPluginCatalogConfig(previousPluginCatalogConfig);
|
|
11274
11508
|
setPlugins(previousPlugins);
|
|
11275
11509
|
setConfigDefaults(previousConfigDefaults);
|
|
11276
11510
|
setSlackReactionConfig(previousSlackReactionConfig);
|
|
11511
|
+
setDashboardConversationLinkOptions(previousDashboardLinkOptions);
|
|
11277
11512
|
throw error;
|
|
11278
11513
|
}
|
|
11279
11514
|
const waitUntil = options?.waitUntil ?? await defaultWaitUntil();
|
|
@@ -11301,7 +11536,15 @@ async function createApp(options) {
|
|
|
11301
11536
|
next
|
|
11302
11537
|
);
|
|
11303
11538
|
});
|
|
11304
|
-
|
|
11539
|
+
mountRoutes(app, pluginRoutes);
|
|
11540
|
+
mountRoutes(
|
|
11541
|
+
app,
|
|
11542
|
+
await createDashboardRouteRegistrations({
|
|
11543
|
+
dashboard,
|
|
11544
|
+
createDashboardApp: virtualConfig?.createDashboardApp,
|
|
11545
|
+
pluginRoutes: pluginDashboardRoutes
|
|
11546
|
+
})
|
|
11547
|
+
);
|
|
11305
11548
|
app.get("/", () => GET());
|
|
11306
11549
|
app.get("/health", () => GET());
|
|
11307
11550
|
app.get("/api/oauth/callback/mcp/:provider", (c) => {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/** Copy app and declared plugin package content into the server output. */
|
|
2
2
|
export declare function copyAppAndPluginContent(cwd: string, serverRoot: string, packageNames?: unknown): void;
|
|
3
|
+
/** Copy dashboard browser assets when core dashboard routes are enabled. */
|
|
4
|
+
export declare function copyDashboardAssets(cwd: string, serverRoot: string): void;
|
|
3
5
|
/** Copy extra file patterns into server output for files the bundler cannot trace. */
|
|
4
6
|
export declare function copyIncludedFiles(cwd: string, serverRoot: string, patterns?: unknown): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Nitro } from "nitro/types";
|
|
2
2
|
import type { PluginCatalogConfig } from "@/chat/plugins/types";
|
|
3
|
+
import type { JuniorDashboardOptions } from "@/app";
|
|
3
4
|
import { type JuniorPluginSet } from "@/plugins";
|
|
4
5
|
export interface RuntimePluginModule {
|
|
5
6
|
exportName: string;
|
|
@@ -7,6 +8,7 @@ export interface RuntimePluginModule {
|
|
|
7
8
|
}
|
|
8
9
|
/** Render the virtual config module consumed by createApp(). */
|
|
9
10
|
export declare function renderVirtualConfig(options: {
|
|
11
|
+
dashboard?: Omit<JuniorDashboardOptions, "reporting">;
|
|
10
12
|
plugins?: PluginCatalogConfig;
|
|
11
13
|
pluginModule?: RuntimePluginModule;
|
|
12
14
|
pluginRuntimeRegistrations?: string[];
|
|
@@ -17,4 +19,5 @@ export declare function injectVirtualConfig(nitro: Nitro, options?: {
|
|
|
17
19
|
pluginModule?: RuntimePluginModule;
|
|
18
20
|
plugins?: PluginCatalogConfig;
|
|
19
21
|
pluginRuntimeRegistrations?: string[];
|
|
22
|
+
dashboard?: Omit<JuniorDashboardOptions, "reporting">;
|
|
20
23
|
}): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Destination } from "@sentry/junior-plugin-api";
|
|
2
2
|
import type { StoredSlackRequester } from "@/chat/requester";
|
|
3
3
|
export type ConversationSource = "api" | "internal" | "local" | "plugin" | "scheduler" | "slack";
|
|
4
|
-
export type ConversationStatus = "awaiting_resume" | "idle" | "pending" | "running";
|
|
4
|
+
export type ConversationStatus = "awaiting_resume" | "failed" | "idle" | "pending" | "running";
|
|
5
5
|
export interface ConversationExecution {
|
|
6
6
|
lastCheckpointAtMs?: number;
|
|
7
7
|
lastEnqueuedAtMs?: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PluginConversations, PluginRoute, PluginOperationalReport, SlackConversationLink, PluginRegistration } from "@sentry/junior-plugin-api";
|
|
1
|
+
import type { PluginConversations, PluginRoute, PluginOperationalReport, PluginRouteApp, SlackConversationLink, PluginRegistration } from "@sentry/junior-plugin-api";
|
|
2
2
|
import type { PluginPromptContributionContext } from "@/chat/plugins/prompt";
|
|
3
3
|
import type { ToolDefinition } from "@/chat/tools/definition";
|
|
4
4
|
import type { ToolRuntimeContext } from "@/chat/tools/types";
|
|
@@ -19,6 +19,10 @@ export interface ToolHookResult {
|
|
|
19
19
|
export interface PluginRouteRegistration extends PluginRoute {
|
|
20
20
|
pluginName: string;
|
|
21
21
|
}
|
|
22
|
+
export interface PluginDashboardRouteRegistration {
|
|
23
|
+
app: PluginRouteApp;
|
|
24
|
+
pluginName: string;
|
|
25
|
+
}
|
|
22
26
|
export interface PluginHookRunner {
|
|
23
27
|
beforeToolExecute(input: ToolHookInput): Promise<ToolHookResult>;
|
|
24
28
|
prepareSandbox(sandbox: SandboxInstance): Promise<void>;
|
|
@@ -39,6 +43,8 @@ export declare function getPluginUserPromptContributions(args: {
|
|
|
39
43
|
export declare function getPluginTools(context: ToolRuntimeContext): Record<string, ToolDefinition<any>>;
|
|
40
44
|
/** Collect route handlers exposed by plugins for app-level mounting. */
|
|
41
45
|
export declare function getPluginRoutes(): PluginRouteRegistration[];
|
|
46
|
+
/** Collect dashboard-scoped route apps exposed by plugins. */
|
|
47
|
+
export declare function getPluginDashboardRoutes(): PluginDashboardRouteRegistration[];
|
|
42
48
|
/** Resolve the first plugin conversation URL for finalized Slack footers. */
|
|
43
49
|
export declare function getPluginSlackConversationLink(conversationId: string): SlackConversationLink | undefined;
|
|
44
50
|
/** Collect read-only operational summaries exposed by plugins. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface DashboardConversationLinkOptions {
|
|
2
|
+
basePath?: string;
|
|
3
|
+
baseURL?: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/** Configure core dashboard links used in Slack footers. */
|
|
7
|
+
export declare function setDashboardConversationLinkOptions(options: DashboardConversationLinkOptions | undefined): DashboardConversationLinkOptions | undefined;
|
|
8
|
+
/** Build the dashboard conversation URL when the core dashboard is enabled. */
|
|
9
|
+
export declare function getDashboardConversationLink(conversationId: string): string | undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RedisStateAdapter } from "@chat-adapter/state-redis";
|
|
2
2
|
import type { StateAdapter } from "chat";
|
|
3
|
-
export declare const ACTIVE_LOCK_TTL_MS = 90000;
|
|
4
3
|
export declare function getConnectedStateContext(): Promise<{
|
|
5
4
|
redisStateAdapter?: RedisStateAdapter;
|
|
6
5
|
stateAdapter: StateAdapter;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Lock, StateAdapter } from "chat";
|
|
2
|
+
export declare const ACTIVE_LOCK_TTL_MS = 90000;
|
|
3
|
+
/**
|
|
4
|
+
* Acquire a lock for long-running work that the queued state adapter should
|
|
5
|
+
* keep alive while the owning invocation is still making progress.
|
|
6
|
+
*/
|
|
7
|
+
export declare function acquireActiveLock(state: StateAdapter, threadId: string): Promise<Lock | null>;
|
|
@@ -7,7 +7,7 @@ export declare const CONVERSATION_WORK_LEASE_TTL_MS = 90000;
|
|
|
7
7
|
export declare const CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15000;
|
|
8
8
|
export declare const CONVERSATION_WORK_STALE_ENQUEUE_MS = 60000;
|
|
9
9
|
export type Source = "api" | "internal" | "local" | "plugin" | "scheduler" | "slack";
|
|
10
|
-
export type ExecutionStatus = "awaiting_resume" | "idle" | "pending" | "running";
|
|
10
|
+
export type ExecutionStatus = "awaiting_resume" | "failed" | "idle" | "pending" | "running";
|
|
11
11
|
export interface AgentInput {
|
|
12
12
|
attachments?: unknown[];
|
|
13
13
|
authorId?: string;
|
|
@@ -127,6 +127,26 @@ export declare function recordConversationActivity(args: {
|
|
|
127
127
|
state?: StateAdapter;
|
|
128
128
|
title?: string;
|
|
129
129
|
}): Promise<void>;
|
|
130
|
+
/** Store task-execution metadata for local/no-SQL reporting. */
|
|
131
|
+
export declare function recordConversationExecution(args: {
|
|
132
|
+
channelName?: string;
|
|
133
|
+
conversationId: string;
|
|
134
|
+
createdAtMs: number;
|
|
135
|
+
destination?: Destination;
|
|
136
|
+
execution: {
|
|
137
|
+
lastCheckpointAtMs?: number;
|
|
138
|
+
lastEnqueuedAtMs?: number;
|
|
139
|
+
runId?: string;
|
|
140
|
+
status: ExecutionStatus;
|
|
141
|
+
updatedAtMs?: number;
|
|
142
|
+
};
|
|
143
|
+
lastActivityAtMs: number;
|
|
144
|
+
requester?: StoredSlackRequester;
|
|
145
|
+
source?: Source;
|
|
146
|
+
state?: StateAdapter;
|
|
147
|
+
title?: string;
|
|
148
|
+
updatedAtMs: number;
|
|
149
|
+
}): Promise<void>;
|
|
130
150
|
/** Record that a wake-up nudge was accepted for the conversation. */
|
|
131
151
|
export declare function markConversationWorkEnqueued(args: {
|
|
132
152
|
conversationId: string;
|