Skip to content

feat(conversations): Name conversations after linked issues - #2218

Merged
BYK merged 4 commits into
mainfrom
feat/conversation-title-linked-issues
Oct 27, 2026
Merged

BYK merged 4 commits into
mainfrom
feat/conversation-title-linked-issues

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

A conversation that starts with "Fix this " gets a vague title like "Verify Issue 1583 and Submit PR if Valid". The title model only sees the URL, so it can't tell what the issue is about.

This adds a provider-neutral conversationTitleContext plugin hook. Before core generates a title from the first human message, it asks plugins for short facts about objects linked in that message. The title model sees those facts under "Linked objects", and the prompt tells it to name the object by number and title (for example, Fix #1583: Login fails on Safari).

The GitHub plugin implements the hook. It finds up to 3 distinct issue, pull request, and commit references (URLs, GitHub autolinks such as owner/repo#123, and prose such as "issue 1583 on getsentry/junior") and reads each title.

The hook reads through host-owned credentialed egress (ctx.egress), not a token it mints itself. Title work runs outside the Turn, so core gives it a conversation-title system actor. Egress policy, grant selection, and audit telemetry apply. The system actor gets only installation grants, never user tokens, and title work never starts authorization. Reads reuse the shared installation lease, so no extra token is minted per object.

The hook is optional and fails open. Core stops waiting after 5 seconds, ignores hooks that throw, and caps how many lines plugins can return and how long each line can be. If nothing comes back, titles are generated the same way as before. Dispatched runs, which have no human message, don't call the hook.

Note: titles from private repositories in the installation can now show up in conversation titles. Junior can already read and quote that content in replies in the same conversation, with the same installation-read grant.

The new lifecycle eval (when the first message links a tracked issue…) needs a Roach recording. I couldn't record it locally without the write token, so it will record on its first CI run.

via BYK.

--

View Junior Session in Sentry

@vercel

vercel Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 27, 2026 5:36pm UTC

Request Review

@sentry-evals

sentry-evals Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Sentry Evals

junior-integration

Run Status Passed Failed Errored Cost Tokens Duration
Head Passed 102 0 0 $0.98 0 351.48s

Base commit 11d1221 has no eval run to compare against.

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally it would detect anything listed here, assuming github: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/autolinked-references-and-urls

Also alternatives like:

  • Let's fix issue 1583 on getsentry/junior
  • Let's get PR 1234 fixed up

etc.

@sentry-junior sentry-junior Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dcramer, about the question of this PR minting its own tokens: it did at first, but head 4498965 changes that. The title context hook now goes through the proxy.

  • The GitHub conversationTitleContext hook reads issues, PRs and commits through ctx.egress.fetch (operations github.issue.get, github.pull.get and github.commit.get). It no longer calls issueInstallationToken or githubRequest.
  • Core builds that egress in packages/junior/src/chat/plugins/conversation-title-context.ts with createPluginEgress. It runs as a conversation-title system actor. That actor gets only installation grants, never user tokens, and it never starts authorization.
  • The host credential proxy picks the grant and adds the token. It reuses the shared installation lease, and egress policy and audit apply.
  • Against main, this PR's diff has no issueInstallationToken or githubRequest calls.

The webhook route on main still mints installation tokens directly. It does this to read PR commit pages for commit classification and to add the "reviewing" reaction on feedback comments. That code is older than this PR. It has no Turn or credential context, so it never used egress. #2222 moves those calls into webhooks/installation-requests.ts. Its oxlint rules ban direct GitHub calls everywhere in the plugin except credential-support.ts and webhooks/. A possible follow-up is to route the webhook calls through egress as a system actor, the same way title context does.

BYK added a commit that referenced this pull request Oct 27, 2026
#2218 first shipped a GitHub plugin hook that minted its own
installation token and called `api.github.com` directly. That skipped
Junior's credential proxy: egress policy, grant selection, the shared
credential lease, and egress audit logs. Review caught it, and #2218 now
reads through `ctx.egress`. This adds lint rules so the next one fails
CI instead of relying on review.

The rules are built-in oxlint rules, applied to
`packages/junior-github/src`:

- `no-restricted-imports` bans `githubRequest` and
`issueInstallationToken` from `credential-support.js`.
- `no-restricted-globals` bans `fetch`, and `no-restricted-properties`
bans `globalThis.fetch`.

The error tells the author to use `ctx.egress.fetch`. Only
`credential-support.ts` and `webhooks/` may call GitHub directly. A
webhook delivery has no Turn and no egress context.

These rules work per file, so this PR also moves the direct GitHub calls
out of `plugin.ts`:

- The webhook commit-page and feedback-reaction requests move to
`webhooks/installation-requests.ts`.
- The `issueCredential` body moves to `issueGitHubCredential` in
`credential-support.ts`. It needs an `IssueCredentialHookContext`, so
other hooks cannot easily misuse it. The installation and user
credential helpers are no longer exported.

The rules live in the shared `packages/junior/.oxlintrc.json` with
`**/junior-github/...` globs. I first tried a package config that
extends the shared one, but oxlint 1.79 then fails to load the
`anti-slop` JS plugin. The relative `jsPlugins` path does not resolve
when the base config is in another directory. Override globs also
resolve from the config's own directory, so the globs must use
`**/junior-github`.

Verification:

- `pnpm lint`, `pnpm typecheck`, and the GitHub plugin tests (233) pass.
- A probe file in `src/` that imports `issueInstallationToken` and
`githubRequest` (direct and nested paths) and calls `fetch` and
`globalThis.fetch` gets 8 errors. That is the same pattern as the
original #2218 bypass. The new `webhooks/installation-requests.ts`
passes.

The rules only cover the GitHub plugin. Someone could still get around
them, for example by re-exporting a function from another module. The
rules are there to catch honest mistakes, not to enforce a security
boundary.

If #2218 merges first, this branch needs a rebase. Both change the
imports in `plugin.ts`.

<!-- junior-request-attribution:start -->
via **BYK**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:local%3Aweb%3A705d61c691f47edcb331770c -->

--

[View Junior Session in
Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A705d61c691f47edcb331770c/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <burak.kaya@sentry.io>
sentry-junior Bot and others added 4 commits October 27, 2026 17:32
Add a conversationTitleContext plugin hook. Core passes the first human
message to plugins and gives the returned facts to the title model under
Linked objects. The GitHub plugin reads the titles of linked issues and
pull requests with a read-only token scoped to each repository.

Co-Authored-By: BYK <burak.kaya@sentry.io>
…itles

Detect issue, pull request, and commit references in the forms that
GitHub autolinks, and in prose such as "issue 1583 on getsentry/junior"
or "PR 1234". A reference without a repository uses the one repository
that the message names, or else the configured github.repo. The title
context hook now gets the plugin's config values for the Location.

Co-Authored-By: BYK <burak.kaya@sentry.io>
Add the linked object instruction only when plugins return title context.
Other title requests then match their eval recordings again, so evals such
as the image edit case replay instead of running live.

Co-Authored-By: BYK <burak.kaya@sentry.io>
The title context hook minted its own installation token and called
GitHub directly. That skipped Junior's credential proxy: egress policy,
grant selection, lease reuse, and audit telemetry.

Core now gives the hook host-owned egress as a conversation-title system
actor. The GitHub hook reads issues, pull requests, and commits through
it. The system actor gets only installation grants, never user tokens,
and title work never starts authorization. Reads reuse the shared
installation lease, so title context no longer mints a token per object.

Co-Authored-By: BYK <burak.kaya@sentry.io>
@blacksmith-sh

This comment has been minimized.

@BYK
BYK merged commit 9c70698 into main Oct 27, 2026
63 of 65 checks passed
@BYK
BYK deleted the feat/conversation-title-linked-issues branch October 27, 2026 19:20

This branch was successfully deployed

1 active deployment
Preview – junior-docs — b2e632f1 Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant