Can one agent start another? Queueing sub-tasks in GitHub Agentic Workflows

Consider a "dark factory" setup: a software factory with almost no people on the production line. A small number of people orchestrate the system, but agents do most of the work: planning, coding, reviewing, securing, deploying and reporting back. The goal is to define the factory itself as code.

Can one agent start another? Queueing sub-tasks in GitHub Agentic Workflows
Photo by Micha Frank / Unsplash

Consider a "dark factory" setup: a software factory with almost no people on the production line. A small number of people orchestrate the system, but agents do most of the work: planning, coding, reviewing, securing, deploying and reporting back. The goal is to define the factory itself as code.

For that to work, agents need to trigger on each other's work with minimal human interaction. One agent may create an issue, another may turn it into a plan, another may implement the code, another may review or secure it, and another may prepare the deployment. The hand-off can happen through an IDE, issues, pull requests, comments, workflow dispatches or other integration points, but the operating model assumes that most work is performed by agents.

That ambition quickly runs into GitHub's accountability model. GitHub has designed Copilot coding agent and Actions interactions so that certain work cannot silently cascade from one agent to another without a human or accountable identity in the loop. The visible symptom may look like an API problem:

POST /agents/repos/{owner}/{repo}/tasks
→ 404

The 404 is the product of deliberate interaction boundaries around identity, billing, legal accountability and human oversight. Understanding those boundaries determines which orchestration patterns are viable today and which remain unavailable.


The accountability rule that explains everything

From GitHub's docs on the agent tasks API:

The agent tasks API only supports user-to-server tokens. You can authenticate using a personal access token, an OAuth app token or a GitHub App user-to-server token.

Server-to-server tokens, such as GitHub App installation access tokens, are not supported.

The GITHUB_TOKEN your workflow receives automatically is an installation access token. It is server-to-server, and there is no user behind it — which is precisely its purpose.

So when a workflow attempts to start a cloud agent session with GITHUB_TOKEN, GitHub is not confused about permissions. It rejects the request because there is no accountable party.

That accountability matters for three concrete reasons:

  1. Licensing and cost. A cloud agent session consumes AI Credits (AIC), and those are charged to the token owner. With no user behind the token, there is nobody to charge.
  2. Abuse prevention. If a workflow token could spawn agents, any repository with a workflow could provision unlimited agent capacity without a single Copilot licence.
  3. Responsibility. When an autonomous agent produces questionable changes, there must be an identity on the audit trail.

gh-aw states this plainly in its own source (pkg/workflow/github_token.go):

// Note: The default GITHUB_TOKEN is NOT included as a fallback because it does not have
// permission to create agent sessions, assign issues to bots, or add bots as reviewers.

The distinction that matters: sub-agent vs. new agent session

This is the misconception at the root of the problem, and it is worth stating explicitly.

The two options may sound similar, but they run in different places, use different identities and carry different accountability boundaries:

Sub-agent Cloud agent session
Where it runs Same runner, same job GitHub-hosted, separate lifecycle
Trigger In-process, same run POST /agents/.../tasks
Auth Inherits the run's context Needs a user token
Who's accountable Whoever/whatever triggered the workflow The token owner
Cost model Part of the run's inference AI Credits charged to the token owner

A sub-agent is comparable to a function call. A cloud agent session is comparable to delegating work to an external party.

Which raises the question that resolves the whole problem: why is a separate agent session needed at all? Why can the workflow's own sub-agent not implement the code?

It can. The agent already running in your workflow has a checked-out repository, a filesystem, and a shell. It can write the code, run the tests, and open the pull request. Spawning a second agent to perform work the first agent is already positioned to do adds indirection — and it is precisely that indirection which requires the token you do not have.


Pattern 1: Do the work in-run (start here)

This is the pattern that works today, requires no PAT for the coding path, and is most likely what you actually need.

---
on:
  issues:
    types: [labeled]
permissions:
  contents: read
  issues: read
  copilot-requests: write   # org-billed inference, no PAT
engine: copilot
safe-outputs:
  create-pull-request:
    draft: true
  add-comment:
