# Long-running tasks

> Goals, compaction, heartbeats, autonomous mode, and retained subagents that keep multi-turn work progressing across disconnects.

- Repository: PrimeIntellect-ai/prime-agent
- GitHub: https://github.com/PrimeIntellect-ai/prime-agent
- Human docs: https://grok-wiki.com/public/docs/primeintellect-ai-prime-agent-3367c32760b1
- Complete Markdown: https://grok-wiki.com/public/docs/primeintellect-ai-prime-agent-3367c32760b1/llms-full.txt

## Source Files

- `packages/coding-agent/skills/goal/SKILL.md`
- `packages/coding-agent/skills/compact/SKILL.md`
- `packages/coding-agent/skills/rlm-heartbeat/SKILL.md`
- `packages/coding-agent/test/suite/agent-session-goal.test.ts`
- `packages/coding-agent/test/suite/agent-session-compaction.test.ts`
- `packages/coding-agent/test/suite/regressions/4482-heartbeat-injected-prompt.test.ts`

---

---
title: "Long-running tasks"
description: "Goals, compaction, heartbeats, autonomous mode, and retained subagents that keep multi-turn work progressing across disconnects."
---

Prime Agent keeps multi-turn work alive through the resident session worker: durable goal state, context compaction, user and agent heartbeats, general schedules, bounded autonomous continuations, and retained RLM child sessions. Detaching the TUI or CLI client does not stop the worker; the session queue, IPython kernel, schedules, and descendants stay owned by the daemon-backed process.

## Runtime model

Long-running surfaces share one worker-owned session tree rather than a client-owned chat process.

```mermaid
flowchart TD
  client["TUI / CLI / attach client"]
  peer["Peer agent or retained subagent"]
  supervisor["Daemon supervisor<br/>routing + attachments"]

  subgraph worker["Resident session worker"]
    heartbeats["User + RLM heartbeats"]
    schedules["schedule add / cron / once"]
    goals["Persistent goal"]
    autonomous["Autonomous mode"]
    policy["Continuation policy"]
    queue["Session prompt queue"]
    session["AgentSession"]
    kernel["Persistent IPython kernel"]
    children["RLM child sessions"]

    heartbeats --> queue
    schedules --> queue
    goals --> policy
    autonomous --> policy
    policy --> queue
    queue --> session
    session --> kernel
    session <--> children
  end

  artifacts["JSONL transcript + session artifacts"]

  client <-->|"attach · detach · commands"| supervisor
  peer -->|"agent_message / send"| supervisor
  supervisor --> queue
  session --> artifacts
  artifacts -. "restore after restart" .-> session
```

| Surface | Owner | What it does |
|---|---|---|
| Persistent goal | User + host; model completes via `goal` skill | Re-prompts the objective until `await goal.complete()` or user/host pause/clear/budget limit |
| Autonomous mode | Host policy | Injects follow-up turns until gates pass or limits are hit |
| `/heartbeat` | User | One visible recurring instruction per session |
| `rlm_heartbeat` | Agent (IPython) | Multiple internal recurring instructions for the same session |
| `prime-agent schedule` | User or automation | One-time or cron prompts targeted at an addressable agent |
| Compaction | Host auto + `/compact` + `compact` skill | Summarizes older transcript while keeping the kernel and long-running policies |
| Retained RLM children | Parent session registry | Keep completed daemon-backed children addressable for follow-ups |

<Note>
Daemon workers are process-isolated for lifecycle and recovery, not security sandboxes. They normally run with the same OS permissions as the client.
</Note>

## Prerequisites for background progress

Long-running work survives disconnect when the session runs as a daemon-backed worker:

```bash
prime-agent list
prime-agent attach <agent>
prime-agent agents
prime-agent status
prime-agent doctor [--fix]
```

Closing the UI detaches the client. Use `prime-agent stop <agent>` to stop a worker, or `prime-agent shutdown [--force]` to stop all agents and services.

Workers persist transcripts as JSONL and store feature-specific state under the session artifact directory. Supervisor or worker restart can recover session state, schedules, and retained completed RLM children without treating the terminal as the owner of the work.

