Skip to content
Merged
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 @@ -5,6 +5,9 @@ targets:
- name: npm
id: "@sentry/junior"
includeNames: /^sentry-junior-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-plugin-api"
includeNames: /^sentry-junior-plugin-api-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-agent-browser"
includeNames: /^sentry-junior-agent-browser-\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 @@ -51,6 +51,7 @@ jobs:
run: |
mkdir -p artifacts
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-datadog pack --pack-destination artifacts
pnpm --filter @sentry/junior-github pack --pack-destination artifacts
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pnpm build:pkg
This repo uses Craft for manual lockstep npm releases of:

- `@sentry/junior`
- `@sentry/junior-plugin-api`
- `@sentry/junior-agent-browser`
- `@sentry/junior-datadog`
- `@sentry/junior-github`
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Start here:
| Package | Purpose |
| ------------------------------ | ---------------------------------------------------------------------------- |
| `@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-datadog` | Datadog plugin package for observability workflows through Datadog's Pup CLI |
| `@sentry/junior-github` | GitHub plugin package for issue workflows |
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 @@ -12,6 +12,7 @@ related:
Junior uses lockstep package releases for:

- `@sentry/junior`
- `@sentry/junior-plugin-api`
- `@sentry/junior-agent-browser`
- `@sentry/junior-datadog`
- `@sentry/junior-github`
Expand Down
77 changes: 77 additions & 0 deletions packages/docs/src/content/docs/extend/build-a-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ The package must include the manifest and skills in `package.json`:
}
```

If the package also exports trusted runtime hooks, include the entrypoint and
depend on `@sentry/junior-plugin-api`:

```json title="package.json"
{
"name": "@acme/junior-my-provider",
"type": "module",
"exports": {
".": {
"types": "./index.d.ts",
"default": "./index.js"
}
},
"files": ["index.d.ts", "index.js", "plugin.yaml", "skills"],
"dependencies": {
"@sentry/junior-plugin-api": "^0.53.0"
}
}
```

## Minimal manifest

A plugin can be manifest-only:
Expand Down Expand Up @@ -116,6 +136,63 @@ export default defineConfig({

Do not use the removed `pluginPackages` option. `junior check` rejects it.

## Add trusted runtime hooks

Most plugins should stay manifest-only. Add trusted runtime hooks only when the
plugin must force deterministic behavior at a Junior-owned boundary, such as
installing sandbox helper files or mutating tool input/env before execution.
Trusted hooks are backend code and must be registered explicitly from app code;
Junior never loads them from `plugin.yaml`.

Export a factory from the plugin package:

```ts title="index.ts"
import { defineJuniorPlugin } from "@sentry/junior-plugin-api";

export function myProviderPlugin() {
return defineJuniorPlugin({
name: "my-provider",
pluginConfig: {
packages: ["@acme/junior-my-provider"],
},
hooks: {
async sandboxPrepare(ctx) {
await ctx.sandbox.writeFile({
path: `${ctx.sandbox.juniorRoot}/my-provider-ready`,
content: "ok\n",
});
},
beforeToolExecute(ctx) {
if (ctx.tool.name === "bash") {
ctx.env.set("MY_PROVIDER_NON_SECRET_FLAG", "1");
}
},
},
});
}
```

Register the trusted plugin from the app:

```ts title="server.ts"
import { createApp } from "@sentry/junior";
import { myProviderPlugin } from "@acme/junior-my-provider";

const app = await createApp({
plugins: [myProviderPlugin()],
});

