Hand an issue to an agent¶
Assign a bot account to an issue, or mention it in a comment, and Mewbo picks the item up in an agent session. One agent-pickup.yml workflow does this on both GitHub Actions and Gitea Actions, posting the item to POST/api/automation/vcs-pickup on your Mewbo API.
How it works¶
flowchart TB
T1["Assign bot"] --> W
T2["Mention bot"] --> W
T3["workflow_dispatch"] --> W
W["agent-pickup.yml<br/>CI runner, read-only token"]
A["POST /api/automation/vcs-pickup<br/>Mewbo API, X-API-Key auth"]
S["Agent session<br/>tag: vcs:owner/repo:kind:number"]
C["Comment on the issue or PR<br/>posted by the bot account"]
W -- "item details, PR branch,<br/>JSON payload" --> A
A -- "owner/repo to project,<br/>branch or worktree" --> S
S -- "session-end hook" --> C
Triggers. The workflow fires on issues: [assigned], pull_request: [assigned], issue_comment: [created], and manual workflow_dispatch. Dispatch takes a required issue_number and an optional prompt. A job guard then decides whether the run proceeds.
- Assignment. Runs when the just-assigned user is
AGENT_BOT_LOGIN. It falls back to the item's full assignees list, which is what Gitea needs. - Comment. Runs when the comment body contains
@<AGENT_BOT_LOGIN>and the author is not the bot. That second condition stops the bot triggering itself in a loop. - Dispatch. Always runs, as the manual override. The dispatch payload carries only a number, so the workflow fetches the item's title, body, and URL from the VCS API.
A concurrency group keyed on the item number serializes runs per issue or PR. Runs already in flight are never cancelled.
Session continuity. The session tag is vcs:<owner/repo>:<kind>:<number>, with kind issue or pull_request, so every trigger on one item lands in one conversation. A prompt that arrives while a run is active is enqueued as a steering message rather than starting a second run.
Working directory.
- Pull request pickups run in a managed git worktree on the PR head branch. The endpoint fetches the branch from
origin, creates the tracking branch and worktree if either is missing, and fast-forwards toorigin/<branch>where it can. The prompt tells the agent to commit and push from there, so the PR updates. - Issue pickups run in an isolated worktree on a deterministic
mewbo/issue-<number>branch cut from the default branch's latest commit, so concurrent pickups never collide. A repeat pickup reuses that branch. Mewbo owns it, so the session-end reaper deletes branch and worktree together.
An issue pickup degrades to the shared main checkout where the project has no managed parent or the worktree cannot be created. The agent is then told to cut its own feature branch.
The pickup prompt closes the issue¶
For an issue pickup, the generated prompt instructs the agent to put a Closes #<issue-number> directive in its pull request description. Merging that pull request closes the issue through the forge's closing keyword convention.
Pull request pickups get no such directive. They already run on the PR's own branch, so the prompt tells the agent to update the existing PR. A full prompt override in the request body replaces the generated prompt entirely, and the Closes # directive goes with it.
Setup, GitHub Actions¶
The workflow file ships in the repo at agent-pickup.yml. Only the secrets and variables need configuring, under Settings, then Secrets and variables, then Actions.
Repository secrets:
| Secret | Required | Purpose |
|---|---|---|
MEWBO_API_URL |
Yes | Base URL of your Mewbo API (for example https://mewbo.example.com). |
MEWBO_API_TOKEN |
Yes | A provisioned Mewbo API key, sent as X-API-Key. |
Provisioning the API key. Mint a dedicated key rather than using the master token. A key from the key store authenticates every route the API key gates, including POST/api/automation/vcs-pickup.
- In the console, open Settings, then API Keys, and create a key labeled for the repo.
- Over the API, call POST/api/keys with the master token.
curl -X POST "$MEWBO_API_URL/api/keys" \
-H "X-API-Key: $MASTER_TOKEN" -H "Content-Type: application/json" \
-d '{"label": "agent-pickup CI (owner/repo)"}'
# -> {"id": ..., "key": "mk_..."} the plaintext is shown exactly once
Store the returned mk_... value as the MEWBO_API_TOKEN repository secret. DELETE/api/keys/{key_id} revokes it and immediately disables every workflow that uses it. The master token is untouched.
Repository variables:
| Variable | Required | Purpose |
|---|---|---|
AGENT_BOT_LOGIN |
Yes | Bot account login to watch for (for example mewbo-ai). Without it, only workflow_dispatch triggers run. |
AGENT_PROJECT |
No | Mewbo project key override. Defaults to owner/repo. |
AGENT_MODEL |
No | LLM model override for the session. |
AGENT_MODE |
No | plan or act. |
Token scope. The workflow declares the least it needs, contents: read, issues: read, and pull-requests: read. The workflow token only GETs issue or PR details, for dispatch and comment pickups whose payload lacks that data. Nothing is written to the repository, because all the work happens inside the Mewbo session.
Important
GitHub does not expose repository secrets to workflows triggered from fork pull requests. Assignment pickup therefore works only for PRs from branches in the same repository. A fork PR's run fails the configuration check, because MEWBO_API_URL arrives empty.
Setup, Gitea Actions¶
Gitea Actions reads the same agent-pickup.yml, so nothing extra needs committing. Configure the same secrets and variables under repo Settings, then Actions, then Secrets and Variables.
The workflow handles three differences from GitHub inline.
- Assignment payload. Gitea's
assignedevent carries noevent.assigneeat the top level, so the guard falls back to the assignees list. A second assignment on an item the bot already holds can therefore re-fire the workflow. That is harmless, because the endpoint resolves the same tag and reuses the session. - API URL. Gitea's
act_runnermay leavegithub.api_urlempty, so the workflow derives<server_url>/api/v1itself. The/repos/{owner}/{repo}/issues/{n}and/pulls/{n}shapes are identical on both platforms, and both accept the workflow token viaAuthorization: token .... - Provider field. The payload's
provideris set togiteawheneverserver_urlis nothttps://github.com. It is informational only.
A runner registered with the ubuntu-latest label must exist. Missing jq is installed via apt-get at run time.
Server-side requirements¶
The Mewbo API maps the owner/repo string to a local project two ways.
- Automatic. A configured project whose git remote matches the repository. Resolution uses the same RepoIdentity alias matching as the worktree routes, so the repo resolves via its Gitea host URL, a GitHub mirror URL,
owner/repo, or the bare repo name. - Explicit. The
AGENT_PROJECTrepository variable overrides theowner/repodefault with a Mewbo project key.
The project path must be a git clone with an origin remote that can fetch PR branches. PR pickups run git fetch origin <branch> in the parent clone before creating the worktree. A pickup whose branch cannot be fetched fails with 422.
Replies back to the issue or PR¶
When a pickup run ends, a session-end hook posts the final answer back to the originating issue or PR as a comment from the bot account. It needs a forge token for the bot under channels.vcs in the server config.
"channels": {
"vcs": {
"tokens": { "git.example.com": "<bot PAT>", "api.github.com": "<bot PAT>" },
"tls_verify": true
}
}
- Tokens are keyed by forge API host, the hostname of the
api_urlthe workflow sends. One Mewbo instance can therefore reply on several forges. - Token identity equals comment author. Mint the PAT for the bot account. On Gitea, an admin can
POST /api/v1/users/<bot>/tokenswith basic auth and the scopesread:user,write:issue, andwrite:repository. On GitHub, use a fine-grained PAT with Issues write plus Pull requests write. The/repos/{owner}/{repo}/issues/{n}/commentsendpoint and theAuthorization: tokenscheme are identical on both. - The same tokens also log in the
teaCLI. The api image shipsteaandgh. 15-tea-setup.sh adds atealogin perchannels.vcs.tokenshost at container start, falling back to~/.git-credentialsfor hosts without one. The agent then opens pull requests as the bot account.tea login addis what needs theread:userscope. - Without a token the reply leg is silently disabled. Pickups still run and the answer stays visible only in the session.
tls_verify: falseopts out of certificate verification, for a forge behind an internal CA the API host does not trust. The Python client uses the system CA store, same as git.- The bot's own comment never triggers a second pickup.
- Answers longer than roughly 60,000 characters are truncated to fit forge comment limits.
Endpoint reference¶
POST /api/automation/vcs-pickup¶
Authenticate with the standard API key in the X-API-Key header. The body is strict JSON and unknown fields are rejected.
| Field | Type | Required | Purpose |
|---|---|---|---|
repository |
string | Yes | owner/repo of the triggering repository. |
kind |
issue or pull_request |
Yes | Item kind. |
number |
int >= 1 | Yes | Issue or PR number. |
provider |
string | No | github or gitea. Informational. |
api_url |
string | No | Forge REST base URL. Enables posting the final answer back as a comment. |
event |
string | No | Triggering event, for example issues or issue_comment. |
url |
string | No | Item HTML URL. |
title |
string | No | Item title. |
body |
string | No | Item description (the workflow truncates at 20,000 chars). |
comment |
string | No | The mention comment text, when comment-triggered. |
comment_author |
string | No | Login of the comment author. |
assignee |
string | No | Login of the just-assigned user. |
bot_login |
string | No | Configured bot login, for self-trigger suppression. |
head_ref |
string | No | PR head branch. Its presence makes a PR pickup worktree-bound. |
base_ref |
string | No | PR base branch. |
project |
string | No | Project key override. Defaults to repository. |
model |
string | No | LLM model override. |
mode |
plan or act |
No | Session mode. |
prompt |
string | No | Full override of the generated pickup prompt. |
Self-trigger suppression. When bot_login is set and equals comment_author, the endpoint returns 200 {"skipped": true, "reason": "comment author is the bot"} without starting anything. The workflow guard blocks the same case before the request is made.
Responses:
| Status | Body | Meaning |
|---|---|---|
200 |
{"session_id", "session_tag", "run_id", "resumed", "worktree_id", "accepted": true} |
Run started. resumed is true when the tag matched an existing session. worktree_id is set for PR pickups and for issue pickups that got an isolated worktree, and null when an issue pickup degraded to the main checkout. |
202 |
{"session_id", "session_tag", "enqueued": true, "resumed": true} |
A run was already active for this item. The prompt was enqueued as a steering message. |
200 |
{"skipped": true, "reason": ...} |
Self-trigger suppressed. |
400 |
{"message": "Invalid input: ..."} |
Body failed validation. |
404 |
error | repository or project did not resolve to a configured project. |
409 |
{"message": "Session is already running."} |
Concurrent-start race lost (rare; the active-run path normally returns 202). |
422 |
{"message": "Failed to prepare branch/worktree ..."} |
PR branch could not be fetched, or the worktree could not be created. |
Testing¶
Work up in four steps.
- Manual dispatch first. Run the Agent Pickup workflow via
workflow_dispatchwith a test issue number. This skips the assignment guard entirely and validates secrets, API reachability, and project resolution. The job log prints the JSON response and the session id. - Scratch issue and assignment. Create a throwaway issue and assign the bot account. An Actions run starts and a session appears in the Mewbo console, tagged
vcs:<owner/repo>:issue:<n>. - Negative check. Assign a user other than the bot to the same issue. The guard must skip the job, so no run starts.
- PR mention. Comment
@<bot-login> <request>on a pull request. A session should start in a managed worktree on the PR head branch. Mention again to confirm the same session continues.
Call the endpoint directly to bypass CI.
curl -X POST "$MEWBO_API_URL/api/automation/vcs-pickup" \
-H "Content-Type: application/json" \
-H "X-API-Key: $MEWBO_API_TOKEN" \
-d '{
"repository": "owner/repo",
"kind": "issue",
"number": 42,
"provider": "gitea",
"event": "workflow_dispatch",
"title": "Scratch issue for agent pickup",
"body": "Reply with a one-line acknowledgement.",
"bot_login": "mewbo-ai"
}'
A 200 with a session_id confirms the server side end to end. Repeat the call to see "resumed": true, or a 202 steering response if the first run is still active.
Next steps¶
- Building a Client covers the session and event primitives a pickup run rides on.
- The full API reference carries every route, parameter, and response shape.