## Persistent goals

A goal is a durable objective the harness keeps presenting across turns until it is complete, paused, budget-limited, errored, or cleared. Goal status, token budget, usage, and timestamps live in the TypeScript host; the IPython `goal` skill is the kernel-side interface.

### Goal statuses

| Status | Meaning |
|---|---|
| `idle` | No goal |
| `active` | Host continues the objective after ordinary assistant turns |
| `paused` | User paused; host does not continue |
| `budget_limited` | Token budget reached; host injects a wrap-up prompt and does not start new substantive goal work |
| `complete` | Model marked success with `await goal.complete()` |
| `error` | Host recorded an error state |

Only completion is exposed to the model through the skill. Pause, resume, clear, and budget-limit transitions are user/host controlled.

### User controls

```text
/goal Ship the release and verify every published artifact
/goal --budget 200000 Complete the repository migration
/goal status
/goal pause
/goal resume
/goal clear
```

Usage:

- `/goal [--budget <tokens>] <objective>` or `/goal --token-budget <tokens> <objective>`
- Objective max length: `4000` characters
- Token budget must be a positive integer when set

Starting a slash goal activates `ipython` if it is not already in the active tool set so the model can call `goal.complete()`.

### Kernel API

```python
await goal.get()
await goal.create("ship the release notes", token_budget=200000)
await goal.complete()
```

Host responses use snake_case fields:

| Field | Type | Notes |
|---|---|---|
| `goal` | object or `null` | `objective`, `status`, `token_budget`, `tokens_used`, `time_used_seconds`, timestamps |
| `remaining_tokens` | int or `null` | `null` when no budget |
| `completion_budget_report` | string or `null` | Present after successful completion when usage is reportable |

Constraints:

- Do not create a goal unless the user or system/developer instructions explicitly request a persistent long-running goal.
- `goal.create` fails while a goal is active, paused, or budget-limited. Completed or errored goals can be replaced.
- The harness keeps continuing an active goal until `await goal.complete()` arrives. Saying the work is done is not enough.
- Do not call `goal.complete()` only because the budget is nearly exhausted.
- Post-completion assistant turns are not billed against the finished goal's token usage.

Active goals inject `customType: "goal_context"` messages (`<goal_context>...</goal_context>`) for continuation, budget-limit wrap-up, and user objective updates. Events include `goal_update` with the current `GoalState`.

## Heartbeats and schedules

Prime Agent has three related scheduling surfaces.

| Surface | Source id | Cardinality | Default interval | Default delivery |
|---|---|---|---|---|
| `/heartbeat` | `heartbeat` | One user-visible job per session (new create cancels previous active/paused) | `every 5m` | `steer` |
| `rlm_heartbeat` | `rlm_heartbeat` | Many concurrent internal jobs | `every 5m` | `steer` |
| `prime-agent schedule` | general schedule jobs | Many one-time or cron jobs per agent | n/a | n/a |

Minimum recurring interval is 10 seconds.

### User heartbeat

```text
/heartbeat every 10m Check the deployment and report meaningful changes
/heartbeat status
/heartbeat pause
/heartbeat resume
/heartbeat clear
```

Add `--follow-up` when the prompt should wait until the current turn finishes. Default delivery steers (interrupts) active work. Use `/heartbeats` to inspect both user and agent-created heartbeats.

### Agent RLM heartbeats

Use when the agent should own recurring checks without replacing the user's `/heartbeat`:

```python
await rlm_heartbeat.create("check test progress", interval="5m", label="tests")
await rlm_heartbeat.create("watch build", delivery_mode="follow_up")
await rlm_heartbeat.list()
await rlm_heartbeat.update("job-id", status="pause")
await rlm_heartbeat.delete("job-id")
```

| API | Behavior |
|---|---|
| `list(include_inactive=False)` | Active and paused by default |
| `create(instruction, interval=None, label=None, delivery_mode=None)` | Recurring only; multiple allowed |
| `update(id, ...)` | Patch instruction, interval, label, status (`pause`/`resume`), delivery mode |
| `delete(id)` | Cancel one job |

