Skip to content

Adopt Pi 1.4.0: durable task recovery, reports, and request identity - #108

Merged
mavam merged 11 commits into
mainfrom
topic/durable-task-recovery
Oct 22, 2026
Merged

mavam merged 11 commits into
mainfrom
topic/durable-task-recovery

Conversation

@mavam

@mavam mavam commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

🔍 Problem

pi-agents doesn't use durable state and APIs that pi-durable provides, and it doesn't build against Pi 1.4.0:

  • Agents never show failed compactions, and the attach view hides the tool calls of codemode scripts.
  • Agents' own tools return only text to scripts.
  • Who sent an input is guessed from [from …]/[user] text.
  • pi-durable 1.4.0 parks tasks after 5 crashes in a row. An agent with parked work would silently "work" forever.
  • onReport now says what failed, but we drop that information.
  • Agents ignore Pi's per-model compaction overrides.
  • pi-durable 1.4.0 breaks the build: checkpointWhen is now required on every document, and TaskRuntime.sleep() is gone.
  • pi-durable 1.4.0 attributes every harness request to Pi, so agents ignored Pi's install-telemetry opt-out.

🛠️ Solution

  • Pi 1.4.0 baseline: pi-durable, chord, and pi-codemode are at 1.4.0 from npm, and the Pi peers at ^1.4.0. There are no version probes or 1.3 paths.
    • Checkpoints: the agents, graphs, and message-log documents store a full base after 64 deltas. These session-scoped documents change in small steps. Without bases, replay grows without bound. A base at every change would rewrite all records each time.
    • No sleep(): a settling graph node rechecks on runtime.setTimer() within its running phase. Stopping agents also aborts their sleeping tasks, such as retry backoffs.
  • Request identity: the harness gets Pi's identity through the public HarnessOptions.identity: a detailed User-Agent with Pi's VERSION, and PI_ATTRIBUTION only while install telemetry is on. PI_TELEMETRY (1/true/yes) overrides getEnableInstallTelemetry(). The identity covers generation, compaction, and codemode models.* calls. Like the other settings, it's read when the harness opens.
  • Input origin: inputs carry the entry kinds pi-agents.user and pi-agents.message (with the sender). The model still reads the markers. The attach view and Esc restoration read the entry, including the pi-agents.results entries of spawned groups. A plain pi.user input is the parent's, so markers are never parsed.
  • Structured results: agent_status, agent_send, and agent_spawn declare output schemas. Refusals stay error results, which reject in scripts. agent_wait stays model-only.
  • Compaction failures: live.compactionFailure shows as a warning until a compaction succeeds. The agent's state doesn't change.
  • Codemode calls: the attach view draws a script's calls from live.nestedTools while the script runs, and from details.calls once it's done.
  • Per-model compaction: compaction.modelOverrides is forwarded, and settings pass through pi-durable's settings function.
  • Stalled agents: parked or blocked work shows as stalled while the agent stays working. /agents retries with r, covering the agent or the graph's agents and everything they spawned. Models see the stall but get no retry tool.
  • Clicks in the attached view: the pane maps clicks through the transcript window it drew (past the separator, clipped start, and newer-lines note) to the transcript container, which routes them to the card there. Click-to-expand lives in one shared expandableCard, used by Pi's transcript result cards and the attach view's message cards; tool cards use Pi's ToolExecutionComponent.
  • Named reports: ReportInfo becomes plain data. Notices name the agent or graph and what failed.

Deliberately unchanged:

  • afterFailedRun: stays keep.
  • Telemetry export: deferred. HarnessOptions.telemetry has no sink to forward to.
  • IndexedDB, SQLite schema v2: irrelevant, since we use JSONL storage.

💬 Review

