Routing GitHub mentions to Codex and Claude draft plan
Extend the existing GitHub Worker with a separate managed-agent module. Its minute cron and D1 follow requests to the OpenAI Agents API for @codex and Claude Managed Agents for @claude. Both providers host execution. SmolPaws posts attributed replies; enyst's write access belongs to later code publication and maintainer operations.
Requests from enyst or SmolPaws reach the same durable ledger, through GitHub comments or an authenticated direct call. A first version returns findings and proposed patches as comments. The hosted routes use their own authorization and lifecycle code inside the GitHub app, independently of SmolPaws's local agent runner.
Revised proposal. The managed routes have not been implemented or activated.
Keep hosted routing inside the GitHub app
smolpaws/apps/github already contains a Cloudflare Worker, a minute cron, notification intake, a queue and GitHub replies. Ordinary SmolPaws requests go through agentServerClient.ts to the local runner. The hosted routes should be selected before that dispatch and handled by their own module. GitHub Worker source, Worker configuration, and local runner adapter.
apps/github/
src/index.ts Worker wiring and route selection
src/managedAgents/
index.ts accept(request), tick(), status(runId)
lifecycle.ts Dispatch, observation and publication
ledger.ts D1 requests, leases and reply outbox
providers/openai.ts OpenAI managed sessions
providers/claude.ts Claude managed sessions
migrations/ Shared durable intake and routing schema
This is a proposed layout. Keep the module free of imports from the agent server, coordinator, personal memory and local runner protocol. Pass its storage, clock, HTTP access and comment writer in explicitly. Provider-specific session formats stay inside the two adapters. The direct SmolPaws tools are clients of this module's authenticated Worker interface.
A recognized managed command is consumed even if it is rejected; it must not also reach the local runner. Keep the hosted author allowlist separate from the ordinary SmolPaws allowlist. Supply each route only the credentials it needs.
Use one D1 database for durable intake receipts and managed requests. Ordinary SmolPaws requests retain their queue and runner path; hosted jobs progress from D1 on each cron tick. Replace Cache API admission checks with durable receipts, including a migration baseline so old mentions do not run again. Cloudflare describes its cache as ephemeral and local to a data center. Cache API storage properties.
Require two integration checks: hosted requests work with the local runner unavailable, and disabling hosted routes preserves ordinary SmolPaws requests. The shared Worker remains one deployment, so releases affect both paths. If independent deployments become useful, the small module interface provides a place to extract a separate Worker. The local github-mention-router draft holds the contract and backlog until implementation moves into the GitHub app.
GitHub access determines the trigger
The target supplied as OpenHands/agent-sdk currently resolves to OpenHands/software-agent-sdk, repository ID 1043426082. Store the ID as the boundary and resolve the current name during preflight. A rename should change API paths, not silently change which repository may receive work.
Push access is enough to propose code through a writable branch. It does not establish authority to configure a repository webhook. GitHub requires repository ownership or admin access to create one. Polling its public comment API avoids that dependency. If an administrator later adds a webhook, it can feed the same request ledger while polling remains a reconciliation path. GitHub webhook requirements.
smolpaws can leave requests and publish the agents' replies through its own comment access. It has no labeling or maintainer permissions. The router checks request author IDs and chooses the writing identity by action: smolpaws for ordinary comments, and enyst for later authorized code publication. Labeling and other maintainer tasks require their own authorized path. The model providers do not become repository members through this arrangement.
| Action | Who performs it | Required access |
|---|---|---|
| Read public comments and source | Router and hosted sandbox | Public read access; authenticated API reads give a larger rate allowance. |
| Request Codex or Claude | enyst or smolpaws | Ability to comment, plus the router author allowlist. |
| Publish a result comment | Router as smolpaws | SmolPaws's existing comment access. |
| Publish a branch and draft PR | Router as enyst, in the later code phase | Contents and pull request write access to that destination. |
| Label or perform maintainer tasks | enyst, through a separately authorized path | The permission required by that operation; outside the comment route. |
| Install a repository webhook | A repository administrator | Separate administrative authority. |
A small controller on Cloudflare
Use the existing Worker cron plus D1. D1 owns accepted requests, scan progress, provider session IDs, next observation times and the reply outbox. Each minute, the scheduled handler claims due work with expiring leases, performs bounded HTTP operations, saves progress and exits. The next tick resumes the same rows. Cron Triggers and D1.
The hosted provider runs the agent's tools while the controller is idle between ticks. A twenty-minute job watchdog spans multiple invocations; the Worker never waits inside one invocation for the whole job. Cloudflare limits cron invocations to fifteen minutes of elapsed time. Set a much smaller tick budget and persist pagination continuations when it is reached. Worker invocation limits.
V1 polls provider state and events once per minute. Workflows and provider webhooks can follow if measured latency or volume justifies them. The existing queue serves ordinary SmolPaws runner traffic; hosted jobs use the D1 lifecycle directly.
- A GitHub comment or direct SmolPaws requestVerified identity, explicit provider and allowed repository.
- The GitHub Worker accepts the requestComment intake and authenticated tools use the same managed-agent module.
- D1 records a unique requestThe existing minute cron advances its durable lifecycle.
- The selected hosted agent works
@codexOpenAI Agents API
Managed Codex harness@claudeClaude Managed Agents
Managed Claude harness - Router collects and publishes the resultAttributed GitHub reply, assessed commit, test evidence, and a durable receipt.
The hosted APIs fit this job
Codex through the OpenAI Agents API
OpenAI's Agents API runs a managed Codex harness with durable sessions and optional hosted sandboxes. It handles the agent loop, context compaction, and recovery. Start with POST /v1/agents/sessions and an openai_hosted environment. Select and pin a model available to the deployment account. Official OpenAI documentation.
The documented beta header is OpenAI-Beta: agents=v1. The application key needs api.agents.read, api.agents.write, and api.responses.write. Keep it in the controller. Follow the quickstart with a controlled task before using the target repository. Agents API prerequisites.
Claude through Claude Managed Agents
Anthropic's managed service also supplies a harness and cloud sandbox. Configure an agent and environment, then create a session and send a user event; initial_events can combine creation and the first request. Its endpoints use anthropic-beta: managed-agents-2026-04-01 with the API's normal version header. The documentation says Managed Agents is enabled by default for API accounts; the deployment still needs a working API key and its own smoke test. Managed Agents overview and Starting a session.
These routes create configured agent sessions. Supply the skills and instructions they need explicitly. Existing personal chats, desktop tasks, and their memory are not inputs in this design.
Define exactly which comments count
Keep the original @codex and @claude commands in V1. The existing SmolPaws notification poller can suggest threads to inspect, but it does not supply a complete stream of individual requests. GitHub notifications describe threads, and a thread's mention reason can persist through later activity. Several requests between ticks must all be enumerated. Review summaries and PR descriptions also remain distinct source types. GitHub notification semantics.
GitHub separates PR conversation comments, inline review comments, and review summary bodies. The first two have repository-wide feeds, so they make a clear first-version boundary. Issue comment API and Review comment API.
| Surface | Intake | Scope |
|---|---|---|
| Issue comments and PR conversation comments | GET /repos/{owner}/{repo}/issues/comments | V1 |
| Inline PR review comments and replies | GET /repos/{owner}/{repo}/pulls/comments | V1 |
| A submitted review's summary body | Per-PR review enumeration or a pull_request_review webhook | Later; a separate intake path is required. |
| Issue/PR descriptions, commit comments, Discussions | Separate APIs and event types | Outside the first comment watcher. |
For either V1 feed, use since, sort by update time ascending, request up to 100 items per page, and follow every Link page. The API's since filter is based on last update, so edits will appear in scans too.
Capture the scan's start time. Read from the last completed scan minus a five-minute overlap, persist discovered requests, and advance that surface's cursor only after all pages succeed. Keep a lease to prevent overlapping scans and retain a wider reconciliation scan for delayed observations. A failed page must leave the completed cursor unchanged. When the tick budget is reached, save the next-page continuation and resume later. Polling cannot recover a comment deleted before it is ever observed.
At activation, establish a baseline. Accept new comments created after it; do not replay old requests. Freeze the first qualifying observed body. Editing an accepted comment must not start another paid job. Ask again in a new comment for a new request. The initial observed snapshot can already include an edit made between scans.
Authorization and mention parsing
Allow immutable GitHub author IDs for enyst and smolpaws. Read them from GitHub metadata. A copied name, a quoted request, and author_association do not authorize work.
Parse actual mention tokens in authored Markdown text, ignoring code, blockquotes, and URL destinations. Match case-insensitively with handle boundaries: @codex-example must not launch Codex. Both actual mentions in one comment create one request per provider. Router-generated replies must be excluded even though their GitHub author is allowlisted.
@codex review this change for cancellation and cleanup bugs @claude explain whether the proposed API preserves replay behavior
Before dispatch, fetch the source again. Deletion or removal of that provider's mention cancels work that has not started. Other edits leave the frozen prompt unchanged. Put the approved request ahead of the surrounding thread, label each context comment with its author and source, and cap context at twenty comments and 32 KiB. Reject approved requests over 16 KiB instead of silently changing them. Show when context was truncated.
Let SmolPaws submit directly
Add ask_codex and ask_claude tools that submit to an authenticated Worker endpoint, plus a tool to read request status. A dedicated service credential identifies SmolPaws; the caller cannot select another principal through its payload. Require a stable client request ID, an allowed repository and an explicit issue/PR destination. A retry returns the same durable request.
Direct requests use the same provider adapters, budgets and reply outbox as GitHub comments. SmolPaws can still request through a GitHub comment when a public request is useful. Keep generated reply IDs and output markers in the exclusion set rather than excluding every SmolPaws-authored comment. These controls also protect recovery when a GitHub post succeeds but its response is lost.
A durable request and a durable reply
For GitHub intake, the request key is repository ID + surface + comment ID + provider. For direct intake, use the authenticated principal, client request ID and provider; a repeated ID with different content is a conflict. Put uniqueness constraints on both. Store the frozen request, policy version, target commit, session ID, event cursor, next attempt time, lease version and publication receipt.
D1 is the lifecycle authority. Admit the request and reserve its budget atomically, then let cron claim and advance due rows. A repeated scan or direct call returns the existing run. Use conditional state updates with a lease owner and version so an expired worker cannot overwrite newer progress. D1 batches support transactional statements, but a provider call or GitHub post remains a separate remote mutation. D1 batch transactions.
accepted → dispatching → running → result ready → publishing → delivered Other outcomes: awaiting action · budget paused · failed · canceled Ambiguous responses: dispatch unknown · delivery unknown
A timeout after session creation is not proof that creation failed. If the provider accepted work but the controller lost the response, a blind retry can start another paid session. Verify the endpoint's supported idempotency and reconciliation mechanisms during implementation. When recovery cannot establish the outcome, park the request for an operator. Apply the same rule to sending input events and GitHub publication.
Completion must be checked against the request
Fetch session state and new events on each due observation. Idle can mean waiting for permission or input; an unfinished provider run may also remain open. Require the current request's answer and a compatible completion reason. Persist the event cursor and bounded result before marking it ready for publication. OpenAI session lifecycle and Claude session operations.
Recover after a missed tick by selecting overdue rows from D1. Back off transient read failures. Permission waits, budget pauses and known failures should produce an attributed status reply. Ambiguous dispatch or publication outcomes require reconciliation and an action-required status in the authenticated status tool. A lost publication response must not cause another status post before its receipt is resolved.
Use one session per request in V1 and include a bounded, current thread snapshot. Serialize work for the same thread and provider. Reusing one durable session per thread is a later enhancement: it needs per-request event ranges, head-change handling, and a policy for pending requests while the agent is running.
Publish through an outbox
Prepare the reply in D1 before calling GitHub. Its header should identify the provider, configured model and assessed commit SHA. Follow with the source request, findings and actual test evidence. Write a stable hidden run marker into the comment and record its GitHub ID after success. After a lost acknowledgement, reconcile the fully paginated destination thread for that marker under SmolPaws's writer ID before another attempt.
Replies posted as smolpaws should identify whether Codex or Claude produced them. Record generated comment IDs and filter them before routing; also avoid copied mention tokens that ping or re-trigger agents. This closes the feedback loop where an agent quotes its original request in its own answer.
Keep writing credentials in the controller
The comment route needs SmolPaws's GitHub credential and provider API credentials. Provision them into Worker secrets during deployment. Keep enyst's separate write credential for the later code publication path; a failed comment post must not escalate to it. Keep credentials out of prompts, D1 payloads, logs, and agent-visible files or environment variables. Public source can enter a sandbox as a commit-pinned archive or through a public clone; neither requires a writing credential.
OpenAI's hosted sandbox accepts input files and setup commands and offers restricted network access. Configure the network explicitly and supply the dependencies required by the SDK's real tests. Anthropic supports GitHub repository mounts with an authorization token; that is an optional integration, not a prerequisite for this public-repository plan. OpenAI hosted sandbox configuration and Claude GitHub access.
The agent can produce findings and patch artifacts while deterministic router code controls publication. Repository instructions and other authors' text can guide analysis, but cannot grant the agent secrets, a new repository destination, or extra write permissions.
Add code changes through branches and draft PRs
After the comment path is proven, explicit requests such as @codex implement … or @claude implement … can produce a patch against a pinned commit. The router should verify that it applies to the allowed repository, reject paths outside its publication policy, and publish to a deterministic proposal branch followed by a draft PR.
Before publishing a review or patch, recheck the target head. If the PR changed, assess the new head or clearly retain the result as a report on the earlier commit. Base-repository write access does not grant access to a contributor's fork branch: use a writable fork or propose a new branch in the allowed repository.
For the initial rollout, use ordinary comments for reviews. Formal approval and requests-for-changes affect repository gates and should be a separate policy choice. Direct pushes to the target's default branch and automatic merging stay outside this plan.
Bound jobs and measure cost
Two single-page comment feeds at one-minute intervals make 120 base GitHub reads per hour, before pagination, context fetches, and reconciliation. Read the response rate headers and back off on primary or secondary limits; the allowance is shared with other use of the same credential.
Reusing the Worker avoids another deployment, while D1 operations and provider usage still add cost. Measure actual API reads, model usage, tools and hosted execution on the fixture before setting the operating budget. Cloudflare pricing and OpenAI Agents API billing.
Proposed starting limits are two concurrent jobs, one per thread/provider, twenty requests per day, and a twenty-minute watchdog. Reserve a daily admission budget atomically before launch. Treat the watchdog and controller budget as limits on new work, with in-flight usage still possible.
Claude supports a session budget set at creation. It stops issuing new model requests at its list-cost cap, but an in-flight request can take the final amount beyond that cap. Keep that margin and publish a paused status when it is reached. Verify current OpenAI controls independently instead of copying Claude's budget fields. Claude session budgets.
Roll out in stages with evidence
- Wire the module in shadow mode. Move the contract and implementation tasks into the GitHub app and its Beads tracker when implementation begins. Add durable intake and bounded cron progression. Prove author filtering, parsing, pagination, missed-tick recovery, direct-call authorization, migration baselines and separation from the local runner. Make no managed provider calls or new router posts.
- Prove both routes on a controlled fixture. Check account access, sandbox dependencies, current endpoint behavior and actual cost. Exercise restarts, response-loss recovery, stale heads, permission waits, budget pauses and output loops. Require one attributed current result per successful request and visible failure status.
- Enable bounded target replies. Set the activation baseline and enable comments on the allowed repository. Keep managed intake, each provider and publication independently switchable. Verify ordinary SmolPaws requests still work.
Branches and draft PRs follow as a separate code publication phase using enyst's credential. Review summaries and persistent session continuation also remain later work.
Review summaries need their own later intake work: enumerate reviews for candidate PRs and verify whether review activity advances the candidate timestamp. Include reconciliation for closed PRs. If webhook administration becomes available, a pull_request_review event is a cleaner trigger for that surface. Do not claim the two V1 feeds cover every GitHub comment type.
Decisions to make at implementation time
- Choose provider accounts, models, sandbox setup, and per-request spending limits after a real preflight.
- Confirm the publication policy: comments first, then which implementation requests may create draft PRs.
- Choose a dedicated SmolPaws credential for replies with the minimum access available under the organization policy. Provision enyst's separate credential only when the code publication phase is authorized.
- Decide whether review summary bodies need priority before target activation, and whether follow-up comments should eventually reuse sessions.
Original draft: 12 October 2026. Worker source, reply identity, cron lifecycle and module placement revised on 14 October 2026. API lifecycle and platform behavior checked against the linked official sources. The module layout, limits and rollout are proposed design choices.