---

# Implement the labelled issue

Only proceed if the triggering issue carries the label `agentic:code-needed`.

1. Read the issue body and any linked design notes.
2. Implement the change directly in the working tree.
3. Run the test suite. If tests fail, fix and re-run — do not open a PR with red tests.
4. Open a **draft** pull request describing what you changed and why.
5. Comment on the originating issue linking the PR.

The agent edits files on the runner. gh-aw's safe-output layer takes the resulting diff, creates the branch, and opens the pull request through a controlled, permission-scoped code path — the agent itself never holds a write token.

One constraint to be aware of: a pull request opened with GITHUB_TOKEN produces a restricted event. From the Actions events reference:

When a pull request is created or updated by a workflow using GITHUB_TOKEN, pull_request events [...] create workflow runs that require approval. [...] With the exception of workflow_dispatch and repository_dispatch, other GITHUB_TOKEN-triggered events do not create workflow runs at all.

So your CI may wait for manual approval. This is the same accountability principle in a different form. If you need CI to run automatically, use repository_dispatch, or configure gh-aw's CI-trigger token with a PAT.


Pattern 2: Two-phase with a gate

If you genuinely want plan-then-implement as separate runs, split them across an artifact GitHub can observe — an issue, a label, a comment.

One practical shape is:

  1. A schedule, issue or other trigger starts a planner workflow.
  2. The planner workflow creates or updates an issue and applies a label such as agentic:code-needed.
  3. A gate is applied, either by a human or by a separate approval signal such as agentic:approved.
  4. The builder workflow starts on the approved label or dispatch event.
  5. The builder workflow's own agent or sub-agent implements the change and runs tests.
  6. The workflow opens a pull request using create-pull-request.
# planner.md
safe-outputs:
  create-issue:
    labels: [agentic:code-needed]
    max: 3
# builder.md
on:
  issues:
    types: [labeled]

Same caveat: an issue created by GITHUB_TOKEN won't trigger the issues.labeled run. Options:

  • Have a human apply the approval label — often exactly the gate you want anyway.
  • Use repository_dispatch from the planner.
  • Give the planner's safe output a PAT so the created issue carries a user identity.

Note that the third option reintroduces the human implicitly: the PAT owner is the accountable party, having pre-authorised the work rather than approving each instance.


Pattern 3: Spawning a cloud agent session (supported, with a PAT)

If you require a genuinely independent agent session, gh-aw supports it. The requirement is an explicit user identity.

safe-outputs:
  create-agent-session:
    base: main
    max: 1                    # default 1, maximum 10
    github-token: ${{ secrets.GH_AW_AGENT_TOKEN }}

Or, for existing issues:

safe-outputs:
  assign-to-agent:
    target: triggering
    github-token: ${{ secrets.GH_AW_AGENT_TOKEN }}

The token requirements, per gh-aw's Copilot Cloud Agent reference:

Fine-grained PAT — repository permissions:

  • Read: metadata
  • Read & write: actions, contents, issues, pull requests

Classic PAT — the repo scope.

gh aw secrets set GH_AW_AGENT_TOKEN --value "<your-pat>"

GH_AW_AGENT_TOKEN is a magic secret — gh-aw looks for it by name, so you don't reference it in frontmatter. The resolution chain the compiler emits is GH_AW_AGENT_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN, and that last fallback is the one that 404s.

A GitHub App is not an alternative here. gh-aw's documentation is explicit: "The Copilot assignment API requires a Personal Access Token [...] GitHub App installation tokens are rejected regardless of permissions." An App is server-to-server, so the same constraint applies.

This pattern works, but consider what it implies. A PAT is a standing delegation of a specific person's identity, Copilot entitlement, and AI Credit balance. The "no human in the loop" claim becomes "one person pre-authorised everything, indefinitely, until the token expires." That is a legitimate architecture, but it should not be mistaken for having removed the human.


Pattern 4: Fan-out inside a single run

For parallelism without spawning sessions, keep the work inside the run. gh-aw caps every safe output:

