# Background Task

> Background Task

- Skill: `minimax-ai/background-task` (Agent Skill)
- Install (CLI): `npx skillmds@latest add minimax-ai/background-task`
- Raw SKILL.md: https://api.skillmd.com/api/skills/minimax-ai/background-task/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: MiniMax AI (https://skillmd.com/u/minimax-ai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/minimax-ai/background-task

---


# Background Task

When a task (a sub-agent invocation or a shell command) is expected to take
more than ~30 seconds, the agent has two choices:

1. **Block**: wait for the task to finish, holding the conversation hostage.
2. **Background**: launch it, get a handle, continue working, and check on it later.

This Skill is about **knowing when to choose (2)** and **how to record the
handle** so the agent (or the user) can check on it later.

## mcode 0.2.4 surface

There are two background-capable tools on mcode 0.2.4, and the right one depends
on whether the background work is a sub-agent or a shell command.

### Sub-agent background: `task(run_in_background: true)` + `task_query` / `task_output` / `task_stop`

The canonical `task` schema:

```text
task(
  description:    string,            // 3-5 word label, required
  prompt:         string,            // the brief, required
  agent_name:  "explore" | "worker" | "verifier",  // required
  run_in_background?: boolean        // optional; true = async, false = sync (default)
)
```

When `run_in_background: true`, mcode returns immediately with a `task_id`.
The companion tools (also canonical in `cli.js`):

| Tool | Purpose | Required fields |
|---|---|---|
| `task_query(task_id?, status?)` | List session tasks (omit `task_id`) or get one. | `task_id` for single fetch; `status` filter optional. |
| `task_output(task_id, offset?)` | Read a task's output incrementally. | `task_id` (required); `offset` (optional, byte offset for long output). |
| `task_stop(task_id, reason?)` | Request a background task to stop. | `task_id` (required); `reason` (optional, human-readable). |

`run_in_background: false` (the default) blocks the calling turn until the
sub-agent finishes and returns its final result.

### Shell background: `bash(run_in_background: true)`

The `bash` tool's canonical schema (from `cli.js:xza`):

```text
bash(
  command:         string,    // required
  timeout?:        number,    // optional, seconds
  run_in_background?: boolean // optional; true = async, false = sync (default)
)
```

