Skip to content

fix(pi): end left runs before continuing Pi, add session controls, trim the README - #1498

Merged
salman1993 merged 32 commits into
mainfrom
sm/pi-cancel-recovery
Oct 22, 2026
Merged

salman1993 merged 32 commits into
mainfrom
sm/pi-cancel-recovery

Conversation

@salman1993

@salman1993 salman1993 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review fixes on top of #1420.

What changed

  • A failed cancellation no longer lets abandoned work resume (f5b817d68). Pi continues every run as soon as it is asked anything. Before, a failed unfinished or drop was logged and the turn went on, so a wait that reopened the agent continued the watch or timer run it had failed to end.
    • Taking on left runs now fails the turn that needed it. It forgets the mentions it took, and the next turn tries again.
    • Opening an agent again, after its host stopped or its storage failed, lists and ends left runs before it waits or hands a turn over again. A run abandoned since the app started is ended too, not continued.
    • A turn that gives up on its run makes the next turn list left runs again (abc3e0ef6). It gives up when the host stops twice while it waits, storage keeps failing, the retried hand-over fails, or a standalone turn fails after it submitted. Before, the cached taking-on was reused, so the next turn continued that run with nothing waiting for it.
  • Session controls for Pi, so a recovered run can be interrupted (a53cbd3dd). Stop on a mention's activity needs the runner's job, which a run left by an earlier app doesn't have. Pi now has the composer's three session controls, as Claude, Codex and goose do (feat(agents): add buttons to compact and start new session #1434). They act on the thread's conversation rather than one request, so they reach runs taken on after a restart.
    • Interrupt aborts the conversation's run, and any compaction in it.
    • Compact runs Pi durable's compact() and settles once the summary is placed.
    • New starts a new context with reset(). The next turn is shown the whole Buzz conversation again, and the old turns stay in the transcript and in Usage.
    • A mention during an action waits for it, past its handover, through the shared SessionControls. Each action takes on left runs first.
    • An action works before the agent's first mention since Buzz started (c51d93b2c). Taking on left runs needs the agent's handle to post their answers; with no delivery's handle yet, the action takes it from Agents.handle.
    • Compact and New check again just before reaching Pi (11746cda2). A left run taken on in the same conversation refuses them, and an Interrupt during their preparation cancels them.
    • Interrupt also cancels a mention still being handed over, as Claude does (abc3e0ef6). It is not delivered and posts nothing; later mentions are unaffected.
  • Pi parses its stored prompts itself (16becb485). It has a small wrapper around the shared promptSections and promptMessage, as goose does. Claude's prompt is private again, so claude-code/transcript.ts matches main.
  • A Stop during a tool call shows as stopped (227803839). Stopped in the tools phase, Pi durable writes an aborted result for each cut-off call (diagnostic code aborted) and no aborted answer, so the transcript showed a failed tool instead.
    • The host flags those results, and the transcript marks the turn stopped and the tool Terminated.
    • An answer after them clears the stop, since aborting a single tool call lets the run go on.
    • A Stop before the first token still leaves only the prompt, so that turn isn't marked stopped.
  • README trimmed (21a3d36ff) from 167 to 50 lines: the required Getting started guide, the checks, how to update the packages, and the limits.
    • The limits keep the compaction-usage line (a6ca0dfc4): Pi durable records compaction's usage only in each conversation's totals, so Usage dates it all at the newest compaction. Manual Compact makes that more frequent.
    • They also keep the duplicate-send limit, with the mechanism corrected (6d57be244). Pi doesn't rerun a cut-off Buzz tool call; after a quit or host stop it tells the model the call may have run, and a send made again may post twice. A Stop or Interrupt ends the run instead.

Testing

  • bin/pnpm exec vitest run src/bundled/pi src/bundled/readmes.test.ts src/features/agents/service.test.ts: all pass. tsc --noEmit and biome are clean.
  • Cancellation tests, all failing on 7bb187829 and passing here:
    • a failed drop hands nothing over and waits for nothing, and the next turn ends the run first
    • an agent opened again ends a run left for no one before it waits
    • a failed drop after reopening waits for nothing
    • a retried hand-over lists and ends what the stopped host left before it delivers again (fails when the retry's taking-on is replaced with a bare open)
    • after a mention or a standalone turn gives up because the host stopped twice, the next mention lists unfinished before deliver (fails without abc3e0ef6)
  • Session-control tests:
    • Interrupt from the thread reaches a run left by an earlier app
    • Compact and New only run while idle, and a mention during Compact is handed over once it ends
    • Compact and New run before any mention, taking on a left run first
    • New is refused when a run taken on first is in the same conversation, and an Interrupt while Compact prepares means Compact is never sent (both fail without 11746cda2)
    • Interrupt during a mention's handover delivers nothing and posts nothing, and a later mention is delivered (fails without abc3e0ef6)
    • Each test fails when its key mapping or deferred handover is reverted.
  • The live host test on real Pi 1.3.0 (PI_DEPS=… vitest run src/bundled/pi/host.live.test.ts) now also checks:
    • Interrupt by key aborts the run
    • Compact places a pi.compaction summary
    • New writes pi.reset and resets what the conversation was shown
    • Interrupt during a real bash call leaves a tool result flagged aborted
  • Transcript test: an aborted tool result marks the turn stopped and the tool Terminated, an answer after it clears the stop, and an ordinary failed tool stays Failed.
  • The Stop-display tests fail without 227803839, both the unit test and the live check.

Known gaps

  • Not yet tried in the app. Agent-tested and human-tested are both still to do.

🤖 Generated with Claude Code

salman1993 and others added 23 commits October 20, 2026 11:47
Ports the Pi plugin from #1103 onto current main as one commit. The plugin
files are unchanged apart from following main's renames:

- Me chats are Solo chats now: read `soloChat` from the agent's reads, and
  fake a Solo channel in the runtime test.
- New agent's Instructions field is labelled optional.
- Keep `buzz.pi` off by default beside `buzz.usage` in the bundled-default
  checks.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
- Stop on a turn's activity aborts the Pi run it is in; a stopped turn
  posts no failure.
- List each agent's sessions for plugins: its Pi conversations, read from
  Pi's storage when first asked, with Working or Idle while it runs here.
- Hand a Solo chat's Canvas to each turn, and read no earlier messages of
  a chat its owner is still creating, as Claude and goose do.
- Give the Buzz tools the community's relay, audience and workflow
  authorities, as the other harnesses do.
- Tag Pi as a coding agent in the plugin catalog.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pins @earendil-works/pi-durable, pi-ai and chord 1.3.0 (from 1.1.0) and
follows the changes since:

- A tool result's text for the model is `output` now, not `content`; with
  `content`, Pi showed the model an empty result for every Buzz tool.
- Storage that fails now closes its Harness for good, so the host drops it
  and opens the agent again on the next request.

The live host test now checks that the model reads what a Buzz tool
answered, and that aborting a conversation leaves its request unanswered
as `aborted`, which the runtime's Stop relies on.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
The Buzz tools Claude and Codex load on search, the attention tools and
those plugins add, are deferred in Pi too: each agent's registry has Pi
durable's `tool_search`, which loads them when the model looks for them,
and they stay loaded in that conversation. The Buzz tools section says
what the search can find, as Claude's server instructions do.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…fely

From review:

- A wait that Pi's failed storage cut off now waits again once the agent is
  opened again, where the run continues, instead of failing the turn while
  the run goes on.
- A new conversation is listed, and subscribers told, as its first turn is
  handed over rather than once it ends.
- Listing sessions opens only the storage of an agent running here, and an
  agent's storage closes when it stops running here even if it only listed.
- The README opens with the short Getting started guide every bundled
  plugin's has.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi's install logic moves from bash into launch.mjs, plain Node, so it runs the
same everywhere. On Mac and Linux pi.sh only finds Node, as before, then starts
the launcher; Windows starts `node` itself. The host source and the lock go on
stdin, since together they pass Windows' 32 KB command line. File uploads read
through `host.files` instead of `base64`, so the Mac and Linux gate goes.

npm now runs inside the install folder: npm 11 refuses `ci --prefix` to a
folder outside where it runs.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…ive a host stopping mid-handover

A mention queued behind another's handover no longer opens the agent's
storage or reaches Pi once the agent is removed, retyped or disconnected; Pi's
coding tools have no sandbox. Storage the handover opened again closes.

A host that stops before it answers `deliver` may already have the turn. The
runtime asks the next host again with the same request, which Pi durable finds
by its request ID rather than taking twice. The host records a new
conversation for its key before submitting to it, so the next host finds that
conversation.

Removes docs/view-pi-session.md, which describes the retired buzz-pi-acp
integration, not Pi durable's storage.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Finds Node set up in an interactive shell's rc file, such as nvm's or fnm's
  in .zshrc, taking its path from behind a marker past what the rc prints.
- Usage adds what Pi durable recorded beyond the answers, such as compaction
  requests, as one more request per model; the host reads the totals.
- A run resumed after an interruption replaces its partial answer and no
  longer shows as stopped; an owner's Stop still does.
- The model field's button says it is the model and opens a dialog.
- A wait after an agent's storage failed is told to open it again, so the
  runtime's retry finds the accepted turn rather than dropping it.
- An agent's storage folder is 0700 and its database 0600, existing ones too;
  SQLite gives its -wal and -shm files the database's mode.
- Runs an earlier app left unfinished are taken on at the first turn after it
  starts: they show as working, and their answer or failure is posted.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…n no key commands

- A run adopted after a restart, in a conversation several threads share,
  answers in its mention's thread: the mention is read to find its root.
- Setup reads Pi's providers and defaults again each time and when the window
  gets focus, so a /login or default chosen with the pi command shows.
- The host no longer runs `!command` keys from auth.json. One host serves
  every agent, so a command held them all; such a key's turns now fail with a
  message to enter the key in Buzz.

Co-authored-by: Opus <f038cc09b32a776e7d95b65c267e9d851777ee2b6cdbd46a48edba01b1d10ebd@buzz.block.builderlab.xyz>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
… name

Buzz runs as a desktop app without the shell's environment, so a `$NAME`
key in Pi's auth.json fails its turn with the same message as a
`!command` key. Main's activity layout names the opened conversation's
button `View transcript: <channel>`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… waiting

- Credential reads take the same lock as writes and `pi`, so a key save or
  refresh can't hand another agent's request a half-written auth.json.
- Adoption resolves every left run's thread and send baseline before any
  wait, since a wait continues all of Pi's runs and one could send first.
- Pi agents get the community repositories folder, as the other types do.
- Point the no-model and package-provider errors at the Identity tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
A paused host whose stale lock another Buzz build takes over went on
running the agent beside it. It now closes that agent's storage, which
records no outcome for its runs, so the new owner continues them; opening
again here says the agent is open in another Buzz app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…ntinue them

Any delivery's wait continues every run in an agent's storage, so a watch's
or timer's run left when the app quit resumed later with no deadline, lane
or Stop. Adoption now ends those before any wait, as quitting ends other
harnesses' turns, and leaves this app's own standalone turns alone when it
lists left runs again. Recovered mentions still continue; their Stop is a
follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Keeps both Pi and relay notes in the plugins that start disabled.

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Aborting a conversation resumes Pi's whole scheduler, so ending a left
watch or timer run could let a mention left beside it send before its
thread and sends were counted, or a sibling one-off run first. The host's
drop now takes every left one-off at once and only withdraws queued
requests and marks placed runs aborted, which continues nothing. Adoption
asks for it after every mention's sends are counted and before any wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Picks up the Usage plugin name fix for the catalog scroll browser test.

Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi continues every run once asked anything. A failed `unfinished` or
`drop` was logged and the turn went on, so a wait that reopened the agent
continued the watch or timer run it had failed to end.

- A taking-on of left runs that fails now fails the turn that needed it,
  forgets the mentions it took, and is done again by the next turn.
- Opening an agent again, after its host stopped or its storage failed,
  lists and ends left runs before it waits or hands a turn over again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Pi reads its stored Buzz prompts with its own small wrapper around the
shared promptSections and promptMessage, as goose does, and Claude's
prompt helper is private again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Stop on a mention's activity needs the runner's job, which a run that an
earlier app left has none of. Pi now has the session controls Claude,
Codex and goose have, which act on a thread's conversation rather than a
request, and so reach runs taken on after a restart.

- Interrupt aborts the conversation's run, and any compaction in it.
- Compact runs Pi durable's compact() and settles once the summary is
  placed.
- New starts a new context with reset(), and the next turn is shown the
  whole Buzz conversation again.
- A mention during an action waits for it, past its handover. Each action
  takes on left runs first, as Pi continues them once asked anything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
@salman1993 salman1993 changed the title fix(pi): end left runs before continuing Pi, and trim the README fix(pi): end left runs before continuing Pi, add session controls, trim the README Oct 21, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Base automatically changed from sm/pi-harness to main October 21, 2026 20:34
salman1993 and others added 2 commits October 21, 2026 16:36
# Conflicts:
#	src/bundled/pi/README.md
#	src/bundled/pi/host.live.test.ts
#	src/bundled/pi/host.mjs
#	src/bundled/pi/index.ts
#	src/bundled/pi/runtime.test.ts
#	src/bundled/pi/runtime.ts
#	src/bundled/pi/transcript.ts
The merge of main brought back the export that #1420 added for Pi, which
now has its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Comment thread src/bundled/pi/host.mjs
Comment thread src/bundled/pi/host.mjs
Pi durable records compaction's usage only in each conversation's
totals, so Usage dates it all at the newest compaction. Manual Compact
makes that more frequent, so the limit stays documented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Stopped in the tools phase, Pi durable writes an aborted result for each
cut-off call (diagnostic code "aborted") and no aborted answer. The host
now flags those results, and the transcript marks the turn stopped and
the tool terminated rather than failed. An answer after them means only
that call was aborted and the run went on, so it clears the stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
@salman1993
salman1993 marked this pull request as ready for review October 21, 2026 21:23
@salman1993
salman1993 requested a review from a team as a code owner October 21, 2026 21:23
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T21:28:50.454033Z 2278038 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2278038391

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/bundled/pi/runtime.ts
salman1993 and others added 2 commits October 21, 2026 18:02
Pi takes on runs left in its storage before any session action, and
posting their answers needs the agent's handle. That handle came only
from a delivery, so Compact and New were refused until the agent had
been mentioned since Buzz started, unlike other harnesses. Session
actions now take the handle from Agents.handle when no delivery has
given one, so they run, with the shared busy state, at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Compact and New are checked idle before Pi's storage opens and its left
runs are taken on. A left run in the same conversation, found then,
still got the action, and an Interrupt during that preparation did not
stop the Compact or New sent after it. The action now stops when either
happened, just before it reaches Pi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
Comment thread src/bundled/pi/runtime.ts Outdated
Comment thread src/bundled/pi/runtime.ts
Comment thread src/bundled/pi/README.md
Comment thread src/bundled/pi/runtime.ts Outdated
salman1993 and others added 2 commits October 21, 2026 19:24
When the app quits or the host stops during a Buzz tool call, Pi tells
the model the call may have run, so a send it makes again may be posted
twice. The trimmed README had dropped this; a Stop or Interrupt does not
cause it, as it ends the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
…handovers on Interrupt

A turn that gave up on its run, after the host stopped twice, storage
kept failing or the retried hand-over failed, left the cached taking-on
in place. The next turn then continued that run with nothing waiting
for it. Such a turn now marks the agent's left runs to be listed again,
so the next turn takes on or ends them first.

Interrupt now also cancels a mention still being handed over, as
Claude's does: it is not delivered and says nothing, and later mentions
are unaffected.

Also covers the retried hand-over listing and ending what the stopped
host left before it delivers again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Salman Mohammed <smohammed@squareup.com>
@salman1993
salman1993 merged commit cc86318 into main Oct 22, 2026
37 checks passed
@salman1993
salman1993 deleted the sm/pi-cancel-recovery branch October 22, 2026 00:12
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.

2 participants