Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .craft.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ targets:
- name: npm
id: "@sentry/junior-agent-browser"
includeNames: /^sentry-junior-agent-browser-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-dashboard"
includeNames: /^sentry-junior-dashboard-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-datadog"
includeNames: /^sentry-junior-datadog-\d.*\.tgz$/
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
pnpm --filter @sentry/junior pack --pack-destination artifacts
pnpm --filter @sentry/junior-plugin-api pack --pack-destination artifacts
pnpm --filter @sentry/junior-agent-browser pack --pack-destination artifacts
pnpm --filter @sentry/junior-dashboard pack --pack-destination artifacts
pnpm --filter @sentry/junior-datadog pack --pack-destination artifacts
pnpm --filter @sentry/junior-github pack --pack-destination artifacts
pnpm --filter @sentry/junior-hex pack --pack-destination artifacts
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ coverage
dist/
/packages/docs/.astro
packages/junior/dist
.codex/environments/
# Auto-generated by dotagents — do not commit these files.
.agents/.gitignore
# Generated by eval replay auto mode; existing tracked recordings stay tracked.
packages/junior-evals/.vitest-evals/recordings/**/*.json
.env*
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ Co-Authored-By: (agent model name) <email>
- `TELEMETRY.spec.md` (format contract for repository-root telemetry maps)
- `specs/index.md` (spec taxonomy, naming rules, and canonical vs archive guidance)
- `specs/security-policy.md` (global runtime/container/token security policy)
- `specs/data-redaction-policy.md` (conversation privacy classification and raw payload redaction policy)
- `specs/chat-architecture.md` (chat composition, service, and test-seam architecture contract)
- `specs/slack-agent-delivery.md` (Slack entry surfaces, reply delivery, continuation, files, images, and resume behavior contract)
- `specs/slack-outbound-contract.md` (Slack outbound boundary, message/file/reaction safety rules, and markdown-to-`mrkdwn` ownership)
Expand All @@ -124,5 +125,6 @@ Co-Authored-By: (agent model name) <email>
- `specs/plugin.md` (plugin architecture for self-contained provider integrations)
- `specs/plugin-manifest.md` (plugin manifest fields and validation contract)
- `specs/plugin-runtime.md` (plugin discovery, loading, skills, and MCP runtime contract)
- `specs/dashboard.md` (authenticated dashboard, stateless Better Auth, and reporting boundary contract)
- `specs/testing.md` (testing taxonomy and layer boundaries: unit/integration/eval)
- Historical evaluations and superseded trackers live under `specs/archive/`.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ This repo uses Craft for manual lockstep npm releases of:
- `@sentry/junior`
- `@sentry/junior-plugin-api`
- `@sentry/junior-agent-browser`
- `@sentry/junior-dashboard`
- `@sentry/junior-datadog`
- `@sentry/junior-github`
- `@sentry/junior-hex`
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Start here:
| `@sentry/junior` | Core Slack bot runtime |
| `@sentry/junior-plugin-api` | Lightweight trusted plugin API types and helpers |
| `@sentry/junior-agent-browser` | Agent Browser plugin package for browser automation |
| `@sentry/junior-dashboard` | Authenticated dashboard package for Junior runtime diagnostics |
| `@sentry/junior-datadog` | Datadog plugin package for observability workflows through Datadog's Pup CLI |
| `@sentry/junior-github` | GitHub plugin package for issue workflows |
| `@sentry/junior-hex` | Hex plugin package for data warehouse query workflows |
Expand Down
5 changes: 5 additions & 0 deletions apps/example/nitro.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { defineConfig } from "nitro";
import { juniorDashboardNitro } from "@sentry/junior-dashboard/nitro";
import { juniorNitro } from "@sentry/junior/nitro";
import { examplePluginPackages } from "./plugin-packages";

Expand All @@ -10,6 +11,10 @@ export default defineConfig({
packages: examplePluginPackages,
},
}),
juniorDashboardNitro({
authRequired: false,
allowedGoogleDomains: ["sentry.io"],
}),
],
routes: {
"/**": { handler: "./server.ts" },
Expand Down
3 changes: 2 additions & 1 deletion apps/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"private": true,
"type": "module",
"scripts": {
"predev": "pnpm --filter @sentry/junior build && pnpm --filter @sentry/junior-dashboard build",
"dev": "nitro dev",
"build": "junior snapshot create && nitro build",
"preview": "nitro preview",
Expand All @@ -11,13 +12,13 @@
"dependencies": {
"@sentry/junior": "workspace:*",
"@sentry/junior-agent-browser": "workspace:*",
"@sentry/junior-dashboard": "workspace:*",
"@sentry/junior-datadog": "workspace:*",
"@sentry/junior-github": "workspace:*",
"@sentry/junior-hex": "workspace:*",
"@sentry/junior-linear": "workspace:*",
"@sentry/junior-notion": "workspace:*",
"@sentry/junior-sentry": "workspace:*",
"@sentry/node": "10.53.1",
"hono": "^4.12.22"
},
"devDependencies": {
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@
"lint": "pnpm --filter @sentry/junior lint",
"lint:fix": "pnpm --filter @sentry/junior lint:fix",
"lint-staged": "lint-staged",
"build": "pnpm --filter @sentry/junior build",
"build": "pnpm --filter @sentry/junior build && pnpm --filter @sentry/junior-dashboard build",
"build:example": "pnpm --filter @sentry/junior-example build",
"docs:dev": "pnpm --filter @sentry/junior-docs dev",
"docs:build": "pnpm --filter @sentry/junior-docs build",
"docs:check": "pnpm --filter @sentry/junior-docs check",
"release:check": "node scripts/check-release-config.mjs",
"start": "pnpm --filter @sentry/junior-example dev",
"test": "pnpm --filter @sentry/junior build && pnpm --filter @sentry/junior test",
"test": "pnpm --filter @sentry/junior build && pnpm --filter @sentry/junior-dashboard build && pnpm --filter @sentry/junior test && pnpm --filter @sentry/junior-dashboard test",
"test:watch": "pnpm --filter @sentry/junior test:watch",
"evals": "pnpm --filter @sentry/junior-evals evals",
"evals:record": "pnpm --filter @sentry/junior-evals evals:record",
"typecheck": "pnpm --filter @sentry/junior-plugin-api typecheck && pnpm --filter @sentry/junior-scheduler typecheck && pnpm --filter @sentry/junior typecheck && pnpm --filter @sentry/junior-testing typecheck && pnpm --filter @sentry/junior-example typecheck",
"typecheck": "pnpm --filter @sentry/junior-plugin-api typecheck && pnpm --filter @sentry/junior-scheduler typecheck && pnpm --filter @sentry/junior typecheck && pnpm --filter @sentry/junior-dashboard typecheck && pnpm --filter @sentry/junior-testing typecheck && pnpm --filter @sentry/junior-example typecheck",
"skills:check": "pnpm --filter @sentry/junior skills:check"
},
"simple-git-hooks": {
Expand Down
1 change: 1 addition & 0 deletions packages/docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export default defineConfig({
label: "Security Hardening",
link: "/operate/security-hardening/",
},
{ label: "Dashboard", link: "/operate/dashboard/" },
{
label: "Sandbox Snapshots",
link: "/operate/sandbox-snapshots/",
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"dependencies": {
"@astrojs/check": "^0.9.9",
"@astrojs/starlight": "^0.39.2",
"@sentry/starlight-theme": "^0.7.0",
"@sentry/starlight-theme": "catalog:",
"astro": "^6.3.7",
"starlight-typedoc": "^0.23.0",
"typedoc": "^0.28.19",
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/content/docs/contribute/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ If your team account requires an explicit Vercel scope, add `--scope <team-slug>
pnpm dev
```

This starts the example app on `http://localhost:3000` by default.
This starts the example app on `http://localhost:3000` by default. It also rebuilds and watches the workspace packages that the example app consumes, so dashboard and runtime package edits are reflected without manually rebuilding first.

## Common checks

Expand Down
1 change: 1 addition & 0 deletions packages/docs/src/content/docs/contribute/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Junior uses lockstep package releases for:
- `@sentry/junior`
- `@sentry/junior-plugin-api`
- `@sentry/junior-agent-browser`
- `@sentry/junior-dashboard`
- `@sentry/junior-datadog`
- `@sentry/junior-github`
- `@sentry/junior-hex`
Expand Down
119 changes: 119 additions & 0 deletions packages/docs/src/content/docs/operate/dashboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: Dashboard
description: Mount the authenticated Junior dashboard with Google domain auth.
type: tutorial
summary: Add the dashboard package to a Nitro deployment and protect diagnostics with Better Auth and Google domain authorization.
prerequisites:
- /start-here/existing-app/
- /reference/config-and-env/
related:
- /reference/handler-surface/
- /operate/security-hardening/
- /start-here/verify-and-troubleshoot/
---

Use `@sentry/junior-dashboard` when you want browser access to Junior runtime diagnostics without exposing plugin, skill, or filesystem discovery publicly. The dashboard mounts into the same Nitro deployment as Junior, but its Better Auth session only protects dashboard routes.

## Install

Install the dashboard package next to `@sentry/junior`:

```bash
pnpm add @sentry/junior-dashboard
```

## Mount the routes

Add `juniorDashboardNitro()` before the catch-all Junior route. Configure the Google Workspace domain that should be allowed to view the dashboard:

```ts title="nitro.config.ts"
import { defineConfig } from "nitro";
import { juniorDashboardNitro } from "@sentry/junior-dashboard/nitro";
import { juniorNitro } from "@sentry/junior/nitro";

export default defineConfig({
preset: "vercel",
modules: [
juniorNitro({
plugins: {
packages: ["@sentry/junior-sentry"],
},
}),
juniorDashboardNitro({
allowedGoogleDomains: ["sentry.io"],
trustedOrigins: ["https://<your-domain>"],
}),
],
routes: {
"/**": { handler: "./server.ts" },
},
});
```

You can also provide the same authorization policy through deployment environment variables when the handler is loaded outside Nitro's virtual module path:

| Variable | Purpose |
| ---------------------------------- | ------------------------------------------------------------- |
| `JUNIOR_DASHBOARD_GOOGLE_DOMAINS` | Comma-separated or JSON array of allowed Google domains. |
| `JUNIOR_DASHBOARD_ALLOWED_EMAILS` | Comma-separated or JSON array of explicit email allowlist. |
| `JUNIOR_DASHBOARD_TRUSTED_ORIGINS` | Comma-separated or JSON array of Better Auth trusted origins. |
| `JUNIOR_DASHBOARD_AUTH_REQUIRED` | Set to `false` only for explicit local dashboard auth bypass. |

The dashboard owns these routes:

| Route | Purpose |
| ------------------ | --------------------------------------- |
| `/` | Authenticated command-center UI. |
| `/conversations` | Authenticated conversation-history UI. |
| `/api/dashboard/*` | Authenticated dashboard JSON APIs. |
| `/api/auth/*` | Better Auth Google login and callbacks. |
| `/health` | Public minimal Junior health response. |

The current dashboard API slices are:

| Endpoint | Purpose |
| -------------------------------------------- | --------------------------------------------------------- |
| `/api/dashboard/health` | Health status for the command center pulse. |
| `/api/dashboard/runtime` | Runtime paths, providers, skills, and packages. |
| `/api/dashboard/plugins` | Loaded plugin list. |
| `/api/dashboard/skills` | Discovered skill list. |
| `/api/dashboard/sessions` | Recent conversation feed from turn-session checkpoints. |
| `/api/dashboard/conversations/:conversation` | Expiring conversation transcript with tool calls/results. |
| `/api/dashboard/config` | Safe dashboard config signals and feature readiness. |
| `/api/dashboard/me` | Signed-in dashboard identity. |

The dashboard UI is a React client using React Router for browser views and TanStack Query to poll dashboard APIs. `/` shows command-center health and recent turn durations; `/conversations` shows conversation history; `/conversations/:conversation` shows the transcript and turn/tool-call detail for one conversation. The dashboard does not wrap Slack webhooks, provider OAuth callbacks, sandbox egress, or `/api/internal/*`.
The conversation feed is a bounded metadata index with the same expiration policy as turn-session checkpoints. Conversation detail reads transcript data from the expiring checkpoint message store, so old transcripts disappear when checkpoint state expires. When `SENTRY_DSN` initializes the runtime and `SENTRY_ORG_SLUG` is set, conversation rows include a Sentry conversation link; when the runtime captures a trace ID, conversation detail shows it with the turn metadata.
Dashboard dates use `JUNIOR_TIMEZONE`, defaulting to `America/Los_Angeles`.

## Configure Google auth

Create a Google OAuth client for the deployment origin. Add this redirect URI:

```text
https://<your-domain>/api/auth/callback/google
```

Set the required environment variables:

| Variable | Purpose |
| ---------------------- | --------------------------- |
| `GOOGLE_CLIENT_ID` | Google OAuth client ID. |
| `GOOGLE_CLIENT_SECRET` | Google OAuth client secret. |

Dashboard cookies are signed with `JUNIOR_SECRET` by default. Set `BETTER_AUTH_SECRET` only when you need a separate rotation boundary for browser sessions.
Dashboard callbacks use `JUNIOR_BASE_URL`, Vercel URL envs, or local dev by default. Set `BETTER_AUTH_URL` only when dashboard auth needs a different public origin.

## Verify

After deployment:

1. `GET https://<your-domain>/health` returns a minimal health JSON response.
2. `GET https://<your-domain>/api/info` returns `404`.
3. Opening `https://<your-domain>/` starts Google login.
4. A user from the configured Google Workspace domain reaches the dashboard.
5. A user outside the configured domain receives `403`.

## Next step

Use [Security Hardening](/operate/security-hardening/) to review production auth boundaries, then use [Verify & Troubleshoot](/start-here/verify-and-troubleshoot/) for deployment smoke checks.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: "createApp"

> **createApp**(`options?`): `Promise`\<`Hono`\<`BlankEnv`, `BlankSchema`, `"/"`\>\>
Defined in: [app.ts:179](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L179)
Defined in: [app.ts:177](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L177)

Create a Hono app with all Junior routes.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ prev: false
title: "JuniorAppOptions"
---

Defined in: [app.ts:32](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L32)
Defined in: [app.ts:30](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L30)

## Properties

### configDefaults?

> `optional` **configDefaults?**: `Record`\<`string`, `unknown`\>

Defined in: [app.ts:34](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L34)
Defined in: [app.ts:32](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L32)

Install-wide provider defaults (`provider.key` format). Channel overrides take precedence.

Expand All @@ -23,7 +23,7 @@ Install-wide provider defaults (`provider.key` format). Channel overrides take p

> `optional` **plugins?**: `PluginConfig` \| `JuniorPlugin`[]

Defined in: [app.ts:42](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L42)
Defined in: [app.ts:40](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L40)

Plugin packages/overrides, or trusted plugin instances loaded by this app.

Expand All @@ -37,4 +37,4 @@ their package config is merged with the catalog bundled by `juniorNitro()`.

> `optional` **waitUntil?**: `WaitUntilFn`

Defined in: [app.ts:43](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L43)
Defined in: [app.ts:41](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L41)
19 changes: 19 additions & 0 deletions packages/docs/src/content/docs/reference/config-and-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'

Use one stable value per deployment. Rotating it invalidates pending internal resume callbacks and sandbox requester context signed with the previous value.

## Dashboard auth

If you mount `@sentry/junior-dashboard`, set these browser-auth variables:

| Variable | Required | Purpose |
| ---------------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `GOOGLE_CLIENT_ID` | Yes | Google OAuth client ID. |
| `GOOGLE_CLIENT_SECRET` | Yes | Google OAuth client secret. |
| `BETTER_AUTH_URL` | No | Optional dashboard callback origin. Defaults to `JUNIOR_BASE_URL`, Vercel URL envs, or local dev. |
| `BETTER_AUTH_SECRET` | No | Optional override for dashboard cookies. Defaults to `JUNIOR_SECRET`. |

Configure allowed Google Workspace domains in `juniorDashboardNitro()` for normal Nitro deployments. If your deployment imports the dashboard handler before Nitro virtual modules are available, set these optional policy variables instead:

| Variable | Required | Purpose |
| ---------------------------------- | -------- | ------------------------------------------------------------- |
| `JUNIOR_DASHBOARD_GOOGLE_DOMAINS` | No | Comma-separated or JSON array of allowed Google domains. |
| `JUNIOR_DASHBOARD_ALLOWED_EMAILS` | No | Comma-separated or JSON array of explicit email allowlist. |
| `JUNIOR_DASHBOARD_TRUSTED_ORIGINS` | No | Comma-separated or JSON array of Better Auth trusted origins. |

## Build-time snapshot warmup

If your build command runs `junior snapshot create`:
Expand Down
3 changes: 2 additions & 1 deletion packages/docs/src/content/docs/reference/handler-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ Handled `GET` routes:

- `/`
- `/health`
- `/api/info`
- `/api/oauth/callback/:provider`
- `/api/oauth/callback/mcp/:provider`

When `@sentry/junior-dashboard` is mounted, the dashboard package owns `/`, `/api/dashboard/*`, and `/api/auth/*`; use `/health` for unauthenticated health checks.

Handled `POST` routes:

- `/api/internal/turn-resume`
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/content/docs/start-here/existing-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default defineConfig({
});
```

If your existing app already owns routes, make sure the Junior Hono app still receives the paths under `/api/webhooks`, `/api/oauth/callback`, `/api/internal/turn-resume`, `/api/info`, and `/health`. Do not split those routes across independent runtime instances.
If your existing app already owns routes, make sure the Junior Hono app still receives the paths under `/api/webhooks`, `/api/oauth/callback`, `/api/internal/turn-resume`, and `/health`. Do not split those routes across independent runtime instances. When mounted, `@sentry/junior-dashboard` owns `/`, `/api/dashboard/*`, and `/api/auth/*`.

Some packages also export trusted runtime hooks. Register those in `createApp()`;
do not rely on `juniorNitro()` alone. For example, see
Expand Down
Loading
Loading