Delivery modes:

- `steer` (default): interrupt the current turn so the heartbeat runs promptly
- `follow_up`: wait until the current turn finishes

The `rlm_heartbeat` skill cannot read, replace, pause, resume, or clear the user-level `/heartbeat`.

Heartbeat prompts are stored as transcript custom messages (`customType: "heartbeat_prompt"`, displayable) while provider input still receives them as user content. Delivery still runs `before_agent_start` extension handlers.

### General schedules

```bash
prime-agent schedule add worker "in 30m" -- "Check the benchmark result"
prime-agent schedule add worker "0 9 * * 1-5" -- "Review open work"
prime-agent schedule list --all
prime-agent schedule cancel <job-id>
```

Jobs are persisted per session and continue while the UI is detached. Due ticks are claimed and advanced before prompt delivery so a crash does not replay an uncertain prompt. Missed ticks coalesce rather than building an unbounded backlog. Different target sessions dispatch independently.

## Autonomous mode

Autonomous mode is a bounded host policy for runs where no human input is expected. After assistant turns, the host may inject a continuation user message until quality gates pass or a limit is reached.

### Enable

Interactive:

```text
/autonomous on
/autonomous status
/autonomous off
```

CLI:

```bash
prime-agent \
  --autonomous \
  --autonomous-gate "npm run check" \
  --autonomous-max-turns 20 \
  "Implement and verify the requested change"
```

| Flag / config | Default | Role |
|---|---|---|
| `--autonomous` | off | Enable policy |
| `--autonomous-gate <command>` | none (repeatable) | Shell gate run before finish is allowed |
| `--autonomous-gate-retries <n>` | `3` | Retries per failed gate |
| `--autonomous-gate-timeout-ms <n>` | `300000` (5m) | Per-gate timeout |
| `--autonomous-max-continuations <n>` | `3` | Host-injected follow-up limit |
| `--autonomous-max-turns <n>` | `12` | Assistant-turn limit |
| `--autonomous-max-tokens <n>` | `80000` | Host token budget (input + output + cache write; cache reads excluded) |
| `--autonomous-timeout-ms <n>` | `1800000` (30m) | Wall-clock limit |

`/autonomous status` reports enabled state plus `continuations/turns/tokens` usage against limits.

### Decision policy

When enabled and the last assistant message is not `error`/`aborted`:

1. If gates are configured, run them in the session cwd.
2. Gate **passed** → stop (no continuation).
3. Gate **failed** within retries → continue with a gate-failure prompt that includes bounded command output (max 6000 chars of gate output).
4. Gate **retry exhausted** or any limit hit → stop.
5. No gates configured → continue with reason `missing_terminal_evidence` until a limit stops the run.

Unchanged-workspace optimization: if a gate failed and the git worktree snapshot (status, diff, untracked hash) is unchanged, the host does not rerun the command. It records `not rerun: workspace unchanged since previous failed gate` and still consumes a retry attempt. Edit source, tests, or a blocker artifact before finishing again.

Default continuation prompt tells the model no human input is available, to keep working within budget, and not to end the session itself; configured gates and host limits decide completion.

### Goals vs autonomous mode

| Concern | Goal | Autonomous mode |
|---|---|---|
| Stores objective | Yes (`objective`, usage, status) | No |
| Completion signal | `await goal.complete()` | Gates pass and/or host limits |
| Continuation content | Goal-context custom message with objective | Fixed or gate-failure continuation prompt |
| Typical use | Explicit multi-turn objective the user wants pursued | Unattended verifier/evaluator loops |

They can run together: the goal holds the objective; autonomous mode decides whether another host-driven turn is injected when the assistant would otherwise stop.

## Compaction for continuity

Compaction frees model context so long-running work can continue. It is not a completion signal and does not stop goals, autonomous continuations, heartbeats, schedules, or existing child sessions.

### Triggers