export default app;
```

`pluginConfig.packages` should include the package that contains `plugin.yaml`
so the trusted registration also loads the declarative provider metadata. Any
packages declared through `juniorNitro({ plugins })` continue to load; trusted
plugin package config is merged with the build-time plugin catalog.

Use `ctx.decision.replaceInput(...)` only with object-shaped tool input. Junior
rejects non-object replacements before the tool runs.

## Validate

Run validation before deploy:
Expand Down
23 changes: 22 additions & 1 deletion packages/docs/src/content/docs/extend/github-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ pnpm add @sentry/junior @sentry/junior-github

## Runtime setup

List the plugin in `juniorNitro({ plugins: { packages: [...] } })`:
List the plugin in `juniorNitro({ plugins: { packages: [...] } })` so the
manifest, runtime dependencies, and bundled skills are copied into the deployed
function:

```ts title="nitro.config.ts"
juniorNitro({
Expand All @@ -31,6 +33,25 @@ juniorNitro({
});
```

Register the trusted GitHub plugin in `createApp()` so Junior can enforce Git
commit attribution at runtime:

```ts title="server.ts"
import { createApp } from "@sentry/junior";
import { githubPlugin } from "@sentry/junior-github";

const app = await createApp({
plugins: [
githubPlugin({
botNameEnv: "GITHUB_APP_BOT_NAME",
botEmailEnv: "GITHUB_APP_BOT_EMAIL",
}),
],
});

export default app;
```

## Configure environment variables

Set these values in the host environment:
Expand Down
32 changes: 31 additions & 1 deletion packages/docs/src/content/docs/extend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,36 @@ juniorNitro({

If you publish your own package with bundled skills, include both `plugin.yaml` and `skills` in package `files`. Manifest-only packages can include just `plugin.yaml`.

## Trusted runtime hooks

Some packaged plugins also export trusted runtime hooks for deterministic
behavior that cannot live in skill prose or `plugin.yaml`. For example, the
GitHub plugin registers runtime code that installs a sandbox Git hook,
configures global Git defaults, and injects commit attribution env before bash
commands run.

Trusted hooks are explicit app code:

```ts title="server.ts"
import { createApp } from "@sentry/junior";
import { githubPlugin } from "@sentry/junior-github";

const app = await createApp({
plugins: [
githubPlugin({
botNameEnv: "GITHUB_APP_BOT_NAME",
botEmailEnv: "GITHUB_APP_BOT_EMAIL",
}),
],
});

export default app;
```

Do not put trusted code entrypoints in `plugin.yaml`; manifests stay
declarative. Use [Build a Plugin](/extend/build-a-plugin/) for the package
authoring contract.

## Local skills vs plugin skills

Junior discovers both:
Expand Down Expand Up @@ -297,7 +327,7 @@ Then install it in the host app:
pnpm add @acme/junior-example
```

The `juniorNitro({ plugins: { packages: [...] } })` 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({ plugins: { packages: [...] } })` module includes `app/**/*` and the declared plugin package content in the deployed function bundle. The plugin list is automatically available at runtime via `createApp()` for declarative manifest behavior. Plugins that export trusted runtime hooks, such as `@sentry/junior-github`, must also be registered from app code.

## Validate extensions

Expand Down
4 changes: 3 additions & 1 deletion packages/docs/src/content/docs/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ The API reference is generated from public package entry points.
1. Read [Route & Handler Surface](/reference/handler-surface/) first.
2. Read `createApp` options to understand runtime route wiring.
3. Read `juniorNitro` options before changing plugin package bundling.
4. Read instrumentation exports for telemetry setup.
4. For trusted plugin hooks, use `@sentry/junior-plugin-api` from a plugin
package and register the returned `JuniorPlugin` with `createApp()`.
5. Read instrumentation exports for telemetry setup.

## Next step

Expand Down
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:118](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L118)
Defined in: [app.ts:175](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L175)

Create a Hono app with all Junior routes.

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

Defined in: [app.ts:25](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L25)
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:27](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L27)
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.

---

### plugins?

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

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

Defined in: [app.ts:29](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L29)
Plugin packages/overrides, or trusted plugin instances loaded by this app.

Plugin packages and manifest overrides loaded by this app instance.
Use `PluginConfig` for declarative package lists and manifest overrides.
Use `JuniorPlugin[]` for trusted plugin factories such as `githubPlugin()`;
their package config is merged with the catalog bundled by `juniorNitro()`.

---

### waitUntil?

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

Defined in: [app.ts:30](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L30)
Defined in: [app.ts:41](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L41)
6 changes: 5 additions & 1 deletion packages/docs/src/content/docs/start-here/existing-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default defineConfig({
modules: [
juniorNitro({
plugins: {
packages: ["@sentry/junior-github"],
packages: ["@sentry/junior-sentry"],
},
}),
],
Expand All @@ -56,6 +56,10 @@ 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.

Some packages also export trusted runtime hooks. Register those in `createApp()`;
do not rely on `juniorNitro()` alone. For example, see
[GitHub Plugin](/extend/github-plugin/) for the `githubPlugin()` app-code setup.

## Add app files

Junior expects app context and local extension files under `app/`:
Expand Down
5 changes: 5 additions & 0 deletions packages/docs/src/content/docs/start-here/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ Run the app check after changing plugins or skills:
pnpm check
```

Plugins with trusted runtime hooks need one more app-code registration step.
For example, `@sentry/junior-github` must be registered with `githubPlugin()`
inside `createApp()` to enforce Git commit attribution. See
[GitHub Plugin](/extend/github-plugin/) for that setup.

## Verify plugin content

When enabled plugins declare sandbox runtime dependencies, the scaffolded build runs snapshot warmup:
Expand Down
19 changes: 19 additions & 0 deletions packages/junior-github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,23 @@ Install it alongside `@sentry/junior`:
pnpm add @sentry/junior @sentry/junior-github
```

Register the trusted plugin from app code:

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

const app = await createApp({
plugins: [
githubPlugin({
botNameEnv: "GITHUB_APP_BOT_NAME",
botEmailEnv: "GITHUB_APP_BOT_EMAIL",
}),
],
});
```

Also list `@sentry/junior-github` in `juniorNitro({ plugins: { packages: [...] } })`
so Nitro bundles the manifest and bundled GitHub skill.

Full setup guide: https://junior.sentry.dev/extend/github-plugin/
9 changes: 9 additions & 0 deletions packages/junior-github/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { JuniorPlugin } from "@sentry/junior-plugin-api";

export interface GitHubPluginOptions {
botEmailEnv?: string;
botNameEnv?: string;
}

/** Register trusted GitHub runtime hooks for commit attribution and package loading. */
export function githubPlugin(options?: GitHubPluginOptions): JuniorPlugin;
Loading
Loading