Skip to content
Draft
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 @@ -26,6 +26,9 @@ targets:
- name: npm
id: "@sentry/junior-notion"
includeNames: /^sentry-junior-notion-\d.*\.tgz$/
- name: npm
id: "@sentry/junior-scheduler"
includeNames: /^sentry-junior-scheduler-\d.*\.tgz$/
Comment thread
cursor[bot] marked this conversation as resolved.
- name: npm
id: "@sentry/junior-sentry"
includeNames: /^sentry-junior-sentry-\d.*\.tgz$/
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
pnpm --filter @sentry/junior-hex pack --pack-destination artifacts
pnpm --filter @sentry/junior-linear pack --pack-destination artifacts
pnpm --filter @sentry/junior-notion pack --pack-destination artifacts
pnpm --filter @sentry/junior-scheduler pack --pack-destination artifacts
pnpm --filter @sentry/junior-sentry pack --pack-destination artifacts
ls -la artifacts

Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ This repo uses Craft for manual lockstep npm releases of:
- `@sentry/junior-hex`
- `@sentry/junior-linear`
- `@sentry/junior-notion`
- `@sentry/junior-scheduler`
- `@sentry/junior-sentry`

Run `pnpm release:check` before changing release package lists so `.craft.yml`, CI,
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ Start here:
| `@sentry/junior-hex` | Hex plugin package for data warehouse query workflows |
| `@sentry/junior-linear` | Linear plugin package for issue workflows |
| `@sentry/junior-notion` | Notion plugin package for page search workflows |
| `@sentry/junior-scheduler` | Scheduler plugin package for scheduled Junior tasks |
| `@sentry/junior-sentry` | Sentry plugin package for issue workflows |
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"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 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-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/src/content/docs/contribute/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Junior uses lockstep package releases for:
- `@sentry/junior-hex`
- `@sentry/junior-linear`
- `@sentry/junior-notion`
- `@sentry/junior-scheduler`
- `@sentry/junior-sentry`

## Package release
Expand Down
16 changes: 8 additions & 8 deletions packages/docs/src/content/docs/extend/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,9 @@ my-junior-plugin/
For reuse across apps or teams, package plugin manifests and any bundled skills as npm packages and install them next to `@sentry/junior`.

```bash
pnpm add @sentry/junior @sentry/junior-agent-browser @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
pnpm add @sentry/junior @sentry/junior-agent-browser @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-scheduler @sentry/junior-sentry
```

Junior also includes the built-in [Scheduler Plugin](/extend/scheduler-plugin/)
for reminders and recurring Slack tasks. It does not require a separate package.

List the plugin packages in `juniorNitro` so they are bundled at build time and available at runtime:

```ts title="nitro.config.ts"
Expand All @@ -78,6 +75,7 @@ export default defineConfig({
"@sentry/junior-hex",
"@sentry/junior-linear",
"@sentry/junior-notion",
"@sentry/junior-scheduler",
"@sentry/junior-sentry",
],
},
Expand Down Expand Up @@ -115,18 +113,20 @@ If you publish your own package with bundled skills, include both `plugin.yaml`

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.
scheduler plugin registers schedule-management tools and heartbeat behavior, and
the GitHub plugin 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";
import { schedulerPlugin } from "@sentry/junior-scheduler";

const app = await createApp({
plugins: [
schedulerPlugin(),
githubPlugin({
botNameEnv: "GITHUB_APP_BOT_NAME",
botEmailEnv: "GITHUB_APP_BOT_EMAIL",
Expand Down Expand Up @@ -331,7 +331,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()` for declarative manifest behavior. Plugins that export trusted runtime hooks, such as `@sentry/junior-github`, must also be registered from app code.
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-scheduler` and `@sentry/junior-github`, must also be registered from app code.

## Validate extensions

Expand Down
31 changes: 25 additions & 6 deletions packages/docs/src/content/docs/extend/scheduler-plugin.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: Scheduler Plugin
description: Enable and verify Junior's built-in scheduled task support.
description: Enable and verify Junior's scheduled task support.
type: tutorial
summary: Configure the built-in scheduler plugin so Slack users can create reminders and recurring tasks.
summary: Configure the scheduler plugin so Slack users can create reminders and recurring tasks.
prerequisites:
- /start-here/quickstart/
- /start-here/slack-app-setup/
Expand All @@ -12,20 +12,39 @@ related:
- /operate/reliability-runbooks/
---

The scheduler plugin is built into `@sentry/junior`. It registers Slack tools for creating, listing, updating, deleting, and running scheduled tasks, then uses Junior's internal heartbeat to dispatch due work back to the configured Slack conversation.
The scheduler plugin lives in `@sentry/junior-scheduler`. It registers Slack tools for creating, listing, updating, deleting, and running scheduled tasks, then uses Junior's internal heartbeat to dispatch due work back to the configured Slack conversation.

## Runtime setup

No plugin package install is required. `createApp()` registers the trusted scheduler plugin automatically:
Install the package next to `@sentry/junior`:

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

Register the trusted plugin in app code:

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

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

export default app;
```