| Trigger | Mechanism |
|---|---|
| Auto-compaction | `contextTokens > contextWindow - reserveTokens` |
| User | `/compact [instructions]` |
| Agent | `await compact.run(instructions=None)` |

Defaults (`settings.compaction`):

| Key | Default |
|---|---|
| `enabled` | `true` |
| `reserveTokens` | `16384` |
| `keepRecentTokens` | `20000` |

Settings live in `~/.prime/agent/settings.json` or `<project-dir>/.prime/agent/settings.json`.

### Kernel API

```python
await compact.status()
await compact.run()
await compact.run("keep the failing test names and the migration checklist")
```

| Call | Result |
|---|---|
| `status()` | `tokens`, `context_window`, `percent` (`None` right after compaction until the next model response), `scheduled` |
| `run(instructions?)` | `{"scheduled": True}` or `{"scheduled": False, "reason": ...}` |

Rules:

- Compaction never runs mid-cell. A scheduled compaction runs when the current turn ends; the harness then resumes with the summary plus recent messages.
- One `run` per turn is enough; later calls before the turn ends only update instructions.
- The IPython kernel persists through compaction: variables, imports, and helpers remain available.
- Default details track cumulative `readFiles` / `modifiedFiles` across repeated compactions.

Transcript shape: a `compaction` session entry with `summary`, `firstKeptEntryId`, `tokensBefore`, optional `customInstructions`, and optional extension `details`. The model sees system prompt + summary + messages from `firstKeptEntryId` onward.

## Retained subagents

RLM children admitted with `handle = await rlm("task", name="worker")` return at admission, not completion. Results arrive only through `agent_message` replies or files.

Daemon-backed children that finish successfully remain in the parent-scoped registry while the parent session is open:

```python
children = await rlm.list_subagents()
for child in children:
    print(child.session_name, child.status, child.active_session_id)

await agent_message.send(
    "Continue with the updated diff",
    receiver_role="child",
    receiver_name=children[0].session_name,
)

await rlm.delete_subagent(children[0])  # when context is no longer needed
```

Registry properties that matter for long runs:

- Survives compaction, kernel restart, and parent restore
- Successfully completed daemon-backed children rehydrate from parent artifacts
- Inline children stay inspectable in-process but have no active-session id
- Deletion cancels/closes the runtime and writes a durable tombstone; it does not erase transcript or artifacts on disk
- Parent can still project retained-child follow-up activity into parent child-update events

Cross-session messaging also works from the shell:

```bash
prime-agent send <agent> "Please verify the latest migration"
```

Delivery modes for `agent_message` / send: `auto` (steer if busy, deliver if idle), `steer`, `follow_up`. Receipts are `delivered` or `queued`.

## Typical long-running workflow

<Steps>
  <Step title="Start durable work in a project session">
    Launch `prime-agent` in the project directory so a daemon-backed worker owns the session. Optionally set a goal and/or enable autonomous mode before or during the task.
  </Step>
  <Step title="Set continuation policy">
    Use `/goal <objective>` when the user wants a durable objective with explicit completion via `goal.complete()`. Use `/autonomous on` or `--autonomous` with gates when the run should proceed without human turns until verifiers pass or limits hit.
  </Step>
  <Step title="Add recurring checks">
    Configure `/heartbeat every <interval> <instruction>` for user-visible rechecks, or have the agent create labeled `rlm_heartbeat` jobs for internal coordination. Use `prime-agent schedule add` for one-shot or cron prompts against a named agent.
  </Step>
  <Step title="Delegate and retain children">
    Spawn RLM children for independent work. Keep handles or recover them with `rlm.list_subagents()`, then follow up with `agent_message` instead of redoing context from scratch.
  </Step>
  <Step title="Manage context growth">
    Let auto-compaction run, or call `/compact` / `await compact.run(...)` at natural boundaries when context is high and work remains. Expect the kernel state to survive; re-read goal and child registry if needed.
  </Step>
  <Step title="Detach safely">
    Close or detach the client. Reattach with `prime-agent attach <agent>` or inspect with `prime-agent list` / `prime-agent agents`. Stop only when the worker should end.
  </Step>
