Skip to content

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:41
a481358

v0.3.1 lets a run start runs of its own agent, lets an approval offer options, and gives @chamvi/github the tools to work pull requests. It covers everything since v0.3.0.

📣 Some News

Every package is at 0.3.1 again

v0.3.0 left @chamvi/http, channel-cron, channel-openclaw and tool-web at 0.2.2. This release puts all sixteen packages back on one version, 0.3.1, so you can pin ^0.3.1 everywhere. The four packages that skip 0.3.0 gain nothing breaking on the way.

👀 Highlights

🤝 Hand-offs: a run starts runs of its own agent

A tool can start a new run of its own agent with startHandoff(ctx, { payload, idempotencyKey?, concurrencyKey?, address? }). It is fire and forget: the parent goes on at once, and the tool gets { runId, deduplicated } back.

This helps when one run would otherwise do everything under one budget. A daily sweep that finds ten pull requests needing work can hand off one run per pull request instead. Each child gets its own budget, its own approvals and its own concurrency key. An agent opts in with handoff.on() on the new agent channel:

const handOffPr = defineTool({
    description: 'Starts a run of its own for one pull request that needs work.',
    inputSchema: z.object({ pr: z.string().describe('owner/repo#number') }),
    execute: async ({ pr }, { context }) => {
        // the day the daily run was fired, not the wall clock: a redo keeps the key
        const day = context.run.trigger.receivedAt.toISOString().slice(0, 10);
        return startHandoff(context, {
            payload: { pr },
            idempotencyKey: `${pr}@${day}`,
            address: pr,
        });
    },
});

const maintainer = defineAgent({
    name: 'maintainer',
    instructions: 'Every morning, hand off each pull request that needs work.',
    channels: [
        cron.on('0 6 * * *'),
        // the opt-in: runs of this agent may start runs of this agent
        handoff.on({
            schema: z.object({ pr: z.string() }),
            concurrencyKey: (p) => p.pr,
            maxPerRun: 20,
        }),
    ],
    tools: { hand_off_pr: handOffPr },
});

The limits hold across crashes, approval pauses and tool code that misbehaves:

  • A child inherits the parent's invoker snapshot, so its grants never widen.
  • The depth is 1: a handed-off run cannot hand off again.
  • maxPerRun (default 100) is a durable count.
  • A redone call returns the same child run.
  • A given idempotencyKey dedupes across runs, so tomorrow's sweep does not start a second run for the same pull request on the same day.

The dashboard's run page links a child to its parent.

Note

@chamvi/app now registers a channel named agent. A channel of your own with that name fails the app setup with the duplicate-channel error. On a bare Engine, add agentChannel() to channels yourself.

📖 Hand-offs

🗳️ Approvals with options

approve() can now ask the owner to choose instead of just answering yes or no. Pass options (up to 20, each with an id, a label and an optional detail) and multiple: true for a multiple choice. The decision then carries selected: the chosen ids, in the order of your options.

const pickRules = defineTool({
    description: 'Asks which new lint rules to adopt.',
    inputSchema: z.object({}),
    execute: async (_input, { context }) => {
        const decision = await approve(context, {
            title: 'Adopt which new rules?',
            body: 'Each option shows what the rule changes.',
            options: [
                { id: 'unicorn/no-null', label: 'unicorn/no-null', detail: '-const user = null;\n+const user = undefined;' },
                { id: 'unicorn/prefer-at', label: 'unicorn/prefer-at', detail: '-items[items.length - 1]\n+items.at(-1)' },
            ],
            multiple: true,
        });
        if (!decision.approved) return `Adopt none: ${decision.reason ?? 'denied'}.`;
        return `Adopt ${decision.selected?.join(', ')}.`;
    },
});

Every place that shows an approval also shows its options:

  • Dashboard: renders a radio or checkbox per option, with its detail in a collapsible block (a diff is colored by line). Approve stays disabled until the selection is valid.
  • Console, GitHub and OpenClaw channels: list <id>: <label> and link to the dashboard. They never send the detail.
  • REST and MCP: POST /runs/:id/approve and approve_run take selected. A selection that does not fit the wait point is a 400 validation_error.

Note

On resume, approve() checks the recorded answer against the options the tool passes now. If they no longer fit (an id dropped, a multiple choice turned single), it returns a denial with the reason options changed, so your tool never gets an id it did not offer. A plain approve() without options works exactly as before.

📖 Offering options

🐙 Pull request tools for @chamvi/github

@chamvi/github now has the tools a maintainer agent needs to work through pull requests, and one App can serve several accounts from one run.

  • Per-repository installations: each tool acts as the installation of the repository it names. It looks that installation up with GET /repos/{owner}/{repo}/installation and caches it for 10 minutes, so one App installed on several accounts and organisations reaches all of them. IGithubApp.repositoryInstallation() is public.
  • New read tools: github_list_repositories, github_list_issues, github_list_pull_requests and github_get_pull_request_checks (check runs plus commit statuses, with the required contexts from branch protection and rulesets). There is also github_get_push_target, which says whether the App can push to a pull request's head branch. github_get_pull_request now also returns the head repository, fork status, mergeability and labels.
  • New effect tools: github_create_issue, github_create_pull_request and github_merge_pull_request. The first two find their earlier result by its effect marker, so a redone step creates nothing twice.
  • selfEvents on github.on(): events the App's own bot may start a subscription with, such as the pull requests the agent opened itself. issue_comment, issues and pull_request_review_comment are refused, so the bot's own comment never starts the agent again.
github.on(['pull_request.opened', 'check_suite.completed'], {
    // the agent's own pull requests start it too
    selfEvents: ['pull_request.opened', 'check_suite.completed'],
}),
// ...
tools: () => ({ ...gh.readTools(), ...gh.writeTools({ mergeMethod: 'squash' }) }),

github_merge_pull_request takes the sha the agent reviewed and re-reads the pull request before it merges. It refuses with ToolDeniedError in any of these cases:

  • the head moved off sha
  • the pull request is a draft, not mergeable or blocked
  • the required checks cannot be read
  • CI has reported no check yet
  • any check is still pending
  • a required check failed

A failing check that is not required still merges (unstable).

Warning

Never put the App on a ruleset bypass list or a branch protection exception. GitHub reports a blocked pull request as clean to a token that may bypass, so protection would no longer stand between the model and the default branch.

Tip

Name the actions in selfEvents. A bare pull_request also takes the labeled, closed and synchronize events that the agent's own labels, merges and pushes cause, so every such write starts the agent again.

📖 GitHub App tools · @chamvi/github reference

⚠️ Heads-Up Before Upgrading

Change What to do
@chamvi/app registers a channel named agent Rename any channel of your own called agent
A GitHub tool call that finds no installation is now a github_request tool error, not a FatalToolError Do not rely on that call failing the run. A public repository the App is not installed on is now readable through the fallback installation
The new pull request tools need more App permissions Grant Pull requests write and Checks and Commit statuses read where an agent uses them

⬆️ Upgrading

npm install @chamvi/app@^0.3.1 @chamvi/cli@^0.3.1

No store migration is needed. Hand-off lineage and approval options live in existing jsonb columns. Bump every other @chamvi/* package you use to ^0.3.1.

👉 Changelog

v0.3.0...v0.3.1

Features

  • core: start runs of the same agent from a tool (#64) (083c385)
  • core: approvals with options (#65) (994da69)
  • github: pull request tools and per-repository installations (#66) (1a87a3a)

Bug Fixes

  • deps: bump @modelcontextprotocol/server from 2.3.1 to 2.4.0 (#62) (2c2d9db)
  • ensure consistent version for release (#67) (c08d0fb)