# Running Tasks

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

## Per-session concurrency

`MaxSessionTasks` (`NumCPU × 4`) 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 opens the cancel-confirmation popup for the active task.

## 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`.

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`.