</Steps>

## Configuration reference

### Compaction settings

```json
{
  "compaction": {
    "enabled": true,
    "reserveTokens": 16384,
    "keepRecentTokens": 20000
  }
}
```

Disable auto-compaction with `"enabled": false`. Manual `/compact` and the `compact` skill still work.

### Autonomous runtime limits

Defaults from `DEFAULT_AUTONOMOUS_LIMITS` / `DEFAULT_AUTONOMOUS_GATES`:

| Field | Default |
|---|---|
| `maxContinuations` | `3` |
| `maxTurns` | `12` |
| `maxTokens` | `80000` |
| `timeoutMs` | `1800000` |
| `gates.commands` | `[]` |
| `gates.maxRetries` | `3` |
| `gates.timeoutMs` | `300000` |

### Heartbeat defaults

| Constant | Value |
|---|---|
| `DEFAULT_HEARTBEAT_SCHEDULE` | `every 5m` |
| `DEFAULT_HEARTBEAT_DELIVERY_MODE` | `steer` |
| Minimum interval | 10 seconds |

## Failure modes and verification

| Symptom | Likely cause | Check |
|---|---|---|
| Goal keeps continuing after "done" | Model never called `await goal.complete()` | Inspect `goal_update` / `goalState.status`; require completion cell |
| Cannot create a second goal | Prior goal still `active`, `paused`, or `budget_limited` | `/goal status`; complete, resume, or clear |
| Autonomous run stops early | Limit hit or gate retry exhausted | `/autonomous status`; gate output in continuation prompts |
| Gate not rerun | Workspace snapshot unchanged after failure | Edit tracked files/tests/blocker artifacts, then retry |
| Heartbeat not firing after disconnect | Worker stopped or schedule cancelled | `prime-agent list`, `prime-agent status`, `/heartbeats` |
| Duplicate schedule delivery expected after crash | Tick was already claimed | Design assumes no uncertain replay; next future tick only |
| Child missing after compaction | Deleted or never daemon-backed | `await rlm.list_subagents()`; only completed daemon children rehydrate as addressable workers |
| Context full / early stop | Compaction disabled or not scheduled | `await compact.status()`; enable auto-compaction or `await compact.run(...)` |

Verification signals for healthy long-running sessions:

- Active goal: `goalState.status === "active"` and recurring `goal_context` messages until completion
- Heartbeat: custom `heartbeat_prompt` entries appear on schedule; user text history does not treat them as ordinary user prompts
- Compaction: `compaction` entries in the session file and `compactionSummary` at the head of model context
- Autonomous: status custom message shows rising continuation/turn/token counters while still enabled
- Retained children: `list_subagents()` returns stable names/ids after parent restore

## Related pages

<CardGroup cols={2}>
  <Card title="Run daemon-backed sessions" href="/daemon-sessions">
    Attach, detach, resume selectors, and worker recovery for background sessions.
  </Card>
  <Card title="RLM control plane" href="/rlm-control-plane">
    Persistent IPython control tool, prompt-as-variable context, and `rlm(...)` admission.
  </Card>
  <Card title="Subagents and messaging" href="/subagents-messaging">
    Child agents, `agent_message`, delivery modes, and multi-agent constraints.
  </Card>
  <Card title="Continual Harness" href="/continual-harness">
    Durable prompts, memories, skill and subagent specs that complement long-running policies.
  </Card>
  <Card title="Sessions and runtime" href="/sessions-runtime">
    Session lifecycle, queueing, events, and session-scoped vs durable state.
  </Card>
  <Card title="Built-in skills reference" href="/builtin-skills">
    Catalog entries for `goal`, `compact`, `rlm-heartbeat`, and related skills.
  </Card>
  <Card title="Session configuration reference" href="/session-configuration">
    Config keys and defaults used by compaction, goals, and autonomous services.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Worker recovery, invalid resume selectors, and connection-mode probes.
  </Card>
</CardGroup>