List the package in `juniorNitro()` as well so Nitro bundles the manifest:

```ts title="nitro.config.ts"
juniorNitro({
plugins: {
packages: ["@sentry/junior-scheduler"],
},
});
```

The scaffolded `vercel.json` includes the internal heartbeat route:

```json title="vercel.json"
Expand Down Expand Up @@ -80,4 +99,4 @@ For recurring or non-reminder scheduled work, Junior should show the proposed ta

## Next step

Read [Build a Plugin](/extend/build-a-plugin/) for the trusted `tools(ctx)` and `heartbeat(ctx)` APIs that the built-in scheduler uses.
Read [Build a Plugin](/extend/build-a-plugin/) for the trusted `tools(ctx)` and `heartbeat(ctx)` APIs that the scheduler uses.
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:180](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L180)
Defined in: [app.ts:179](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L179)

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:33](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L33)
Defined in: [app.ts:32](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L32)

## Properties

### configDefaults?

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

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

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:43](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L43)
Defined in: [app.ts:42](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L42)

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:44](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L44)
Defined in: [app.ts:43](https://github.com/getsentry/junior/blob/main/packages/junior/src/app.ts#L43)
1 change: 1 addition & 0 deletions packages/docs/src/content/docs/start-here/existing-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ If your existing app already owns routes, make sure the Junior Hono app still re

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

## Add app files
Expand Down
11 changes: 7 additions & 4 deletions packages/docs/src/content/docs/start-here/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Packaged plugins must be installed and listed in `juniorNitro` so Nitro bundles
Install only the plugins you plan to enable:

```bash
pnpm add @sentry/junior-agent-browser @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-sentry
pnpm add @sentry/junior-agent-browser @sentry/junior-datadog @sentry/junior-github @sentry/junior-hex @sentry/junior-linear @sentry/junior-notion @sentry/junior-scheduler @sentry/junior-sentry
```

Then list them in `nitro.config.ts`:
Expand All @@ -121,6 +121,7 @@ export default defineConfig({
"@sentry/junior-hex",
"@sentry/junior-linear",
"@sentry/junior-notion",
"@sentry/junior-scheduler",
"@sentry/junior-sentry",
],
},
Expand All @@ -139,9 +140,11 @@ 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.
For example, `@sentry/junior-scheduler` must be registered with
`schedulerPlugin()` inside `createApp()` to enable scheduled tasks, and
`@sentry/junior-github` must be registered with `githubPlugin()` to enforce Git
commit attribution. See [Scheduler Plugin](/extend/scheduler-plugin/) and
[GitHub Plugin](/extend/github-plugin/) for those setups.

## Verify plugin content

Expand Down
4 changes: 2 additions & 2 deletions packages/junior-evals/evals/behavior-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
import { getAgentPlugins, setAgentPlugins } from "@/chat/plugins/agent-hooks";
import { getPluginOAuthConfig, setPluginConfig } from "@/chat/plugins/registry";
import { generateAssistantReply } from "@/chat/respond";
import { createSchedulerPlugin } from "@/chat/scheduler/plugin";
import { schedulerPlugin } from "@sentry/junior-scheduler";
import { getStateAdapter } from "@/chat/state/adapter";
import { resetSkillDiscoveryCache } from "@/chat/skills";
import { createWebFetchTool } from "@/chat/tools/web/fetch-tool";
Expand Down Expand Up @@ -1679,7 +1679,7 @@ export async function runEvalScenario(
try {
const currentAgentPlugins = getAgentPlugins();
previousAgentPlugins = setAgentPlugins([
createSchedulerPlugin(),
schedulerPlugin(),
...currentAgentPlugins.filter((plugin) => plugin.name !== "scheduler"),
]);

Expand Down
1 change: 1 addition & 0 deletions packages/junior-evals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"devDependencies": {
"@sentry/junior": "workspace:*",
"@sentry/junior-github": "workspace:*",
"@sentry/junior-scheduler": "workspace:*",
"@sentry/junior-sentry": "workspace:*",
"@sentry/junior-testing": "workspace:*",
"chat": "4.29.0",
Expand Down
15 changes: 15 additions & 0 deletions packages/junior-plugin-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export interface AgentPluginLogger {
warn(message: string, metadata?: Record<string, unknown>): void;
}

/** Thrown when a trusted plugin tool rejects invalid model or user input. */
export class AgentPluginToolInputError extends Error {
constructor(message: string, options?: { cause?: unknown }) {
super(message, options);
this.name = "AgentPluginToolInputError";
}
}

export interface AgentPluginContext {
log: AgentPluginLogger;
plugin: AgentPluginMetadata;
Expand Down Expand Up @@ -138,6 +146,12 @@ export interface AgentPluginState {
delete(key: string): Promise<void>;
get<T = unknown>(key: string): Promise<T | undefined>;
set(key: string, value: unknown, ttlMs?: number): Promise<void>;
setIfNotExists(key: string, value: unknown, ttlMs?: number): Promise<boolean>;
withLock<T>(
key: string,
ttlMs: number,
callback: () => Promise<T>,
): Promise<T>;
}

export interface HeartbeatHookContext extends AgentPluginContext {
Expand Down Expand Up @@ -165,6 +179,7 @@ export interface AgentPluginHooks {
}

export interface JuniorPluginConfig {
legacyStatePrefixes?: string[];
packages?: string[];
}

Expand Down
40 changes: 40 additions & 0 deletions packages/junior-scheduler/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@sentry/junior-scheduler",
"version": "0.55.0",
"private": false,
"publishConfig": {
"access": "public"
},
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/getsentry/junior.git",
"directory": "packages/junior-scheduler"
},
"exports": {
".": {
"types": "./src/index.ts",
"default": "./dist/index.js"
}
},
"files": [
"dist",
"src",
"plugin.yaml"
],
"scripts": {
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
"prepare": "pnpm run build",
"prepack": "pnpm run build",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@sentry/junior-plugin-api": "workspace:*",
"@sinclair/typebox": "^0.34.49"
},
"devDependencies": {
"@types/node": "^25.9.1",
"tsup": "^8.5.1",
"typescript": "^6.0.3"
}
}
2 changes: 2 additions & 0 deletions packages/junior-scheduler/plugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name: scheduler
description: Scheduled Junior task management and heartbeat dispatch
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type {
ScheduledLocalTime,
ScheduledTask,
ScheduledTaskRecurrence,
} from "@/chat/scheduler/types";
} from "./types";

/** Parse an ISO timestamp into a finite Unix timestamp in milliseconds. */
export function parseScheduleTimestamp(value: string): number | undefined {
Expand Down
27 changes: 27 additions & 0 deletions packages/junior-scheduler/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export { createSchedulerPlugin, schedulerPlugin } from "./plugin";
export { buildScheduledTaskRunPrompt } from "./prompt";
export {
createSlackScheduleCreateTaskTool,
createSlackScheduleDeleteTaskTool,
createSlackScheduleListTasksTool,
createSlackScheduleRunTaskNowTool,
createSlackScheduleUpdateTaskTool,
type SchedulerToolContext,
} from "./schedule-tools";
export { createSchedulerStore } from "./store";
export type {
ScheduledCalendarFrequency,
ScheduledLocalTime,
ScheduledRun,
ScheduledRunStatus,
ScheduledTask,
ScheduledTaskConversationAccess,
ScheduledTaskCredentialSubject,
ScheduledTaskDestination,
ScheduledTaskExecutionActor,
ScheduledTaskPrincipal,
ScheduledTaskRecurrence,
ScheduledTaskSchedule,
ScheduledTaskSpec,
ScheduledTaskStatus,
} from "./types";
Loading
Loading