When `run_in_background: true`, the `bash` call returns immediately with a
job handle. **Conceptual pseudocode below.** The exact shape of the returned
handle is NOT part of the public mcode 0.2.4 runtime contract; the host's
job-control API is the source of truth for the underlying process id. The
Skills below treat the handle as opaque and locate the process id by means
outside the mcode contract (the launch context, the host's job-control API,
or the calling agent's own bookkeeping). Windows: `Stop-Process -Id <process-id>`;
POSIX: `kill <process-id>`, both invoked through a foreground `bash` call
rather than any `action="kill"` field. The placeholder `<process-id>` is
whatever process id the host's job-control API identifies; the launching
agent must record it (or a way to resolve it) by its own means, separate
from this Skill. **There is no `task_name=` and no `action="kill"`
field.** The Codex-harness shape `bash(task_name=..., run_in_background=true,
action="kill")` is **not** the mcode surface — mcode's `bash` validator
rejects any key outside `command` / `timeout` / `run_in_background`.

Killing a shell background job: invoke the host's job-control API in a
**foreground** `bash` call. Windows: `Stop-Process -Id <process-id>`. POSIX:
`kill <process-id>`. The Skills do not pretend `bash(action="kill")` exists
on mcode 0.2.4. The `<process-id>` value is host-internal (e.g. a Windows
PID resolved via `Get-Process`, or a POSIX pid resolved via `ps -p`); the
launching agent records it out-of-band from this Skill.

## When to use

Activate when **any** of these is true:

- A sub-agent is expected to take > 1 minute (deep research, multi-file
  refactor, anything you cannot predict the duration of).
- A shell command is expected to take > 30 seconds (`cargo test`, `npm install`,
  `docker build`, a long-running dev server, a large data download).
- The user explicitly says "background" / "后台" / "non-blocking" / "in the background".
- You need a long-running process to coexist with ongoing work (a dev server, a
  watch script, a streaming pipeline).
- You would otherwise block the conversation on a result the user can come back
  to later.

## When NOT to use

- The task / command finishes in <5 seconds.
- The user explicitly wants the output now (interactive REPL, vim, ssh, a build
  whose output the next step depends on).
- The command is interactive (it expects a TTY or human input).

## Process

1. **Estimate the duration**. If unsure, assume the worst case. The mcode
   `task` tool description (`run_in_background`) says "Set to true when the
   sub-task is open-ended or expected to take more than ~1 minute (deep
   research, multi-step investigation, large refactors, anything you cannot
   predict the duration of), so you can keep working and the result is
   reported back automatically when it completes. Leave false (the default)
   for short, well-scoped sub-tasks whose result you need right now to
   continue. When in doubt for a long or uncertain task, prefer true."
2. **Choose a descriptive handle**. The agent (and the user) will need to
   recognise it later. `dev-server` is good. `task1` is bad.
3. **Launch in the background using the matching tool**:
   - Sub-agent: `task(..., run_in_background: true)`; mcode returns a
     `task_id`. Store it.
   - Shell: `bash(command: "npm run dev", run_in_background: true)`; mcode
     returns a job handle (the exact shape is not part of the public
     runtime contract; the host's job-control API is the source of
     truth). Store the handle.
4. **Record the handle**. In a multi-step task, store the handle (task_id,
   job id, log path) somewhere persistent — in a `world-state-tracking` file,
   a `session-handoff` note, or in the running brief.
5. **Continue working**. The conversation does not block on the background
   task.
6. **When the result matters**:
   - Sub-agent: `task_query(task_id)` for status, `task_output(task_id)` for
     output, `task_stop(task_id)` to stop.
   - Shell: foreground `bash` call against the host's job-control API
     (`Get-Process -Id <process-id>` / `Stop-Process -Id <process-id>` on
     Windows; `ps -p <process-id>` / `kill <process-id>` on POSIX). Read
     the log file or stdout from the original launch. `<process-id>` is
     the placeholder for the process id the launching agent recorded at
     launch time (out-of-band from this Skill); mcode 0.2.4 does not
     document the `bash` job-handle shape.

## Output contract

After activating this Skill, the agent's next message MUST include:

- The chosen **handle** (`task_id` or job id) and the **tool** that produced it
  (`task` / `bash`).
- The **expected duration estimate**.
- The **log or status path** so a later turn can check on it.
- Whether the agent is **continuing** or **blocking** on the result.

## Common pitfalls

- **Launching and forgetting the handle** — the user comes back in an hour,
  the agent has no idea which process was which. Always record the handle.
- **Re-using a generic name** — `task1` collides; `cargo-test` does not.
- **Polling too eagerly** — a 5-minute build polled every 5 seconds wastes
  context. Poll on a sensible cadence (every minute for builds, every 5 minutes
  for downloads).
- **Killing without saving output** — read the log first, then kill, otherwise
  the result is lost.
- **Using Codex-only `bash(task_name=..., action="kill")` syntax** — mcode
  0.2.4's `bash` does not have those fields. Use `task_stop(task_id=...)` for
  sub-agents and a foreground `bash` call to the host's job-control API for
  shell jobs.
- **Passing `model_config_id` in a background `task()` call** — the `task`
  tool does not accept it (see `model-router`). The model is the session's
  current model.

## Example

The example below is **MiniMax Code 0.2.4 `task` / `bash` tool syntax**. Two
background launches are demonstrated.

### Sub-agent background

```text
# Launch a long-running research sub-agent in the background.
# mcode returns a task_id we can later query / read / stop.

> task(
    description="Research migration paths",
    agent_name="explore",
    run_in_background=true,
    prompt="""
      Investigate migration paths from <lib-A> to <lib-B> in the
      <repo> codebase. Produce a markdown report at
      <repo>/notes/migration.md comparing the top 3 candidates
      with code samples, risk notes, and a recommended path.
      This may take 10+ minutes; you can take your time.
    """
  )
# Returns immediately with:
#   { task_id: "tsk_01HXYZ...", status: "queued" }

# Later, check status:
> task_query(task_id="tsk_01HXYZ...")
#   { task_id: "tsk_01HXYZ...", status: "running", ... }

# Read partial output (the report grows as the sub-agent works):
> task_output(task_id="tsk_01HXYZ...", offset=0)
#   <partial markdown content>
#   next_offset: 12345

# Stop it if the user changed their mind:
> task_stop(task_id="tsk_01HXYZ...", reason="user changed scope")
#   { task_id: "tsk_01HXYZ...", status: "stopping" }
```

### Shell background

```text
# Launch a long-running dev server in the background.
# mcode returns a job id we can later target via the host's job-control API.
#
# CONCEPTUAL PSEUDOCODE for the "check / kill" steps: the mcode 0.2.4
# `bash` tool does not document the shape of the returned job handle
# and does not document a kill action. The launching agent must record
# the underlying process id (Windows PID / POSIX pid) at launch time,
# out-of-band from this Skill. The `<process-id>` placeholder below is
# that recorded value. On a real install, replace it with the actual
# process id; do not assume the handle is a raw integer PID or that it
# parses to one.

> bash(
    command="npm run dev",
    run_in_background=true
  )
# Returns immediately with a job handle. The exact shape is not
# part of the public mcode 0.2.4 runtime contract; the host's
# job-control API is the source of truth. Treat the handle as
# opaque and pass it to the host's job-control API in a
# foreground `bash` call (e.g. `Stop-Process -Id <process-id>` /
# `kill <process-id>` on POSIX) when you need to stop the job.

# Later, check whether it is still alive (foreground bash call):
> bash(
    command="Get-Process -Id <process-id> | Select-Object Id,ProcessName,StartTime"
  )
# (or on POSIX: `ps -p <process-id> -o pid,etime,cmd`)

# Stop it when done (foreground bash call to the host's job-control API):
> bash(
    command="Stop-Process -Id <process-id>"
  )
# (or on POSIX: `kill <process-id>`)
```

The **decision** (background, with a recorded handle) is the same; the
**execution mechanism** depends on whether the background work is a sub-agent
(use `task` + `task_query` / `task_output` / `task_stop`) or a shell command
(use `bash(run_in_background: true)` + the host's job-control API).

## Verification checklist

- [ ] Did you estimate the duration before choosing background vs blocking?
- [ ] Did you choose a **descriptive** handle (not `task1`)?
- [ ] Did you use the right tool — `task` for sub-agents, `bash` for shell
      commands?
- [ ] Did you set `run_in_background: true` (not Codex's `bash(task_name=...)`)?
- [ ] Did you record the handle (task_id / job id / log path) in a persistent place?
- [ ] Did you tell the user "I launched X in the background, here's the handle and
      log path"?
- [ ] If you stopped it, did you use `task_stop(task_id=...)` (sub-agent) or
      `Stop-Process` / `kill` via a foreground `bash` call (shell)?