Rebased onto main (v0.37.0). Recursive spawning (#115) replaced delegate_graph, so the structured-output commit now targets agent_spawn/agent_status/agent_send. Stalls and retries follow agent_spawn ownership recursively.

No backward compatibility, deliberately:

  • Inputs that earlier versions stored show their raw markers as plain parent input in the attach view. Nothing in storage was deleted or migrated.
  • Pi 1.3 is no longer supported.
  • A rollback to an earlier pi-agents version after running on 1.4 may leave a sleeping retry backoff that never resumes, as the changelog entry says.

Validation:

  • bun run check passes on every commit against the npm 1.4.0 packages from a clean bun install (252 tests at the tip). The lockfile is regenerated, with no local paths or tarballs.
  • Smoke test with the local 1.4.0 CLI (node_modules/@earendil-works/pi-coding-agent/dist/bundle/cli.js) in a throwaway HOME, using a scripted faux provider, with no paid calls. The parent ran agent_spawn and agent_wait, and a child's codemode script got agent_status as data. With PI_TELEMETRY=0, the child's requests carried attribution: null. With PI_TELEMETRY=1, they carried PI_ATTRIBUTION, both with version: "1.4.0".
  • Not exercised manually: the interactive TUI (stall row and r retry, the attach view's codemode calls, compaction warnings), a real provider's headers, and compaction requests in a live session. Automated tests cover the logic.

Where to look:

  • src/agents/records.ts: the checkpoint policy.
  • src/agents/graphs.ts: nextChange.
  • src/host/harness-setup.ts: harnessIdentity.
  • src/agents/stalls.ts and its wiring, plus Projection.below(), the retry scope.

github-actions Bot and others added 10 commits October 22, 2026 07:26
Raise pi-durable, chord, and pi-codemode to 1.4.0, and the Pi peers to ^1.4.0, from npm; nothing probes for or works around 1.3.

pi-durable 1.4.0 requires a checkpoint policy on every document definition. The agents, graphs, and message log documents live as long as the session and change in small steps, so each stores a complete base after 64 deltas: replay stays bounded and storage may reclaim older records, without rewriting every agent at each change. Before, they never checkpointed.

TaskRuntime.sleep() is gone. A settling graph node rechecks on a phase timer instead, since the phase keeps running and watching anyway. Stopping agents also aborts their sleeping tasks, such as generation retry backoffs, which now sleep between phases.
Messages from other agents and the user's attach-view input now carry an input entry kind (pi-agents.message with the sender, pi-agents.user) instead of relying on the [from <name>] and [user] markers. The model still reads the markers; the attach view and Esc restoration read the entry, including the results entries of spawned groups. A plain pi.user input is the parent's: inputs stored by earlier versions aren't parsed for markers.
agent_status, agent_send, and agent_spawn declare structured output schemas, so an agent's codemode scripts get data instead of text: the reachable agents with their state, model, and job; a send's recipient, mode, and what its interrupt did; and a spawned group with its agents' IDs and full names. The model-facing text and the receipts stay as they were; refusals stay error results, which reject in scripts. agent_wait stays model-only.
pi-durable keeps the newest compaction failure in pi.live until a compaction produces a summary. Agent infos carry it, and the panel line, /agents, the attach view, and the parent's agent_status show it, without changing the agent's state.
A running codemode call draws the nested calls it owns from pi.live.nestedTools; a finished one draws the calls its result's details list. The script output drops its header and, when the call lines show, the failure text's list of calls, so nothing shows twice.
Pi resolves compaction.modelOverrides only for a given model, so agents got the harness-wide values alone. The harness settings now carry the overrides as pi-durable's compaction.modelOverrides, which applies them for the model each compaction acts for. The harness also reads its settings through pi-durable's settings function instead of copying them once at open, so the getters run at each use as documented.
pi-durable parks a task that crashes cut off restartLimit times in a row, and its task graph tells why a live task can't run. The service follows the task graph: an agent or graph whose tasks are parked or blocked shows as stalled while it stays working, and service.retry() runs parked tasks again, of the agent or the graph's agents and of everything they spawned. /agents retries with r, the attach view and agent_status say why the agent doesn't progress, and the models get no retry tool, since a crash loop needs the user's judgment.
pi-durable's onReport now passes ReportInfo: the task, its conversation, the extension, or the host callback a report is about. The host turns it into plain data, and the service words it with the agent or graph it belongs to. Reports during open wait until the service knows the agents, and a failed session reports only through the storage recovery.
pi-durable 1.4.0 attributes every harness request to Pi unless told otherwise, and agents borrow the parent's model runtime, which lacks the identity Pi adds per request. The harness now gets Pi's identity: a detailed User-Agent with Pi's version, and Pi's attribution only while install telemetry is on, with PI_TELEMETRY overriding the setting as in Pi. It covers generation, compaction, and codemode scripts' model calls, and is read when the harness opens, like the other settings.
@mavam
mavam force-pushed the topic/durable-task-recovery branch from 964aef4 to 350a98e Compare October 22, 2026 05:39
@mavam mavam changed the title Adopt pi-durable task recovery, reports, and released 1.3.0 APIs Adopt Pi 1.4.0: durable task recovery, reports, and request identity Oct 22, 2026
@mavam
mavam marked this pull request as ready for review October 22, 2026 05:43
The attach pane handled only wheel events, so clicks never reached the transcript and cards expanded only with Ctrl+O. The pane now maps a click's row through the window it last drew, past the separator and the clipped start, to the transcript container, which hands it to the card there as Pi's containers do. Tool cards toggle through Pi's ToolExecutionComponent; clicks on the status, editor, or separator pass on, and a click on the newer-lines note follows new output.

Click-to-expand for cards now lives in one place, expandableCard, which both Pi's transcript result cards and the attach view's message cards use: a click toggles the card, and a change of the global expansion (Ctrl+O) sets it again.
@mavam
mavam merged commit 6407306 into main Oct 22, 2026
3 checks passed
@mavam
mavam deleted the topic/durable-task-recovery branch October 22, 2026 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant