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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ Co-Authored-By: (agent model name) <email>
- `specs/security-policy.md` (global runtime/container/token security policy)
- `specs/chat-architecture-spec.md` (chat composition, service, and test-seam architecture contract)
- `specs/slack-agent-delivery-spec.md` (Slack entry surfaces, reply delivery, continuation, files, images, and resume behavior contract)
- `specs/github-agent-delivery-spec.md` (GitHub mention entry surfaces, one-turn comment delivery, and platform/tool boundary contract)
- `specs/slack-outbound-contract-spec.md` (Slack outbound boundary, message/file/reaction safety rules, and markdown-to-`mrkdwn` ownership)
- `specs/skill-capabilities-spec.md` (capability declaration + broker/injection contract)
- `specs/oauth-flows-spec.md` (OAuth authorization code flow + Slack UX contract)
Expand Down
12 changes: 10 additions & 2 deletions packages/docs/src/content/docs/extend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ For reuse across apps or teams, package plugin manifests + skills as npm package
pnpm add @sentry/junior @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
```

List the plugin packages in `juniorNitro` so they are bundled at build time and available at runtime:
List the plugin packages in `juniorNitro` so they are bundled at build time, then enable the plugin manifest names on each platform that should use them:

```ts title="nitro.config.ts"
import { defineConfig } from "nitro";
Expand All @@ -71,6 +71,14 @@ export default defineConfig({
"@sentry/junior-notion",
"@sentry/junior-sentry",
],
platforms: {
slack: {
plugins: ["datadog", "github", "hex", "linear", "notion", "sentry"],
},
github: {
plugins: ["github"],
},
},
}),
],
routes: {
Expand Down Expand Up @@ -271,7 +279,7 @@ Then install it in the host app:
pnpm add @acme/junior-example
```

The `juniorNitro({ pluginPackages: [...] })` module includes `app/**/*` and the declared plugin package content in the deployed function bundle. The plugin list is automatically available at runtime via `createApp()` — no need to declare it twice.
The `juniorNitro({ pluginPackages: [...] })` module includes `app/**/*` and the declared plugin package content in the deployed function bundle. Use `platforms.<platform>.plugins` to decide which bundled providers each chat surface may use.

## Validate extensions

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ prev: false
title: "handlePlatformWebhook"
---

> **handlePlatformWebhook**(`request`, `platform`, `waitUntil`, `bot?`): `Promise`\<`Response`\>
> **handlePlatformWebhook**(`request`, `platform`, `waitUntil`, `bot`): `Promise`\<`Response`\>

Defined in: [handlers/webhooks.ts:124](https://github.com/getsentry/junior/blob/main/packages/junior/src/handlers/webhooks.ts#L124)
Defined in: [handlers/webhooks.ts:22](https://github.com/getsentry/junior/blob/main/packages/junior/src/handlers/webhooks.ts#L22)

Handles `POST /api/webhooks/:platform`.

The router only resolves the platform and delegates to the adapter webhook
implementation; request semantics stay owned by the adapter package.

For Slack, the body is read once and used to detect `message_changed` events
that introduce a new bot @mention, which the Slack adapter silently ignores.
The request is then reconstructed so the adapter can consume it normally.
Platform-owned preprocessors may rebuild the request before delegation when
an adapter has side-channel behavior the generic router should not own.

## Parameters

Expand All @@ -32,9 +31,9 @@ The request is then reconstructed so the adapter can consume it normally.

`WaitUntilFn`

### bot?
### bot

`JuniorChat`\<\{ `slack`: `SlackAdapter`; \}\> = `...`
`ProductionBot`

## Returns

Expand Down
102 changes: 88 additions & 14 deletions packages/docs/src/content/docs/reference/config-and-env.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,70 @@ related:

## Core runtime

| Variable | Required | Purpose |
| ------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SLACK_SIGNING_SECRET` | Yes | Verifies Slack request signatures. |
| `SLACK_BOT_TOKEN` or `SLACK_BOT_USER_TOKEN` | Yes | Posts thread replies and calls Slack APIs. |
| `REDIS_URL` | Yes | Queue and runtime state storage. |
| `JUNIOR_BOT_NAME` | No | Bot display/config naming. |
| `AI_MODEL` | No | Primary model selection override for main assistant turns. Defaults to `openai/gpt-5.4`; Junior chooses the reasoning effort per turn automatically. |
| `AI_FAST_MODEL` | No | Faster model for lightweight tasks and routing/classification passes before the main turn begins. Defaults to `openai/gpt-5.4-mini`. |
| `AI_VISION_MODEL` | No | Dedicated image-understanding model; unset disables vision features. |
| `AI_WEB_SEARCH_MODEL` | No | Override for the `webSearch` tool model. Defaults to a search-tuned model; does not fall through to `AI_MODEL`. |
| `JUNIOR_BASE_URL` | No | Canonical base URL for callback/auth URL generation. |
| `AI_GATEWAY_API_KEY` | No | AI gateway auth if used in your setup. |
| Variable | Required | Purpose |
| ------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SLACK_SIGNING_SECRET` | Conditional | Verifies Slack request signatures. Required when `slack` is enabled via `enabledPlatforms` or `platforms`. |
| `SLACK_BOT_TOKEN` or `SLACK_BOT_USER_TOKEN` | Conditional | Posts thread replies and calls Slack APIs. Required when `slack` is enabled via `enabledPlatforms` or `platforms`. |
| `REDIS_URL` | Yes | Queue and runtime state storage. |
| `JUNIOR_BOT_NAME` | No | Bot display/config naming. |
| `AI_MODEL` | No | Primary model selection override for main assistant turns. Defaults to `openai/gpt-5.4`; Junior chooses the reasoning effort per turn automatically. |
| `AI_FAST_MODEL` | No | Faster model for lightweight tasks and routing/classification passes before the main turn begins. Defaults to `openai/gpt-5.4-mini`. |
| `AI_VISION_MODEL` | No | Dedicated image-understanding model; unset disables vision features. |
| `AI_WEB_SEARCH_MODEL` | No | Override for the `webSearch` tool model. Defaults to a search-tuned model; does not fall through to `AI_MODEL`. |
| `JUNIOR_BASE_URL` | No | Canonical base URL for callback/auth URL generation. |
| `AI_GATEWAY_API_KEY` | No | AI gateway auth if used in your setup. |

## Chat ingress platforms

Slack is enabled by default. Configure chat ingress platforms in the app initializer:

```ts
import { createApp } from "@sentry/junior";

const app = await createApp({
enabledPlatforms: ["slack", "github"],
});
```

Use `enabledPlatforms: ["github"]` for a GitHub-only deployment without Slack.

You can also put the same setting in `juniorNitro(...)`; `createApp()` reads that build-time config automatically.

For one deployment that serves multiple platforms, prefer per-platform configuration:

```ts
juniorNitro({
pluginPackages: ["@sentry/junior-github", "@sentry/junior-sentry"],
platforms: {
slack: {
plugins: ["github", "sentry"],
skills: ["github-issues", "sentry-issues"],
},
github: {
plugins: ["github"],
skills: ["github-issues", "github-pr-review"],
},
},
});
```

`pluginPackages` controls what plugin package content is bundled. `platforms.<platform>.plugins` controls which plugin providers that platform can use at runtime. `platforms.<platform>.skills` is an optional exact skill allowlist.

## GitHub mention webhook (optional, V1)

Enable this when you want inbound GitHub mentions to run Junior through `/api/webhooks/github`.

Add `github` to `createApp({ enabledPlatforms })`, `juniorNitro({ enabledPlatforms })`, or `platforms.github` before adding these values.

| Variable | Required | Purpose |
| ------------------------ | -------- | --------------------------------------------------------------------------------------------- |
| `GITHUB_APP_ID` | Yes | GitHub App identity. |
| `GITHUB_APP_PRIVATE_KEY` | Yes | GitHub App signing key used for GitHub API calls. |
| `GITHUB_INSTALLATION_ID` | Yes | Initial single-installation target for V1 mention handling. |
| `GITHUB_WEBHOOK_SECRET` | Yes | Verifies GitHub webhook signatures (`x-hub-signature-256`). |
| `GITHUB_BOT_USERNAME` | Yes | Mention handle Junior listens for in issue/PR/review comment webhooks, without a leading `@`. |

V1 behavior is mention-only. Untagged GitHub comments do not trigger a turn.

## Build-time snapshot warmup

Expand Down Expand Up @@ -59,7 +111,7 @@ The egress proxy verifies Vercel-signed Sandbox OIDC tokens per request and bind
| `SENTRY_CLIENT_ID` | Yes | OAuth client ID. |
| `SENTRY_CLIENT_SECRET` | Yes | OAuth client secret. |

## Install-wide config defaults
## Config defaults

Pass `configDefaults` to `createApp()` to set provider defaults across all conversations:

Expand All @@ -75,13 +127,35 @@ const app = await createApp({
});
```

Keys must be registered plugin config keys. Channel-scoped overrides (`jr-rpc config set`) take precedence.
Use `platforms.<platform>.configDefaults` when Slack and GitHub need different defaults in the same deployment:

```ts
juniorNitro({
platforms: {
slack: {
plugins: ["sentry"],
configDefaults: {
"sentry.org": "sentry",
},
},
github: {
plugins: ["github"],
configDefaults: {
"github.repo": "myorg/myrepo",
},
},
},
});
```

Keys must be registered plugin config keys. Channel-scoped overrides (`jr-rpc config set`) take precedence over platform defaults, and platform defaults take precedence over install-wide defaults.

## Verification

- Validate required variables exist in deployment environment.
- Redeploy after variable changes.
- Run one end-to-end Slack thread action per enabled integration.
- If GitHub webhook mode is enabled, send one explicit `@<bot>` mention in a supported GitHub comment and confirm one final comment reply.

## Next step

Expand Down
12 changes: 8 additions & 4 deletions packages/docs/src/content/docs/reference/handler-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@ Handled `GET` routes:
- `/`
- `/health`
- `/api/info`
- `/api/oauth/callback/:provider`
- `/api/oauth/callback/mcp/:provider`
- `/api/oauth/callback/:provider` when Slack ingress is enabled
- `/api/oauth/callback/mcp/:provider` when Slack ingress is enabled

Handled `POST` routes:

- `/api/internal/turn-resume`
- `/api/webhooks/:platform` (Slack path is `/api/webhooks/slack`)
- `/api/internal/turn-resume` when Slack ingress is enabled
- `/api/webhooks/:platform`
- Slack: `/api/webhooks/slack`
- GitHub mention webhook (V1): `/api/webhooks/github`

## Expected behavior

- Unknown routes return `404`.
- Disabled webhook platforms return `404` without initializing that platform's bot adapter.
- Queue callback validates queue topic and processes thread work.
- Webhook handler logs and surfaces non-success behavior for operators.
- GitHub webhook entry is mention-only in V1: explicit `@<bot>` tags in issue/PR/review comments trigger one turn; untagged comments are ignored.

## Next step

Expand Down
47 changes: 46 additions & 1 deletion packages/docs/src/content/docs/start-here/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ related:
- Node.js 20+
- pnpm
- A Slack app with signing secret + bot token
- (Optional) A GitHub App for inbound mention webhooks
- Redis URL
- A Vercel account

Expand Down Expand Up @@ -43,12 +44,22 @@ For a new app, you usually do not need to hand-create routes or runtime wrapper

Copy values into your local env file. The scaffold includes `.env.example` with the core runtime variables.

Required:
Required for the default Slack ingress:

- `SLACK_SIGNING_SECRET`
- `SLACK_BOT_TOKEN`
- `REDIS_URL`

For a GitHub-only deployment, use `createApp({ enabledPlatforms: ["github"] })` or `juniorNitro({ enabledPlatforms: ["github"] })`, then omit the Slack variables.

Optional for GitHub mention webhook ingress (V1):

- `GITHUB_APP_ID`
- `GITHUB_APP_PRIVATE_KEY`
- `GITHUB_INSTALLATION_ID`
- `GITHUB_WEBHOOK_SECRET`
- `GITHUB_BOT_USERNAME`

Recommended:

- `JUNIOR_BOT_NAME`
Expand All @@ -73,6 +84,7 @@ Check the health route first, then verify a real Slack thread.
- `GET http://localhost:3000/health` returns JSON with `status: "ok"`.
- Set your Slack Event Subscriptions and Interactivity URLs to `http://<your-tunnel-or-dev-host>/api/webhooks/slack`.
- Mention the bot in Slack and confirm it replies in the same thread.
- If GitHub ingress is enabled, set your GitHub App webhook URL to `http://<your-tunnel-or-dev-host>/api/webhooks/github`, then post an explicit `@<bot>` mention in an issue/PR/review comment and confirm one final reply comment.

## Add plugins

Expand Down Expand Up @@ -184,6 +196,7 @@ Optional:

- `JUNIOR_BASE_URL`
- `AI_GATEWAY_API_KEY`
- GitHub mention webhook env vars listed earlier (only if GitHub ingress is enabled)

### Configure Slack request URL

Expand All @@ -193,10 +206,42 @@ Set Event Subscriptions and Interactivity URLs to:
https://<your-domain>/api/webhooks/slack
```

### Configure GitHub webhook URL (optional, mention-only V1)

Enable GitHub in the app initializer before configuring this webhook:

```ts
const app = await createApp({
enabledPlatforms: ["slack", "github"],
});
```

If you keep deployment wiring in `nitro.config.ts`, use `juniorNitro({ enabledPlatforms: ["slack", "github"] })` instead and leave `createApp()` without platform options.

For one deployment where Slack and GitHub should expose different plugins or skills, use `juniorNitro({ platforms: { slack: { plugins: [...] }, github: { plugins: [...] } } })` instead of `enabledPlatforms`.

Set your GitHub App webhook URL to:

```text
https://<your-domain>/api/webhooks/github
```

Subscribe to:

- `issue_comment`
- `pull_request_review_comment`

V1 behavior:

- Junior runs only on explicit `@<bot>` mentions in those comment surfaces.
- Untagged comments are ignored.
- Delivery is one final GitHub comment reply (no streaming/status surface).

### Verify in production

- `GET https://<your-domain>/health` succeeds.
- A Slack mention produces a thread reply.
- If GitHub ingress is enabled, an explicit GitHub mention in a supported comment surface produces one final reply comment.
- Queue callback logs show successful processing.

## Common failures
Expand Down
Loading