safe-outputs:
  create-issue:
    max: 5
  create-agent-session:
    max: 3          # each still needs the PAT

Your workflow agent can analyse, decide, and emit up to max outputs from a single run. The bound is deliberate: an agent able to spawn agents without limit is a fork bomb with a cost attached.


Summary

Supported today

Agent writes code and opens a PR in one run ✅ No PAT needed for the code path
Sub-agents / custom agents inside a run ✅ Same runner, same job
Multi-workflow chains via issues/labels/comments ✅ Subject to the GITHUB_TOKEN event rule
Spawning cloud agent sessions ✅ With a user PAT
Assigning Copilot to issues/PRs ✅ With a user PAT
Cross-repo agent sessions ✅ Explicit target-repo / allowed-repos
Org-billed inference, zero PATs permissions: copilot-requests: write

Not supported

Cloud agent session with plain GITHUB_TOKEN ❌ Server-to-server rejected by design
GitHub App installation token for agent tasks or assignment ❌ Same reason
Org billing as a substitute for user identity ❌ Covers inference, not spawning
Unbounded self-replicating agents max caps, hard ceiling of 10
Silent workflow chain reactions on GITHUB_TOKEN events ❌ Only workflow_dispatch / repository_dispatch
target-repo: "*" on create-agent-session ❌ Explicit repos only

Inference vs. spawning: a distinction worth being precise about

gh-aw does offer a genuine "no PAT required" path, and it is easily over-interpreted.

permissions: copilot-requests: write lets the Actions token pay for Copilot inference, with the AI Credits charged against your organisation's Copilot tenant rather than any individual. No COPILOT_GITHUB_TOKEN, no PAT. It requires the organisation policy Settings → Copilot → Policies → Copilot CLI → "Allow use of Copilot CLI billed to the organization".

This is a significant convenience. It also applies only to inference — the model calls made by the agent running inside your workflow.

copilot-requests: write   →  the agent in your workflow can THINK, AIC billed to the org   ✅
copilot-requests: write   →  the agent in your workflow can SPAWN another agent           ❌

Spawning still requires a user-to-server token, and the resulting session's AI Credits are charged to that token's owner, not to the organisation tenant. I expect this to relax eventually in an enterprise context, where cost can roll up to an enterprise-level identity that is itself an accountable principal — but I am not aware of any preview flag for it today, and the API documentation gives no indication of one.


What to tell your agent

Copilot performs poorly on this topic at present, largely for reasons outside its control. The surface changed recently (gh-aw renamed create-agent-task to create-agent-session in 0.4.0), there is very little public example code, and models fill the gaps with confident invention — such as recommending a support ticket for a feature flag that does not exist.

Two measures help considerably:

  1. Clone github/gh-aw and add it to your workspace. Then instruct the agent: "When in doubt about how agentic workflows function, read the actual implementation instead of making assumptions." (This warrants a separate article — it is the most effective technique I know for fast-moving dependencies.)

  2. Put the constraint in your instructions file. Something like:

<!-- .github/copilot-instructions.md -->
## Agentic workflow constraints

- `GITHUB_TOKEN` CANNOT create Copilot cloud agent sessions. The agent tasks API
  requires a user-to-server token. Do not suggest support tickets or feature flags.
- Prefer implementing code in-run with a sub-agent + `create-pull-request` over
  spawning a new agent session.
- Only use `create-agent-session` / `assign-to-agent` when `GH_AW_AGENT_TOKEN`
  is configured, and say so explicitly.
- Run `gh aw compile` before committing any workflow change.

Conclusion

The instinct to "queue a sub-task" by spawning a new agent is inherited from human organisations, where delegation is how parallelism is achieved. In an agentic workflow it is usually an anti-pattern: you already have an agent running on a runner, with the repository checked out and a shell available. Use it.

Use a cloud agent session when you genuinely need what it uniquely provides — an independent lifecycle, work that outlives your run, and its own review surface. Then treat the PAT as a requirement rather than an obstacle. It is GitHub's answer to the question of who authorised the work, and that question deserves an answer.


References