# How PI-Desktop Manages Agent Workflows: From Interactive Chat to Sub-Agent Execution

> Discover how PI-Desktop manages agent workflows using its agent plan and goal lifecycle. Transition from chat to autonomous execution with sub-agent delegation.

- Repository: [Lan/PI-Desktop](https://github.com/vastsa/PI-Desktop)
- Tags: internals
- Published: 2026-09-11

---

**PI-Desktop implements agent workflows through a three-mode lifecycle—agent, plan, and goal—that transitions users from interactive chat to structured plan approval, culminating in autonomous goal execution with optional sub-agent delegation.**

The open-source PI-Desktop repository orchestrates complex AI delegation through a strict lifecycle defined in [`packages/shared/src/types.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/types.ts). This architecture separates interactive conversations from background task execution by enforcing a Markdown-based planning phase before any autonomous goal runs. Understanding these agent workflows is essential for developers extending the desktop client or integrating custom sub-agents.

## The Three Modes of Agent Workflows

PI-Desktop drives its AI assistant through three distinct **modes** defined in the shared types package. Each mode represents a specific phase in the delegation lifecycle, governing how the model interacts with user input and system resources.

### Agent Mode: The Default Interactive State

In **agent** mode, the system operates as a standard interactive chat interface. The `Mode` type is set to `"agent"`, and the model answers user prompts directly without spawning background processes. This is the default state when no plan is pending or executing.

### Plan Mode: Structured Approval Workflow

**Plan** mode introduces a validation gate before execution. When a user creates a plan—a Markdown document stored under `~/.agents/plans/…`—the system sets `ProposalKind = "plan"` and transitions the `PlanningState` to `"planning"` or `"awaiting_approval"`. The UI renders a plan editor, and upon submission, the proposal enters a pending approval queue. This phase ensures that potentially destructive or complex operations require explicit user consent before the runtime allocates resources.

### Goal Mode: Autonomous Execution with Sub-Agents

Once approved, the proposal transitions to **goal** mode with `ProposalKind = "goal"`. In this state, the main agent enters the sub-agent orchestration layer and may spawn **sub-agents** (also called *subagents*) to carry out parts of the work. These sub-agents run in their own execution turns and report lifecycle events (`agent_start`, `agent_end`, `turn_end`) tagged with `agentName` via the `AgentActivityAgent` interface.

## The Plan-to-Goal Lifecycle Implementation

The transition from plan to goal involves several technical layers across the PI-Desktop codebase, from IPC communication to state normalization.

### Creating and Submitting Plans

Users author plans as Markdown files in the `~/.agents/plans/` directory. When submitted, the desktop client sends the plan through the **RACP** (Remote Agent Control Protocol) using the `plansPending` and `plansResolve` IPC channels declared in [`packages/shared/src/protocol.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/protocol.ts). The host returns a raw execution response that must be normalized before use.

The `normalizeProposalKind` utility in [`packages/shared/src/types.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/types.ts) ensures that the proposal `kind` is strictly typed as either `"plan"` or `"goal"`:

```typescript
import { normalizeProposalKind, ProposalKind } from "@pi-desktop/shared";

function getKind(raw: unknown): ProposalKind {
  // Accepts "plan", "goal", or any value and falls back to "plan"
  return normalizeProposalKind(raw);
}

```

### Decoding Execution Records

The main process decodes host responses using the `executionFromResponse` function in [`apps/desktop/electron/main/plan-execution.ts`](https://github.com/vastsa/PI-Desktop/blob/main/apps/desktop/electron/main/plan-execution.ts). This normalizes the host-provided `PlanExecution` record, which contains:

- `id`, `proposalId`, `sessionId`
- `kind` (`"plan"` or `"goal"`) normalized via `normalizeProposalKind`
- `targetPermissionMode` normalized via `normalizeGlobalPermissionMode`
- `state` values: `"queued"`, `"running"`, `"completed"`, `"interrupted"`

```typescript
import { executionFromResponse } from "./plan-execution";

const rawResponse = await ipcInvoke("plansResolve", { id: "123" });
const exec = executionFromResponse(rawResponse);

if (exec) {
  console.log(`Goal ${exec.id} is ${exec.state}`);
}

```

## Sub-Agent Orchestration in Goal Mode

When a goal executes, the runtime ([`packages/agent-runtime/src/runtime.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-runtime/src/runtime.ts)) manages **sub-agents** defined by Markdown files in `~/.agents/subagents/…` (see `SubagentDefinition` in [`packages/shared/src/subagent-definition.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/subagent-definition.ts)).

### Spawning Sub-Agents via the Task Tool

Parent agents spawn sub-agents through the `Task` tool, which generates events trackable across the system:

```typescript
import { Task } from "@pi-desktop/agent-runtime";

await Task.call({
  tool: "subagent",
  args: {
    name: "explorer",
    input: "Search repository for TODO comments",
  },
});

```

Internally, the `Task` tool produces an event with `type: "turn_end"` and `agentName: "explorer"` (see `AgentActivityAgent` in [`packages/shared/src/types.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/types.ts)).

