# Running Tasks

How tasks in one session run side by side, how to cancel them, and how interrupted work resumes.

## Per-session concurrency

`MAX_SESSION_TASKS` (`3` since v1.1.2; `NumCPU × 4` before) limits how many concurrent `Execute()` calls a single session can run. A task publishes its ToriiDB online marker and registers its cancel function *before* competing for a slot, so a queued task stays visible and cancellable instead of blocking invisibly. The marker carries a 60 s TTL refreshed every 55 s, so a killed or crashed process stops counting as online by expiry rather than by a PID check. The value is a package constant — it is not read from `config.json`.

## Cancelling work

Cancellation is **per task**, not per session: `POST /v1/session/:id/cancel/:task_hash` stops one running task (`{"cancelled": true}`); when that task hash is not running in this process it records a canceled event and returns `{"cancelled": false, "stale": true}`. The former `once_id` identifier was replaced by the task hash. Tool confirmations are answered separately with `POST /v1/session/:id/confirm/:confirm_hash`, where `confirm_hash` comes from the confirm event. There is deliberately no cancel-everything variant, so one long-running job can be dropped without disturbing the session's other tasks. In the TUI, `Esc` during a run (after any open `/` command picker and notice box are closed) opens the cancel-confirmation popup for the active task, or cancels it at once when nothing has been shown yet; since v1.2.0 the TUI sends the cancel to the daemon as a `cancel` frame on its socket.

## Resuming interrupted work

A run that stopped on an `ask_user` or a confirmation leaves a pending task behind. `GET /v1/session/:id/task` lists the ones that are actually resumable: a live run refreshes `action:<session_id>:<task_hash>` in ToriiDB every 55 s with a 60 s TTL, so a task abandoned by a closed window or a killed process reappears in that list within a minute, while a task still being worked on does not. Answer it with `POST .../resume`, or drop it with `DELETE .../:task_hash`. In the TUI this is `/pending`, which since v1.2.0 resumes through the daemon with a `pending` frame and asks any saved question inline.

Completed runs stay queryable too: `GET /v1/session/:id/task/history` lists them as `task_hash`, `end_at`, `objective`, `model`, and `reasoning` (optional `keyword` filter), and `GET .../:task_hash/history` returns one run's full action record as `content`.
