@sentry/junior 0.77.0 → 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-7P2WSR4R.js → agent-hooks-OFDNZJB2.js} +4 -2
- package/dist/api-reference.d.ts +2 -2
- package/dist/app.d.ts +27 -0
- package/dist/app.js +234 -8
- 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/task-execution/state.d.ts +21 -1
- package/dist/{chunk-NYKJ3KON.js → chunk-237T7XAN.js} +3 -3
- package/dist/{chunk-W36B5PT4.js → chunk-2MSW5BZY.js} +3 -3
- package/dist/{chunk-KPL4WJWA.js → chunk-LUNMJQ7D.js} +48 -2
- package/dist/{chunk-TO3UAY2M.js → chunk-QDQVOMBA.js} +20 -1
- package/dist/{chunk-WBSGTHNO.js → chunk-SSWBYEFH.js} +25 -1
- package/dist/cli/chat.js +4 -4
- package/dist/cli/plugins.js +2 -2
- package/dist/cli/upgrade.js +4 -4
- 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/reporting.js +8 -19
- package/dist/{runner-JWLZI3EX.js → runner-WW4GJFUB.js} +4 -4
- package/package.json +4 -4
|
@@ -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,9 +11,9 @@ import {
|
|
|
10
11
|
getPlugins,
|
|
11
12
|
setPlugins,
|
|
12
13
|
validatePlugins
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-SSWBYEFH.js";
|
|
14
15
|
import "./chunk-RARSKPVT.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-237T7XAN.js";
|
|
16
17
|
import "./chunk-G3E7SCME.js";
|
|
17
18
|
import "./chunk-LXTPBU4K.js";
|
|
18
19
|
import "./chunk-Q6XFTRV5.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,7 +90,7 @@ 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
|
|
@@ -129,7 +129,7 @@ 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
|
|
@@ -142,13 +142,14 @@ 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
|
|
@@ -156,7 +157,7 @@ import {
|
|
|
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,
|
|
@@ -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
|
}
|
|
@@ -11158,6 +11211,7 @@ function createProductionConversationWorkOptions(options) {
|
|
|
11158
11211
|
}
|
|
11159
11212
|
|
|
11160
11213
|
// src/app.ts
|
|
11214
|
+
var DASHBOARD_PACKAGE_NAME = "@sentry/junior-dashboard";
|
|
11161
11215
|
async function defaultWaitUntil() {
|
|
11162
11216
|
try {
|
|
11163
11217
|
const { waitUntil } = await import("@vercel/functions");
|
|
@@ -11176,6 +11230,8 @@ async function resolveVirtualConfig() {
|
|
|
11176
11230
|
try {
|
|
11177
11231
|
const mod = await import("#junior/config");
|
|
11178
11232
|
return {
|
|
11233
|
+
createDashboardApp: mod.createDashboardApp,
|
|
11234
|
+
dashboard: mod.dashboard,
|
|
11179
11235
|
pluginSet: mod.pluginSet,
|
|
11180
11236
|
plugins: mod.plugins,
|
|
11181
11237
|
pluginRuntimeRegistrations: mod.pluginRuntimeRegistrations ?? []
|
|
@@ -11238,7 +11294,161 @@ function validateBuildIncludesPluginRuntimeRegistrations(runtimeRegistrations, v
|
|
|
11238
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 }).`
|
|
11239
11295
|
);
|
|
11240
11296
|
}
|
|
11241
|
-
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) {
|
|
11242
11452
|
for (const route of routes) {
|
|
11243
11453
|
const handler = (c) => route.handler(c.req.raw);
|
|
11244
11454
|
const methods = Array.isArray(route.method) ? route.method : [route.method ?? "ALL"];
|
|
@@ -11254,6 +11464,7 @@ function mountPluginRoutes(app, routes) {
|
|
|
11254
11464
|
}
|
|
11255
11465
|
async function createApp(options) {
|
|
11256
11466
|
const virtualConfig = await resolveVirtualConfig();
|
|
11467
|
+
const dashboard = options?.dashboard ?? virtualConfig?.dashboard;
|
|
11257
11468
|
const configuredPlugins = options?.plugins ?? virtualConfig?.pluginSet;
|
|
11258
11469
|
const plugins = pluginRuntimeRegistrationsFromPluginSet(configuredPlugins);
|
|
11259
11470
|
const pluginConfig = configuredPlugins ? pluginCatalogConfigFromPluginSet(configuredPlugins) : virtualConfig?.plugins ?? pluginCatalogConfigFromEnv();
|
|
@@ -11268,7 +11479,9 @@ async function createApp(options) {
|
|
|
11268
11479
|
const previousPlugins = setPlugins(plugins);
|
|
11269
11480
|
const previousConfigDefaults = getConfigDefaults();
|
|
11270
11481
|
const previousSlackReactionConfig = getSlackReactionConfig();
|
|
11482
|
+
const previousDashboardLinkOptions = setDashboardConversationLinkOptions(dashboard);
|
|
11271
11483
|
let pluginRoutes = [];
|
|
11484
|
+
let pluginDashboardRoutes = [];
|
|
11272
11485
|
let sandboxEgressTracePropagationDomains = [];
|
|
11273
11486
|
try {
|
|
11274
11487
|
sandboxEgressTracePropagationDomains = normalizeSandboxEgressTracePropagationDomains(
|
|
@@ -11286,11 +11499,16 @@ async function createApp(options) {
|
|
|
11286
11499
|
);
|
|
11287
11500
|
}
|
|
11288
11501
|
pluginRoutes = getPluginRoutes();
|
|
11502
|
+
validateDashboardRouteOwnership({ dashboard, routes: pluginRoutes });
|
|
11503
|
+
if (dashboard && !dashboard.disabled) {
|
|
11504
|
+
pluginDashboardRoutes = getPluginDashboardRoutes();
|
|
11505
|
+
}
|
|
11289
11506
|
} catch (error) {
|
|
11290
11507
|
setPluginCatalogConfig(previousPluginCatalogConfig);
|
|
11291
11508
|
setPlugins(previousPlugins);
|
|
11292
11509
|
setConfigDefaults(previousConfigDefaults);
|
|
11293
11510
|
setSlackReactionConfig(previousSlackReactionConfig);
|
|
11511
|
+
setDashboardConversationLinkOptions(previousDashboardLinkOptions);
|
|
11294
11512
|
throw error;
|
|
11295
11513
|
}
|
|
11296
11514
|
const waitUntil = options?.waitUntil ?? await defaultWaitUntil();
|
|
@@ -11318,7 +11536,15 @@ async function createApp(options) {
|
|
|
11318
11536
|
next
|
|
11319
11537
|
);
|
|
11320
11538
|
});
|
|
11321
|
-
|
|
11539
|
+
mountRoutes(app, pluginRoutes);
|
|
11540
|
+
mountRoutes(
|
|
11541
|
+
app,
|
|
11542
|
+
await createDashboardRouteRegistrations({
|
|
11543
|
+
dashboard,
|
|
11544
|
+
createDashboardApp: virtualConfig?.createDashboardApp,
|
|
11545
|
+
pluginRoutes: pluginDashboardRoutes
|
|
11546
|
+
})
|
|
11547
|
+
);
|
|
11322
11548
|
app.get("/", () => GET());
|
|
11323
11549
|
app.get("/health", () => GET());
|
|
11324
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;
|
|
@@ -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;
|
|
@@ -449,7 +449,7 @@ function destinationUpsertFromDestination(args) {
|
|
|
449
449
|
};
|
|
450
450
|
}
|
|
451
451
|
function executionStatusFromValue(value) {
|
|
452
|
-
if (value === "awaiting_resume" || value === "idle" || value === "pending" || value === "running") {
|
|
452
|
+
if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
|
|
453
453
|
return value;
|
|
454
454
|
}
|
|
455
455
|
throw new Error("Conversation record execution status is invalid");
|
|
@@ -727,8 +727,8 @@ var SqlStore = class {
|
|
|
727
727
|
executionUpdatedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
|
|
728
728
|
executionStatus: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
|
|
729
729
|
runId: sql2`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
|
|
730
|
-
lastCheckpointAt: sql2`case when ${incomingExecutionIsFresh} then excluded.last_checkpoint_at else ${juniorConversations.lastCheckpointAt} end`,
|
|
731
|
-
lastEnqueuedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.last_enqueued_at else ${juniorConversations.lastEnqueuedAt} end`
|
|
730
|
+
lastCheckpointAt: sql2`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_checkpoint_at, ${juniorConversations.lastCheckpointAt}) else ${juniorConversations.lastCheckpointAt} end`,
|
|
731
|
+
lastEnqueuedAt: sql2`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_enqueued_at, ${juniorConversations.lastEnqueuedAt}) else ${juniorConversations.lastEnqueuedAt} end`
|
|
732
732
|
}
|
|
733
733
|
});
|
|
734
734
|
}
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
recordAuthorizationRequested,
|
|
29
29
|
recordMcpProviderConnected,
|
|
30
30
|
upsertAgentTurnSessionRecord
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-QDQVOMBA.js";
|
|
32
32
|
import {
|
|
33
33
|
createPluginEmbedder,
|
|
34
34
|
createPluginHookRunner,
|
|
@@ -39,14 +39,14 @@ import {
|
|
|
39
39
|
getPlugins,
|
|
40
40
|
getSlackToolContext,
|
|
41
41
|
resolveChannelCapabilities
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-SSWBYEFH.js";
|
|
43
43
|
import {
|
|
44
44
|
createPluginLogger,
|
|
45
45
|
createPluginState
|
|
46
46
|
} from "./chunk-RARSKPVT.js";
|
|
47
47
|
import {
|
|
48
48
|
getDb
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-237T7XAN.js";
|
|
50
50
|
import {
|
|
51
51
|
SANDBOX_DATA_ROOT,
|
|
52
52
|
SANDBOX_SKILLS_ROOT,
|
|
@@ -74,7 +74,7 @@ function normalizeSource(value) {
|
|
|
74
74
|
return void 0;
|
|
75
75
|
}
|
|
76
76
|
function normalizeExecutionStatus(value) {
|
|
77
|
-
if (value === "awaiting_resume" || value === "idle" || value === "pending" || value === "running") {
|
|
77
|
+
if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
|
|
78
78
|
return value;
|
|
79
79
|
}
|
|
80
80
|
return void 0;
|
|
@@ -231,8 +231,11 @@ function isLeaseActive(lease, nowMs) {
|
|
|
231
231
|
function pendingMessages(conversation) {
|
|
232
232
|
return [...conversation.execution.pendingMessages].sort(compareMessages);
|
|
233
233
|
}
|
|
234
|
+
function isRunnableStatus(status) {
|
|
235
|
+
return status !== "failed" && status !== "idle";
|
|
236
|
+
}
|
|
234
237
|
function hasRunnableWork(conversation) {
|
|
235
|
-
return conversation.execution.status
|
|
238
|
+
return isRunnableStatus(conversation.execution.status) || pendingMessages(conversation).length > 0;
|
|
236
239
|
}
|
|
237
240
|
function executionWithPendingMessages(execution, pending) {
|
|
238
241
|
const pendingMessages2 = [...pending].sort(compareMessages);
|
|
@@ -736,6 +739,48 @@ async function recordConversationActivity(args) {
|
|
|
736
739
|
});
|
|
737
740
|
});
|
|
738
741
|
}
|
|
742
|
+
async function recordConversationExecution(args) {
|
|
743
|
+
const nowMs = args.updatedAtMs;
|
|
744
|
+
await withConversationMutation(args, async (state) => {
|
|
745
|
+
const existing = await readConversation(state, args.conversationId);
|
|
746
|
+
if (existing && args.destination) {
|
|
747
|
+
assertSameConversationDestination({
|
|
748
|
+
conversationId: args.conversationId,
|
|
749
|
+
current: existing.destination,
|
|
750
|
+
next: args.destination
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
const current = existing ?? emptyConversation({
|
|
754
|
+
conversationId: args.conversationId,
|
|
755
|
+
destination: args.destination,
|
|
756
|
+
nowMs,
|
|
757
|
+
source: args.source
|
|
758
|
+
});
|
|
759
|
+
await writeConversation(
|
|
760
|
+
state,
|
|
761
|
+
withExecutionUpdate(
|
|
762
|
+
{
|
|
763
|
+
...current,
|
|
764
|
+
...current.destination ?? args.destination ? { destination: current.destination ?? args.destination } : {},
|
|
765
|
+
...current.source ?? args.source ? { source: current.source ?? args.source } : {},
|
|
766
|
+
...current.channelName ?? args.channelName ? { channelName: current.channelName ?? args.channelName } : {},
|
|
767
|
+
...current.requester ?? args.requester ? { requester: current.requester ?? args.requester } : {},
|
|
768
|
+
...current.title ?? args.title ? { title: current.title ?? args.title } : {},
|
|
769
|
+
createdAtMs: Math.min(current.createdAtMs, args.createdAtMs),
|
|
770
|
+
lastActivityAtMs: Math.max(
|
|
771
|
+
current.lastActivityAtMs,
|
|
772
|
+
args.lastActivityAtMs
|
|
773
|
+
)
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
...current.execution,
|
|
777
|
+
...args.execution
|
|
778
|
+
},
|
|
779
|
+
nowMs
|
|
780
|
+
)
|
|
781
|
+
);
|
|
782
|
+
});
|
|
783
|
+
}
|
|
739
784
|
async function markConversationWorkEnqueued(args) {
|
|
740
785
|
const nowMs = args.nowMs ?? now();
|
|
741
786
|
await withConversationMutation(args, async (state) => {
|
|
@@ -1061,6 +1106,7 @@ export {
|
|
|
1061
1106
|
appendInboundMessage,
|
|
1062
1107
|
requestConversationWork,
|
|
1063
1108
|
recordConversationActivity,
|
|
1109
|
+
recordConversationExecution,
|
|
1064
1110
|
markConversationWorkEnqueued,
|
|
1065
1111
|
startConversationWork,
|
|
1066
1112
|
checkInConversationWork,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getConversationStore
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-237T7XAN.js";
|
|
4
4
|
import {
|
|
5
5
|
SANDBOX_DATA_ROOT,
|
|
6
6
|
SANDBOX_WORKSPACE_ROOT,
|
|
@@ -1567,6 +1567,14 @@ function parseAgentTurnSessionStatus(parsed) {
|
|
|
1567
1567
|
function parseAgentTurnSurface(value) {
|
|
1568
1568
|
return value === "slack" || value === "api" || value === "scheduler" || value === "internal" ? value : void 0;
|
|
1569
1569
|
}
|
|
1570
|
+
function conversationExecutionFromSummary(summary) {
|
|
1571
|
+
const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
|
|
1572
|
+
return {
|
|
1573
|
+
status,
|
|
1574
|
+
runId: summary.sessionId,
|
|
1575
|
+
updatedAtMs: summary.updatedAtMs
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1570
1578
|
function parseSource(value) {
|
|
1571
1579
|
const result = sourceSchema.safeParse(value);
|
|
1572
1580
|
return result.success ? result.data : void 0;
|
|
@@ -1690,6 +1698,17 @@ async function recordConversationActivityMetadata(args) {
|
|
|
1690
1698
|
requester: sessionLogRequester(args.summary.requester),
|
|
1691
1699
|
source
|
|
1692
1700
|
});
|
|
1701
|
+
await conversationStore.recordExecution({
|
|
1702
|
+
channelName: args.summary.channelName,
|
|
1703
|
+
conversationId: args.summary.conversationId,
|
|
1704
|
+
createdAtMs: args.summary.startedAtMs,
|
|
1705
|
+
destination: args.summary.destination,
|
|
1706
|
+
execution: conversationExecutionFromSummary(args.summary),
|
|
1707
|
+
lastActivityAtMs: args.summary.updatedAtMs,
|
|
1708
|
+
requester: sessionLogRequester(args.summary.requester),
|
|
1709
|
+
source,
|
|
1710
|
+
updatedAtMs: args.nowMs
|
|
1711
|
+
});
|
|
1693
1712
|
} catch (error) {
|
|
1694
1713
|
logWarn(
|
|
1695
1714
|
"conversation_activity_metadata_update_failed",
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-RARSKPVT.js";
|
|
5
5
|
import {
|
|
6
6
|
getDb
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-237T7XAN.js";
|
|
8
8
|
import {
|
|
9
9
|
SANDBOX_WORKSPACE_ROOT
|
|
10
10
|
} from "./chunk-G3E7SCME.js";
|
|
@@ -606,6 +606,29 @@ function getPluginRoutes() {
|
|
|
606
606
|
}
|
|
607
607
|
return routes;
|
|
608
608
|
}
|
|
609
|
+
function getPluginDashboardRoutes() {
|
|
610
|
+
const routes = [];
|
|
611
|
+
for (const plugin of getPlugins()) {
|
|
612
|
+
const pluginName = plugin.manifest.name;
|
|
613
|
+
const hook = plugin.hooks?.dashboardRoutes;
|
|
614
|
+
if (!hook) {
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
const app = hook({
|
|
618
|
+
...basePluginContext(plugin)
|
|
619
|
+
});
|
|
620
|
+
if (app === void 0) {
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
if (!isRecord(app) || typeof app.fetch !== "function") {
|
|
624
|
+
throw new Error(
|
|
625
|
+
`Plugin dashboardRoutes hook from plugin "${pluginName}" must return a fetch-compatible app`
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
routes.push({ app, pluginName });
|
|
629
|
+
}
|
|
630
|
+
return routes;
|
|
631
|
+
}
|
|
609
632
|
function trustedSlackConversationUrl(pluginName, link) {
|
|
610
633
|
const url = typeof link?.url === "string" ? link.url.trim() : "";
|
|
611
634
|
if (!url) {
|
|
@@ -958,6 +981,7 @@ export {
|
|
|
958
981
|
getPluginUserPromptContributions,
|
|
959
982
|
getPluginTools,
|
|
960
983
|
getPluginRoutes,
|
|
984
|
+
getPluginDashboardRoutes,
|
|
961
985
|
getPluginSlackConversationLink,
|
|
962
986
|
getPluginOperationalReports,
|
|
963
987
|
createPluginHookRunner
|
package/dist/cli/chat.js
CHANGED
|
@@ -130,10 +130,10 @@ async function configureLocalChatPlugins(pluginSet) {
|
|
|
130
130
|
databaseModule
|
|
131
131
|
] = await Promise.all([
|
|
132
132
|
import("../plugins-PZMDS7AT.js"),
|
|
133
|
-
import("../agent-hooks-
|
|
133
|
+
import("../agent-hooks-OFDNZJB2.js"),
|
|
134
134
|
import("../registry-RRIDPJBT.js"),
|
|
135
135
|
import("../validation-MDMYBRFB.js"),
|
|
136
|
-
import("../db-
|
|
136
|
+
import("../db-NGQ3JCMF.js")
|
|
137
137
|
]);
|
|
138
138
|
const resolvedPluginSet = pluginSet === void 0 ? await loadLocalPluginSet() : pluginSet ?? void 0;
|
|
139
139
|
const plugins = pluginsModule.pluginRuntimeRegistrationsFromPluginSet(resolvedPluginSet);
|
|
@@ -193,7 +193,7 @@ async function runPrompt(options, io, pluginSet) {
|
|
|
193
193
|
defaultStateAdapterForLocalChat();
|
|
194
194
|
await configureLocalChatPlugins(pluginSet);
|
|
195
195
|
const conversationId = newRunConversationId();
|
|
196
|
-
const { runLocalAgentTurn } = await import("../runner-
|
|
196
|
+
const { runLocalAgentTurn } = await import("../runner-WW4GJFUB.js");
|
|
197
197
|
const result = await runLocalAgentTurn(
|
|
198
198
|
{
|
|
199
199
|
conversationId,
|
|
@@ -217,7 +217,7 @@ async function runInteractive(io, pluginSet) {
|
|
|
217
217
|
defaultStateAdapterForLocalChat();
|
|
218
218
|
await configureLocalChatPlugins(pluginSet);
|
|
219
219
|
const conversationId = newRunConversationId();
|
|
220
|
-
const { runLocalAgentTurn } = await import("../runner-
|
|
220
|
+
const { runLocalAgentTurn } = await import("../runner-WW4GJFUB.js");
|
|
221
221
|
const rl = readline.createInterface({
|
|
222
222
|
input: io.input,
|
|
223
223
|
output: io.output,
|
package/dist/cli/plugins.js
CHANGED
|
@@ -10,13 +10,13 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
setPlugins,
|
|
12
12
|
validatePlugins
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-SSWBYEFH.js";
|
|
14
14
|
import {
|
|
15
15
|
createPluginLogger
|
|
16
16
|
} from "../chunk-RARSKPVT.js";
|
|
17
17
|
import {
|
|
18
18
|
getDb
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-237T7XAN.js";
|
|
20
20
|
import "../chunk-G3E7SCME.js";
|
|
21
21
|
import "../chunk-LXTPBU4K.js";
|
|
22
22
|
import "../chunk-Q6XFTRV5.js";
|
package/dist/cli/upgrade.js
CHANGED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
getConversation,
|
|
3
3
|
listConversationsByActivity,
|
|
4
4
|
recordConversationActivity,
|
|
5
|
+
recordConversationExecution,
|
|
5
6
|
requestConversationWork
|
|
6
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-LUNMJQ7D.js";
|
|
7
8
|
import {
|
|
8
9
|
JUNIOR_THREAD_STATE_TTL_MS,
|
|
9
10
|
coerceThreadConversationState
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
createJuniorSqlExecutor,
|
|
23
24
|
createSqlStore,
|
|
24
25
|
getDb
|
|
25
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-237T7XAN.js";
|
|
26
27
|
import {
|
|
27
28
|
disconnectStateAdapter,
|
|
28
29
|
getConnectedStateContext
|
|
@@ -75,8 +76,7 @@ function createStateConversationStore(state) {
|
|
|
75
76
|
return {
|
|
76
77
|
get: (args) => getConversation({ ...args, state }),
|
|
77
78
|
recordActivity: (args) => recordConversationActivity({ ...args, state }),
|
|
78
|
-
recordExecution:
|
|
79
|
-
},
|
|
79
|
+
recordExecution: (args) => recordConversationExecution({ ...args, state }),
|
|
80
80
|
listByActivity: (args) => listConversationsByActivity({ ...args, state })
|
|
81
81
|
};
|
|
82
82
|
}
|
package/dist/nitro.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type JuniorPluginSet } from "./plugins";
|
|
2
|
+
import type { JuniorDashboardOptions } from "./app";
|
|
3
|
+
export type JuniorNitroDashboardOptions = Omit<JuniorDashboardOptions, "reporting">;
|
|
2
4
|
export interface JuniorPluginModuleReference {
|
|
3
5
|
/** Runtime-safe module that exports a `defineJuniorPlugins(...)` set. */
|
|
4
6
|
module: string;
|
|
@@ -8,6 +10,8 @@ export interface JuniorPluginModuleReference {
|
|
|
8
10
|
export type JuniorNitroPluginSource = JuniorPluginModuleReference | JuniorPluginSet | string;
|
|
9
11
|
export interface JuniorNitroOptions {
|
|
10
12
|
cwd?: string;
|
|
13
|
+
/** Authenticated dashboard configuration injected for createApp(). */
|
|
14
|
+
dashboard?: JuniorNitroDashboardOptions;
|
|
11
15
|
maxDuration?: number;
|
|
12
16
|
/** Vercel Queue topic for durable conversation work. Must match the runtime queue producer topic. */
|
|
13
17
|
conversationWorkQueueTopic?: string;
|
package/dist/nitro.js
CHANGED
|
@@ -54,6 +54,8 @@ function globToRegex(pattern) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// src/build/copy-build-content.ts
|
|
57
|
+
var DASHBOARD_PACKAGE_NAME = "@sentry/junior-dashboard";
|
|
58
|
+
var DASHBOARD_ASSET_NAMES = ["client.js", "tailwind.css"];
|
|
57
59
|
function copyAppAndPluginContent(cwd, serverRoot, packageNames) {
|
|
58
60
|
copyIfExists(path.join(cwd, "app"), path.join(serverRoot, "app"));
|
|
59
61
|
const packagedContent = discoverInstalledPluginPackageContent(cwd, {
|
|
@@ -83,6 +85,27 @@ function copyAppAndPluginContent(cwd, serverRoot, packageNames) {
|
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
}
|
|
88
|
+
function copyDashboardAssets(cwd, serverRoot) {
|
|
89
|
+
const packageDir = resolvePackageDir(cwd, DASHBOARD_PACKAGE_NAME);
|
|
90
|
+
if (!packageDir) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`createApp({ dashboard }) requires installing "${DASHBOARD_PACKAGE_NAME}"`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
for (const fileName of DASHBOARD_ASSET_NAMES) {
|
|
96
|
+
const source = dashboardAssetPath(packageDir, fileName);
|
|
97
|
+
copyIfExists(
|
|
98
|
+
source,
|
|
99
|
+
path.join(
|
|
100
|
+
serverRoot,
|
|
101
|
+
"node_modules",
|
|
102
|
+
DASHBOARD_PACKAGE_NAME,
|
|
103
|
+
"dist",
|
|
104
|
+
fileName
|
|
105
|
+
)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
86
109
|
function copyIncludedFiles(cwd, serverRoot, patterns) {
|
|
87
110
|
if (patterns === void 0) return;
|
|
88
111
|
if (!Array.isArray(patterns)) {
|
|
@@ -170,6 +193,20 @@ function copyIfExists(source, target) {
|
|
|
170
193
|
cpSync(source, target, { recursive: true });
|
|
171
194
|
return true;
|
|
172
195
|
}
|
|
196
|
+
function dashboardAssetPath(packageDir, fileName) {
|
|
197
|
+
const candidates = [
|
|
198
|
+
path.join(packageDir, fileName),
|
|
199
|
+
path.join(packageDir, "dist", fileName)
|
|
200
|
+
];
|
|
201
|
+
for (const candidate of candidates) {
|
|
202
|
+
if (existsSync(candidate)) {
|
|
203
|
+
return candidate;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
throw new Error(
|
|
207
|
+
`Junior dashboard asset ${fileName} was not built; run pnpm --filter @sentry/junior-dashboard build before building Nitro`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
173
210
|
function copyRootIntoServerOutput(cwd, serverRoot, root) {
|
|
174
211
|
copyIfExists(root, resolveServerOutputPath(cwd, serverRoot, root));
|
|
175
212
|
}
|
|
@@ -205,14 +242,25 @@ function renderRuntimePluginImport(module) {
|
|
|
205
242
|
}
|
|
206
243
|
return `import { ${module.exportName} as juniorRuntimePluginSet } from ${JSON.stringify(module.specifier)};`;
|
|
207
244
|
}
|
|
245
|
+
function renderDashboardImport(enabled) {
|
|
246
|
+
return enabled ? [
|
|
247
|
+
'import { createDashboardApp as juniorCreateDashboardApp } from "@sentry/junior-dashboard";',
|
|
248
|
+
"export const createDashboardApp = juniorCreateDashboardApp;"
|
|
249
|
+
] : ["export const createDashboardApp = undefined;"];
|
|
250
|
+
}
|
|
251
|
+
function dashboardEnabled(dashboard) {
|
|
252
|
+
return Boolean(dashboard && !dashboard.disabled);
|
|
253
|
+
}
|
|
208
254
|
function renderVirtualConfig(options) {
|
|
209
255
|
const lines = [
|
|
256
|
+
...renderDashboardImport(dashboardEnabled(options.dashboard)),
|
|
210
257
|
...options.pluginModule ? [
|
|
211
258
|
renderRuntimePluginImport(options.pluginModule),
|
|
212
259
|
"export const pluginSet = juniorRuntimePluginSet;"
|
|
213
260
|
] : ["export const pluginSet = undefined;"],
|
|
214
261
|
`export const plugins = ${JSON.stringify(options.plugins ?? { packages: [] })};`,
|
|
215
|
-
`export const pluginRuntimeRegistrations = ${JSON.stringify(options.pluginRuntimeRegistrations ?? [])}
|
|
262
|
+
`export const pluginRuntimeRegistrations = ${JSON.stringify(options.pluginRuntimeRegistrations ?? [])};`,
|
|
263
|
+
`export const dashboard = ${JSON.stringify(options.dashboard)};`
|
|
216
264
|
];
|
|
217
265
|
return lines.join("\n");
|
|
218
266
|
}
|
|
@@ -227,7 +275,8 @@ function injectVirtualConfig(nitro, options = {}) {
|
|
|
227
275
|
plugins: pluginCatalogConfigFromPluginSet(pluginSet),
|
|
228
276
|
pluginRuntimeRegistrations: pluginRuntimeRegistrationsFromPluginSet(
|
|
229
277
|
pluginSet
|
|
230
|
-
).map((plugin) => plugin.manifest.name)
|
|
278
|
+
).map((plugin) => plugin.manifest.name),
|
|
279
|
+
dashboard: options.dashboard
|
|
231
280
|
});
|
|
232
281
|
};
|
|
233
282
|
}
|
|
@@ -255,6 +304,9 @@ function assertSerializableDirectPluginSet(pluginSet) {
|
|
|
255
304
|
`juniorNitro({ plugins }) cannot receive a direct defineJuniorPlugins(...) set with runtime plugin registration(s): ${pluginRuntimeNames.join(", ")}. Export the set from a runtime-safe plugin module and pass juniorNitro({ plugins: "./plugins" }) so createApp() can import the same registrations at runtime.`
|
|
256
305
|
);
|
|
257
306
|
}
|
|
307
|
+
function dashboardEnabled2(dashboard) {
|
|
308
|
+
return Boolean(dashboard && !dashboard.disabled);
|
|
309
|
+
}
|
|
258
310
|
function runtimeModuleForResolvedPluginModule(moduleRef) {
|
|
259
311
|
return {
|
|
260
312
|
exportName: moduleRef.exportName,
|
|
@@ -356,7 +408,8 @@ function juniorNitro(options = {}) {
|
|
|
356
408
|
pluginModule: runtimeModuleForResolvedPluginModule(pluginModule)
|
|
357
409
|
} : {},
|
|
358
410
|
plugins: pluginCatalogConfig,
|
|
359
|
-
pluginRuntimeRegistrations
|
|
411
|
+
pluginRuntimeRegistrations,
|
|
412
|
+
dashboard: options.dashboard
|
|
360
413
|
});
|
|
361
414
|
const copyBuildContent = async () => {
|
|
362
415
|
const pluginSet = await loadConfiguredPluginSet();
|
|
@@ -366,6 +419,9 @@ function juniorNitro(options = {}) {
|
|
|
366
419
|
nitro.options.output.serverDir,
|
|
367
420
|
compiledPluginCatalogConfig?.packages
|
|
368
421
|
);
|
|
422
|
+
if (dashboardEnabled2(options.dashboard)) {
|
|
423
|
+
copyDashboardAssets(cwd, nitro.options.output.serverDir);
|
|
424
|
+
}
|
|
369
425
|
copyIncludedFiles(
|
|
370
426
|
cwd,
|
|
371
427
|
nitro.options.output.serverDir,
|
package/dist/reporting.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
buildSystemPrompt,
|
|
12
12
|
getAgentTurnSessionRecord,
|
|
13
13
|
listAgentTurnSessionSummariesForConversation
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-QDQVOMBA.js";
|
|
15
15
|
import {
|
|
16
16
|
getPluginOperationalReports
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-SSWBYEFH.js";
|
|
18
18
|
import "./chunk-RARSKPVT.js";
|
|
19
19
|
import {
|
|
20
20
|
getConversationStore
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-237T7XAN.js";
|
|
22
22
|
import "./chunk-G3E7SCME.js";
|
|
23
23
|
import "./chunk-LXTPBU4K.js";
|
|
24
24
|
import "./chunk-Q6XFTRV5.js";
|
|
@@ -163,6 +163,9 @@ function statusFromConversation(conversation, fallback, nowMs) {
|
|
|
163
163
|
if (conversation.execution.status === "idle") {
|
|
164
164
|
return "completed";
|
|
165
165
|
}
|
|
166
|
+
if (conversation.execution.status === "failed") {
|
|
167
|
+
return "failed";
|
|
168
|
+
}
|
|
166
169
|
const updatedAtMs = conversation.execution.updatedAtMs ?? conversation.updatedAtMs;
|
|
167
170
|
if (conversation.execution.status === "running" && nowMs - updatedAtMs > HUNG_TURN_PROGRESS_MS) {
|
|
168
171
|
return "hung";
|
|
@@ -796,22 +799,8 @@ async function readConversationStatsReport(options = {}) {
|
|
|
796
799
|
});
|
|
797
800
|
const truncated = conversations.length > CONVERSATION_STATS_LIMIT;
|
|
798
801
|
const sampledConversations = conversations.slice(0, CONVERSATION_STATS_LIMIT);
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
);
|
|
802
|
-
const reportsByConversation = await reportsFromConversations({
|
|
803
|
-
conversations: sampledConversations,
|
|
804
|
-
detailsByConversationId,
|
|
805
|
-
nowMs
|
|
806
|
-
});
|
|
807
|
-
const sessions = sampledConversations.flatMap(
|
|
808
|
-
(conversation) => reportsByConversation.get(conversation.conversationId) ?? [
|
|
809
|
-
sessionReportFromConversation(
|
|
810
|
-
conversation,
|
|
811
|
-
nowMs,
|
|
812
|
-
detailsByConversationId.get(conversation.conversationId)
|
|
813
|
-
)
|
|
814
|
-
]
|
|
802
|
+
const sessions = sampledConversations.map(
|
|
803
|
+
(conversation) => sessionReportFromConversation(conversation, nowMs)
|
|
815
804
|
);
|
|
816
805
|
return buildConversationStatsReport({
|
|
817
806
|
generatedAt,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
startActiveTurn,
|
|
15
15
|
updateConversationStats,
|
|
16
16
|
upsertConversationMessage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-2MSW5BZY.js";
|
|
18
18
|
import {
|
|
19
19
|
coerceThreadConversationState
|
|
20
20
|
} from "./chunk-Z4CIQ3EB.js";
|
|
@@ -23,10 +23,10 @@ import "./chunk-KNFROR7R.js";
|
|
|
23
23
|
import {
|
|
24
24
|
commitMessages,
|
|
25
25
|
loadProjection
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import "./chunk-
|
|
26
|
+
} from "./chunk-QDQVOMBA.js";
|
|
27
|
+
import "./chunk-SSWBYEFH.js";
|
|
28
28
|
import "./chunk-RARSKPVT.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-237T7XAN.js";
|
|
30
30
|
import "./chunk-G3E7SCME.js";
|
|
31
31
|
import "./chunk-LXTPBU4K.js";
|
|
32
32
|
import "./chunk-Q6XFTRV5.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.78.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"pg": "^8.16.3",
|
|
71
71
|
"yaml": "^2.9.0",
|
|
72
72
|
"zod": "^4.4.3",
|
|
73
|
-
"@sentry/junior-plugin-api": "0.
|
|
73
|
+
"@sentry/junior-plugin-api": "0.78.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@emnapi/core": "^1.10.0",
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
"typescript": "^6.0.3",
|
|
87
87
|
"vercel": "^54.4.0",
|
|
88
88
|
"vitest": "^4.1.7",
|
|
89
|
-
"@sentry/junior-memory": "0.
|
|
89
|
+
"@sentry/junior-memory": "0.78.0",
|
|
90
90
|
"@sentry/junior-testing": "0.0.0",
|
|
91
|
-
"@sentry/junior-scheduler": "0.
|
|
91
|
+
"@sentry/junior-scheduler": "0.78.0"
|
|
92
92
|
},
|
|
93
93
|
"scripts": {
|
|
94
94
|
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
|