### Tracking and Permissions

The runtime tracks each sub-agent’s **thinking level** (`SUBAGENT_THINKING_LEVELS`) and **permission** (`SUBAGENT_PERMISSIONS`) to enforce sandbox boundaries and resource limits during goal execution.

## State Management and Error Recovery

While a goal runs, the central session’s `planningState` is set to `"inactive"` (indicating the plan phase has concluded). The UI subscribes to the `plansChanged` IPC event (declared in [`packages/shared/src/protocol.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/protocol.ts)) to render progress indicators. Upon completion, the state transitions to `"completed"` or `"interrupted"`, and results are persisted to disk.

For resilience, the system implements **provider-retry** logic in [`packages/agent-runtime/src/provider-retry.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-runtime/src/provider-retry.ts) alongside RACP timeout schemas ([`racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/racp.ts)). These mechanisms recover from transient host failures, allowing goals to resume if the connection drops during sub-agent execution.

## Summary

- **Three distinct modes** drive PI-Desktop: interactive `agent` chat, approval-gated `plan` creation, and autonomous `goal` execution.
- **Markdown-based plans** stored in `~/.agents/plans/` require RACP validation through `plansPending`/`plansResolve` IPC channels before transitioning to goals.
- **Sub-agent orchestration** occurs within goal mode, using the `Task` tool to spawn agents defined in `~/.agents/subagents/` with tracked permissions and thinking levels.
- **Resilient execution** is ensured via `normalizeProposalKind` type safety, `executionFromResponse` decoding, and provider-retry logic for fault recovery.

## Frequently Asked Questions

### What is the difference between plan mode and goal mode in PI-Desktop?

**Plan mode** requires explicit approval before execution and operates with `ProposalKind = "plan"` and planning states of `"planning"` or `"awaiting_approval"`. **Goal mode** represents active execution with `ProposalKind = "goal"`, where the system spawns sub-agents and tracks execution states like `"running"` or `"completed"` via the `PlanExecution` type.

### How does PI-Desktop validate plans before execution?

Validation occurs through the **RACP** (Remote Agent Control Protocol) using the `plansPending` and `plansResolve` IPC channels defined in [`packages/shared/src/protocol.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/protocol.ts). The host returns an execution record that is normalized through `executionFromResponse` in [`apps/desktop/electron/main/plan-execution.ts`](https://github.com/vastsa/PI-Desktop/blob/main/apps/desktop/electron/main/plan-execution.ts), ensuring type-safe state transitions.

### Where are sub-agent definitions stored?

Sub-agent definitions are Markdown files located in the `~/.agents/subagents/` directory. The `SubagentDefinition` interface in [`packages/shared/src/subagent-definition.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/subagent-definition.ts) describes their schema, which the runtime uses when spawning agents via the `Task` tool during goal execution.

### How does the system recover from failures during goal execution?

The runtime implements retry logic in [`packages/agent-runtime/src/provider-retry.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-runtime/src/provider-retry.ts) and conforms to RACP timeout schemas in [`racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/racp.ts). These components handle transient provider failures, ensuring that interrupted goals can resume without data loss if the host crashes or the connection drops.