Repository navigation
Plannotator Inbox: the reply follows the person when two Pi or OpenCode 2 processes share a session (the lease) - #1766
Merged
Conversation
…de 2 processes share a session (the lease), and the Inbox tool in the package READMEs
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The story
When two Pi processes had one session open, the idle one took the person's reply while they worked in the other. The PR 1763 review found this (item 1, P5). OpenCode 2 has the same gap with two servers on one database: the idle server queued the wake into a session that the other server was running. This PR ports the Claude Code mod's lease to the shared wake, so the process the person used last delivers. The claim still keeps every delivery to exactly one.
What changed
packages/shared/inbox/agent-link.ts:InboxWakeholds a per-session lease atinbox/leases/<host>-<session>.json. It uses the mod'swatcher.jsonrule unchanged:{ owner, at, touchedAt }, a random owner id per wake, renewed or re-read every 5 s, stale after 20 s, released at dispose. The most recent touch wins, and a live holder keeps the lease against an older or equal touch. Only the holder polls and delivers. The wake re-reads the lease right before the mkdir claim.start()touches the lease, andtouch()is public.apps/pi-extension/inbox.ts: Pi touches the lease on aninputwhosesourceis notextension(the person typed in this process) and on aplannotator_inboxcall.startcovers a session start.apps/opencode-plugin/inbox.ts: OpenCode 2 touches the lease on everywatch(a tool call, or a run seen in this process) and on a user item entering the session.AGENTS.md(= CLAUDE.md), the "Pi and OpenCode (step 7)" paragraph: the lease and the two new proofs.Proofs (each fails on main and passes here)
apps/pi-extension/inbox.test.ts, "two Pi processes on one session: the reply goes to the one the person works in, once, and follows them back to the other". This runs the real Pi 0.84.4 in RPC mode against the compiled binary. A asks a question. B opens the same session with--session. The person works in B with the turn held at the model. 8 s after Send, A and B have 0 wakes. After B's turn ends, B gets the reply once (A 0, B 1, one claim). The person then asks again in A, and the second reply lands in A once. On main the same test fails at the 8 s check: A, the idle process, took the reply.apps/opencode-plugin/inbox-v2.test.ts, "two OpenCode 2 servers on one session: the reply goes to the one the person works in, once". This runs twoopencode2 serve2.0.19 processes on one OpenCode home and database, each with the packed plugin installed. A asks. In B the agent callsplannotator_inboxand then its turn is held. 8 s after Send there are 0 wake rows in A and 0 wake requests at the model. After B's turn ends, B has 1 wake row and A has 0. On main, packed from main's code, the test fails at the 8 s check: A queued the wake while B was running the session.apps/pi-extension/inbox.test.tswithPI_TEST_OLD_CLIset to Pi 0.79.1, plustests/parity/vendor-parity.test.ts: 8 pass. All ofapps/opencode-plugin/inbox-v2.test.ts: 3 pass.apps/opencode-plugin/inbox.test.ts(OpenCode 1): 1 pass.Commands:
Both already run in CI:
inbox-pi.yml(path-gated onpackages/shared/inbox/**andapps/pi-extension/**) and the "OpenCode 2 installed package" job intest.yml.Gates
bun run typecheck: exit 0.bun test apps/pi-extension apps/opencode-plugin: 820 pass, 1 fail.bun test packages apps/hook tests scriptsunder a temp data dir and HOME: 5807 pass, 1 fail. The two failures areplannotator-tool.test.ts"the CLI's startup error is the tool result" andannotate-cli.test.ts"occupied fixed port". Both come from this shell'sFORCE_COLOR=3(ANSI codes in the expected text). Both files pass withFORCE_COLORunset. They are unrelated to this change.bun pm pack --dry-run:apps/pi-extensionshipsinbox.ts,generated/inbox/agent-link.tsandgenerated/inbox/connection.ts, with noinbox.html.apps/opencode-pluginhas 13 files, with noinbox.html.bun run check:release-version: consistent (v0.28.7).origin/main..HEAD: no leaks.Deviations and what it leaves out
v2.event.subscribeyielded no events, so the B process in the proof joins through its agent's tool call. If B only receives a typed prompt and never calls the tool, B has no wake and A still delivers. That is the existing step 7 rule for when a wake is made (deviation 8 of PR 1763), and this PR does not change it.inbox/leases/gets one small file per session, the same asinbox/claims/. Nothing prunes them, per the no-hardening rule.uninstall --purgeremovesinbox/.plannotator_inboxmoved to their own PR (docs/inbox-package-readmes), which merges on launch day with the un-hide PR. This PR's second commit removes them; the lease code is unchanged.