Skip to content

Pi Pocket 0.19.0: Pi Durable 1.3.0, subagent tiles, and the Lancet model download - #109

Merged
TannerMidd merged 64 commits into
mainfrom
release/0.19.0
Oct 21, 2026
Merged

TannerMidd merged 64 commits into
mainfrom
release/0.19.0

Conversation

@TannerMidd

@TannerMidd TannerMidd commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

What

  • Update Pi Durable and the other @earendil-works packages from 1.1.0 to 1.3.0. The built-in tools, tests, docs and examples now return output instead of content. Pi Pocket still reads content from drop-in tools.
  • Show what subagents do in peek tiles and on the subagents board. A peek tile lists the subagents at work in its session. The board shows a sub-tile for each subagent. By Steven Goldade (@smgoldade) in Show what subagents are doing in peeks and on the board #108.
  • Keep the subagent tiles short when many subagents work. A peek tile shows 3 subagent rows at most (1 in the phone strip). The board shows 6 sub-tiles for each session (3 on a phone). When exactly 1 more subagent is active, the board shows it too. N more opens the board at the first subagent that the tile does not show. The approval box on a tile names the subagent that asks.
  • Add a Download button for the LANCET Nano model to the Lancet Guard row in Menu → Extensions. The button uses the verified downloader of the package. It installs the model in the folder where Pi puts it.
  • Show the Providers dialog in 2 columns on screens 1000 px wide or more. Phones and narrow screens keep 1 column.
  • Fix the blank screen after a system swipe back in the iPhone home screen app. The app now keeps the session list under the current page in the history.
  • Add Interrupt to the send modes (Steer, Queue, Interrupt) while Pi works.
    • Interrupt stops the run at once, as Stop does. It also stops a long tool call. Then it sends the message.
    • Like Stop, Interrupt withdraws the queue. After 1 use, the send mode goes back to Steer.
    • On a narrow message box, the model chip hides while Pi works.
    • Suggested by Steven Goldade (@smgoldade).
  • Fix a message that a person sends again with the same model. The new session now uses the model that the session uses now, as the message sheet says. Before, the new session used the model from the time of the first send. By wangb (@wangbinyq) in Conversation: a message sent again with the same model takes the model the session runs on now #110.
  • Redesign the Extensions hub. Discover and Installed use quiet rows with 1 action. An entry goes through Install, Look it over, and Turn it on. The hub asks in place, not in a browser dialog. An Installed row opens and shows its settings and details.
  • Make the corners squarer across the app. Pills and avatars stay round. Themes that set their own corners keep them.
  • Let extensions add panels and status items. A panel opens next to the conversation, where Browser and Files dock. On a phone, a panel opens as a new screen. A status item shows in the status line under the message box. With this change, the Spark dashboard in Spark dashboard: a hardware status chip and a right-hand telemetry panel #86 by @benwvlshvm can be a drop-in.
  • Make these parts into slots: the top bar, the status line, the places, the buttons next to the message box, and the actions under answers. Extensions can add items to the slots. People can hide and reorder the items in Appearance. The choices of a person win.
  • Add pocket.insertText for extensions. It puts text in the message box. It never sends the text.
  • Make the app faster:
    • Draw an answer block by block while it streams. The app draws again only the block that changes.
    • Keep the session list still while an answer streams.
    • Send only the changes to the message that Pi writes (protocol 3). Older tabs still get the full message.
    • Send the session list to a tab only when the list changed.
    • Compress text answers and the event stream (brotli, else gzip). Web files get an ETag and a 304 answer.
    • List the modules of the app as modulepreload links on the app page.
    • Store a new base for busy documents every 64 changes. Before the final review, the author measured the startup time on a copy of the live data: 600 ms to 380 ms. The final review did not repeat this measurement, because the live data now has the new bases. On test data with 20,000 changes, the startup time went from 150 ms to 95 ms.
    • Read only new entries for the Changes count.
  • Fix nix build (and nix run). The fix changes 2 test checks. They expected old results. Now they expect what the app does today. No app code changed. By Michał Zając (@Quintasan) in Fix nix build: the picker's ~ path, and what a drop-in installs #111.
  • Let extensions start a linked session in a different folder. linkedSessions.start takes an optional cwd. Without cwd, the session starts in the folder of the source, as before. By Brian Ketelsen (@bketelsen) in Allow linked sessions to select a working directory #112.
  • Remove the line "Do not commit, push, publish, or deploy unless asked" from the base prompt of Pi Pocket. People said that the line was too restrictive.
  • Remove the rule "Do not commit, push, or publish unless the person asks." from docs/index.md and docs/self-editing.md. In docs/self-editing.md, the change also removes the sentence "Show the diff instead."
  • Remove the message limit between linked sessions. Before, a team stopped after 20 messages between its sessions (10 turns each way) until a person wrote. Now no message count stops them. Also remove the cap of 10 linked sessions for each session. Now no count limits the linked sessions that a session starts. The other limits of a team stay.
  • Add a rule block to the rules of PiP. PiP now writes text for other agents in ASD-STE100 (Simplified Technical English), at about 80% adherence. This covers messages to sessions, scheduled messages and the instructions of watches. The answers of PiP to its person do not change.
  • Fix the findings of the final review:
    • Tell linked sessions to answer once, and not to reply to thanks. No count limit applies to a team. The spend limit of the top session is the brake.
    • Correct the old team limits in SECURITY.md, docs/map.md and docs/features.md.
    • Show cwd on the stable HumanLinkedSessions.start in the reference, and correct 8 small points in the docs of the extension API.
    • Keep the run when an Interrupt request comes again with the same request id.
    • Start a linked session with a blank cwd in the folder of the source.
    • Refuse a top bar button and a panel with the same id. Redraw an action under answers after update().
    • Set an unused Interrupt back to Steer when Pi stops. Keep Stop inside the message box on screens narrower than 360 px.
    • Raise pocketApi from 1 to 2. A gallery draft now gives the pocketApi that the module exports.
  • Fix 2 regressions of the fix round. A chosen Queue stays chosen when Pi stops. The actions under answers do not draw again when a module adds the same action again.
  • Make agents write to each other in Simplified Technical English (ASD-STE100):
    • PiP and sessions, both ways. PiP asks each session to answer it in ASD-STE100. A session answers a message that came through PiP in ASD-STE100.
    • Linked sessions. Pi writes team messages in ASD-STE100 and asks for answers in it. A linked message asks for the reply in ASD-STE100, and every session prompt names the [via linked session …] marker.
    • Subagents. Pi writes each task for a subagent in ASD-STE100 and asks for the report in it.
    • The answers to messages from people do not change.
  • Make Pi save a lesson only when a person asks for it, or when Pi is sure that the lesson will help in many later sessions. On its own, Pi does not save a one-off fact, a niche detail, a guess, or a lesson for a rare case.
  • Set version 0.19.0 in the package, the lock file and the site. The site now tells about the subagent tiles and the model download.

Why

  • Pi Durable 1.2 renamed a field of the tool result. Without the shim, a drop-in tool that still returns content gives the model an empty result.
  • The peeks and the board must show what each subagent does. A person must not have to open the subagent. The message of the commit 242bbbf says that 20 subagents in 1 session made a tile about 1,300 px tall. Nobody repeated this measurement. For this reason, the follow-up change limits the rows.
  • The guard needs its model. Before, only the /lancet-guard setup command of Pi downloaded it.
  • The Providers dialog was a long single column on wide screens.
  • Before, a Steer message waited for minutes behind a long tool call or a long model turn. Interrupt sends the message at once.
  • Without a page under the app, the system swipe back left the home screen app on a blank page.
  • A message that a person sends again with the same model must use the model that the sheet names.
  • Before, extensions had only these places: pages, sheets, rail buttons, menu rows and launcher actions. A dashboard such as Spark dashboard: a hardware status chip and a right-hand telemetry panel #86 needed a change to the core.
  • nix build failed in its test steps. Because of this, Nix users were not able to install Pi Pocket.
  • An extension that coordinates work across repositories had to start every linked session in the folder of the source session. A coordinator in project A was not able to start a specialist in project B.
  • A team of linked sessions stopped after 10 turns each way. This limit was too low.
  • A session started 10 linked sessions at most. This cap was too low.
  • PiP writes the messages that start and guide sessions. The new rule gives these messages one clear style.
  • Agents read long messages from other agents in many styles. The same plain style keeps the facts clear for each agent.
  • The memory tool proposed lessons for small one-off facts. Each lesson waits for the approval of the owner, so the owner had to dismiss many of them.
  • A long answer cost its length squared, in the data for each tab and in the time to draw it. Pi Pocket compressed nothing. Each reload fetched about 80 web files (about 2 MB). The 0.18 page loads 80 files with 1,976,318 bytes. A cold read replayed every change that Pi Pocket ever stored for a document.

Fixes #106 (through #111). The commits of #108, #110, #111 and #112 are in this branch.

For extension authors

Packages (A1). Pi Pocket 0.19 pins these packages at 1.3.0. In 0.18, they were at 1.1.0. A drop-in gets the same versions through the node_modules link.

  • @earendil-works/pi-durable 1.1.0 → 1.3.0
  • @earendil-works/pi-ai 1.1.0 → 1.3.0
  • @earendil-works/pi-coding-agent 1.1.0 → 1.3.0
  • @earendil-works/pi-codemode 1.1.0 → 1.3.0
  • @earendil-works/pi-mcp 1.1.0 → 1.3.0
  • @earendil-works/chord 1.1.0 → 1.3.0

Changed. In Pi Durable 1.2, the result of the execute of a tool has output where it had content.

  • New code returns { output: [{ type: "text", text }] }. The guides and examples now show this form.
  • A drop-in that returns { content: [...] } continues to work. For the tools of a drop-in, Pi Pocket reads content as output. If a result has both fields, output wins. The shim does not change isError, details or image parts.
  • The shim covers only the execute of the tools of a drop-in. A hook (afterTool) or a wrap (wrapTool) that reads or returns a result must use output. An old hook or wrap that returns content gives the model an empty result, with no error. For this reason, pocketApi goes to 2.
  • A module that returns output needs Pi Pocket 0.19.0 or newer. It exports pocketApi = 2. A gallery entry for it must set minPocket to 0.19.0.
  • The shim stays at least until a later minor release. The notes of that release will tell when the shim goes. The removal of the shim is a breaking change, and it raises pocketApi (A18).

Other Pi Durable 1.2 and 1.3 changes that a module can notice:

  • bash returns an error result for a command that exits with a nonzero code. It does not throw an error. The result has an exit_code diagnostic.
  • A tool that is not replay-safe can make nested calls. After a restart, Pi Durable abandons the unfinished nested calls of that tool.
  • ToolHookCall has a new optional field, parent, for nested calls.
  • A tool call with malformed JSON arguments fails validation. It does not run with the fields that Pi Durable parsed before the error.
  • section, hook, wrapTool, api.memo and api.snapshot work as the docs say. 6 installed drop-ins loaded and ran on 1.3.0. Nobody tested a 7th drop-in (agent-civilization.ts) alone.

Added (experimental, since 0.19.0). All of these members are in pocket, the browser module API. They can change in a minor release (A4). Before you use a member, make sure that it is there (A10). In a gallery entry, set minPocket to 0.19.0.

  • pocket.panel(spec), pocket.openPanel(id), pocket.closePanel(): a panel next to the conversation.
  • pocket.status(spec): an item in the status line, with a short form for narrow lines.
  • pocket.topbar(spec): a button in the top bar.
  • pocket.composerButton(spec): a button on the left of the row of the message box.
  • pocket.replyAction(spec): an action under the answers of Pi. Its run gets the answer.
  • pocket.insertText(text, options): put text in the message box. It never sends the text.
  • pocket.arrange(slot, arrangement): hide or reorder the items of a slot.
  • An Action can now be { panel }.
  • New types: PanelSpec, StatusSpec, Placement, TopbarSpec, ComposerButtonSpec, Reply, ReplyActionSpec, SlotName, Arrangement.
  • New theme parts: extension-panel and status-item, each with data-module. data-focus can be extension-panel.
  • pocket.topbar and pocket.panel refuse an id that the other already has in the same module. replyAction(...).update() now redraws the actions that show.
  • types/pocket.d.ts marks each new member @since 0.19.0 and @experimental. The reference says "Since 0.19.0 · Experimental".

Added (since 0.19.0), on the server. From #112.

  • cwd in host.linkedSessions.start(source, { …, cwd }, key): the folder for the new session. It must be a folder that exists. It uses the path rules of the folder picker. It is experimental, as host.linkedSessions is.
  • cwd in the human-bound request.linkedSessions.start({ …, cwd }, key) of a conversation route. That interface is stable, so this option is stable too.
  • Without cwd, the session starts in the folder of the source, as before. A blank cwd counts as no cwd. A relative cwd resolves against the folder of the server process, not the folder of the source.
  • An older Pi Pocket ignores cwd. A gallery entry that needs cwd must set minPocket to 0.19.0.
  • The types, the host reference, the linked-sessions guide and the API versions table say "since 0.19.0". No other member of the host or web changed.
  • Since 0.19.0, host.linkedSessions.send and start do not stop at 20 messages between the sessions of a team. The stable human-bound request.linkedSessions.start does not stop at 20 messages either: the removal is a higher limit, an addition (A8), not a breaking change. Before, they refused with a 409 error until a person wrote. The limits page now says "no limit". The other limits stay. A linked session starts none. The spend limit of the session that started the team applies.
  • Since 0.19.0, host.linkedSessions.start and the stable human-bound request.linkedSessions.start do not stop at 10 linked sessions for each session. Before, the 11th start failed with a 409 error. The removal is a higher limit, an addition (A8), not a breaking change. The limits page now says "no limit".

Themes. The default corner tokens are smaller: --r-mark to --r-float go from 3–20 px to 2–12 px. --r-pill stays at 999 px. The token names do not change. A theme that sets its own corners keeps them.

Deprecated, Removed, Reserved names, Security: no changes.

pocketApi goes from 1 to 2.

  • Rule A2: when a package update breaks a use that the guides document, pocketApi goes up (A7). A2 names hook and wrapTool.
  • Pi Durable 1.2 renamed content to output. An old afterTool or wrapTool that returns content gives the model an empty result, with no error.
  • Modules that export pocketApi = 1, or no pocketApi, continue to load (A9). The oldest supported version is 1.
  • A module that returns output can export pocketApi = 2. Then Pi Pocket 0.18 refuses it with a clear message. The guides and examples that return output now export 2.
  • A module for a newer version than 2 does not load. The Extensions sheet tells why.
  • The panels, status items, slots, cwd, and the higher limits of a team are additions (A8). They alone do not raise pocketApi.
  • Protocol 3 and the compression change the events between the server and its tabs. Those events are internal. Drop-ins do not use them.

Tested

  • npm run check, npm run format:check and npm run lint pass in a clean clone of the head (fe5899d), after npm ci.
  • Full npm test in a clean clone at the head (fe5899d): 384 tests, 384 pass, 0 fail.
  • A new test in test/agent-teams.test.ts checks the ASD-STE100 rules of the team tool and the subagent tool, the reply line in a linked message, and the marker rule in the prompt of the linked session. The test fails when any of the 4 texts is reverted.
  • The PiP test in test/pip.test.ts now also checks that PiP asks each session to answer in ASD-STE100, that a session sees the [via PiP] marker, and that its prompt has the rule for that answer. The test fails on the old code.
  • At f32ecb4, a first full run had 7 failures, all in test/mobile-ui.test.ts ("Timed out waiting for the session list"). Other heavy work ran on the machine at that time. The next full run passed.
  • A new test in test/memory.test.ts checks that the prompt of a session and of PiP has the new memory rule and its safety sentences. It also checks that the old words "when you learn something that will help later sessions" are gone, and that the tool description says when to use add. The test fails on the old text.
  • At 2af63f9, the full npm test gave 382 tests, 382 pass, 0 fail.
  • A first full run of the same commit had 1 failure: "Pi uses the browser tool" in test/browser.test.ts, a known flaky test. That file passed alone 2 times.
  • At 4f9f203, the full npm test gave 380 tests, 380 pass, 0 fail.
  • New tests of the regression fixes. Each test fails on the old code:
    • test/composer-focus-ui.test.ts: a chosen Queue stays chosen for the next run.
    • test/extension-ui.test.ts: the same action under answers, added again, keeps the key of the slot. This covers the same object and a new object with the same fields and run. A new run changes the key, and the new run runs.
  • A check by hand in a copy at 360 px and 1280 px: Queue stays, an unused Interrupt goes back to Steer, and the same action keeps the key.
  • The docs site builds 44 pages. npm pack --dry-run passes.
  • At f36d9af, a first full run had 1 failure: a browser timeout in test/extension-ui.test.ts. That file passed alone 2 times, and the second full run passed.
  • New tests of the fix round. Each test fails on the old code:
    • test/agent-teams.test.ts: the team tool and the linked message tell Pi to answer once and to stop when the work is done.
    • test/app.test.ts: an Interrupt with the same request id does not stop the run that it started.
    • test/linked-sessions.test.ts: a blank cwd starts the session in the folder of the source.
    • test/extension-ui.test.ts: replyAction(...).update() redraws the label and runs the new run.
    • test/extension-ui.test.ts: a top bar button cannot take the id of its panel, and an order with an id 2 times keeps every button.
    • test/composer-focus-ui.test.ts: an Interrupt that a person chose but did not use does not stay for the next run.
    • test/composer-focus-ui.test.ts: at 320 px and at 350 px, Stop stays inside the message box while Pi works.
    • test/dropins.test.ts: a drop-in for API 2, 1, or none loads. Pi Pocket refuses a drop-in for API 3, 0, or 1.5 with a clear error. A gallery draft gives the pocketApi that the module exports.
    • test/extension-ui.test.ts: a browser module for API 2 loads. A browser module for API 3 or 0 tells why it does not load.
  • A check by hand in a copy (test/serve.ts) at 320 px, 360 px and 1280 px: Stop fits at 320 px. The layout at 360 px and at 1280 px did not change. The slot fixes work.
  • A fresh review of the diff of this round (rules A2, A7, A8 and A9) found 0 blockers, 2 minors and 3 nits. This round fixed the 2 minors and 2 nits. It added a comment for the 3rd nit (2 Interrupt requests with the same id at the same moment).
  • A new test starts 12 linked sessions from 1 session. No count refuses one. The test fails on the old code with the cap error.
  • A new test in test/pip.test.ts checks that the prompt of PiP has the ASD-STE100 rule, and that the prompt of a session does not. The test fails without the rule block.
  • A new test sends 25 messages between 2 linked sessions (12 turns each way after the first message). No count stops them. The test fails on the old code with the 20-message error.
  • An earlier run at eada028 had 1 failure: the cleanup step of test/composer-focus-ui.test.ts (a temporary folder was not empty yet). Its tests passed. It did not fail in the later full runs.
  • Allow linked sessions to select a working directory #112 adds tests for cwd. They cover the new folder, its project instructions and shell tools, and an unchanged source. They also cover the default folder, bad folders, a repeated start, and the human-bound start.
  • New tests cover Interrupt, the same-model send again (Conversation: a message sent again with the same model takes the model the session runs on now #110), live deltas, and the sends of the session list. They also cover compression and ETags, document bases, and the Changes count. Other new tests cover how the app draws an answer while it streams.
  • test/back-home-screen.test.ts is a small test of the swipe-back fix.
  • The message of each earlier commit tells its test:
    • Pi Durable update: an upgrade check on synthetic data.
    • Lancet model: a live download in a throwaway copy.
    • Subagent tiles: a live check with 20 subagents.
    • Panels and slots: a demo drop-in on a test copy at desktop and phone sizes.
    • Performance: measurements on a copy of the live data.

Notes

  • The final review (7 reviewers): ready, 0 blockers. This round fixed M1 (with text, not with a hard limit), minors 2 to 8, 10 and 11, and 10 nits. pocketApi 2 answers minor 1. This round did not fix minors 9 and 12 to 16, or the other nits.
  • The final live test: 14 pass, 0 fail, 1 not tested (the server log, which goes only to the terminal of the launcher).
  • The post-fix check of 4f9f203: the live checks gave 7 of 7 pass. The 2 reviewers found 0 blockers, 0 majors, 3 minors and 8 nits. The last round fixed 1 minor and 1 nit: the 2 regressions. These stay open:
    • A pocketApi that is a string or null gets a wrong error text. Pi Pocket still refuses the module.
    • A gallery draft reads export const pocketApi: number = 2 as API 1.
    • 7 small points in the docs and the tests.
    • The race of 2 identical Interrupt requests at the same moment.
  • A copy of the live data that 0.19 used started again on 0.18 and showed all 252 sessions.
  • Nobody can check these points:
    • a swipe back in a real iPhone home screen app;
    • compressed event streams on Safari and through Cloudflare tunnels;
    • nix build;
    • a real download of the LANCET model;
    • long use of 0.19, and then a return to 0.18;
    • how often real models loop in a team.
  • The base prompt change and the web/ changes apply without a restart. The Pi Durable update and the server performance changes need a restart. The live server already had this restart.
  • Allow linked sessions to select a working directory #112 changes server code (src/server/linked-sessions.ts and src/server/host.ts). The live server runs that code since its restart. Fix nix build: the picker's ~ path, and what a drop-in installs #111 changes tests only.
  • The message limit change and the removal of the cap are server code too (src/server/linked-sessions.ts). The live server runs that code since its restart. The new text of the team tool (src/server/extensions/agent-teams.ts) loads without a restart.
  • The ASD-STE100 rule of PiP is server code in src/server/extensions/pip.ts. The live server loads a changed file in src/server/extensions/ again by itself. Thus this change needs no restart. Menu → Restart server also loads it.
  • The fix round changes these server files: src/server/commands.ts, src/server/linked-sessions.ts, src/server/extension-web.ts, src/server/reload.ts, src/server/gallery/share.ts and src/server/gallery/entry.ts. The live server runs them since its restart. src/server/extensions/agent-teams.ts loads again by itself. The web/ changes load in each browser by themselves.
  • The rules for ASD-STE100 between agents are in src/server/extensions/pip.ts, prompt.ts, agent-teams.ts and subagents.ts. The live server loads them again by itself, without a restart.
  • The reply line in a linked message is in src/server/linked-sessions.ts. The live server needs Menu → Restart server before it uses that line. The other rules work without it.
  • The memory rule is in src/server/extensions/memory.ts. The live server loads it again by itself, without a restart. Sessions get the new text in their next request.
  • Restart: the live server restarted at 18:51 on 2026-10-20. It runs the server files of 4f9f203. The round of the 2 regressions changes only web/ files, tests and 1 docs line. Thus it needs no restart.
  • An older tab continues to work. It gets the full live message, as before. A 0.18 data folder upgrades. The tests do not cover a return to 0.18 after 0.19 ran on a data folder.
  • This PR does not merge, tag or publish anything.

TannerMidd and others added 30 commits October 19, 2026 20:32
The guard needs the LANCET Nano model, which is a one-time download that
only Pi's /lancet-guard setup could do. The owner now gets a Download
button on the guard's row in Extensions. It uses the package's own
verified downloader and installs where Pi puts the model, so Pi uses it
too. Progress shows in every tab. A guard that is on without its model
now shows as failed with the reason.

Tested: check, lint, app tests, a new lean model test, and a live
download and score in a throwaway copy.
On screens 1000px and wider, the owner's Providers sheet is 1160px wide
with a fixed height. Providers (with search) are on the left, grouped as
Needs sign-in, Signed in and Not configured. Refresh models and Models
for subagents are in a side panel on the right. Each side scrolls on
its own. Narrower screens and phones keep one column, as before.

Tested: npm run check, lint, sign-in-ui, subagents-ui, peeks-ui,
pip-chat-ui; looked at 1920x1080, 1280x800, 900x900 and phone.
Pin pi-durable, chord, pi-ai, pi-codemode, pi-coding-agent and pi-mcp
to 1.3.0 (exact). The lock file drops esbuild (chord no longer needs
it) and adds @silvia-odwyer/photon (WebAssembly, no install script).

Pi Durable 1.2 renamed a tool result's `content` to `output`. Built-in
tools, tests, docs and examples now return `output`. A drop-in that
still returns `content` would give the model an empty result, so the
loader reads `content` as `output` for drop-ins.

Tested: check, lint, format:check, full npm test, and an upgrade check
on synthetic data (history, an interrupted tool call and an interrupted
generation recover; a second restart keeps the data).
The shim that reads `content` as `output` for drop-in tools wrote into
the array a module returned, so a frozen list made the load fail. It
also copied a tool with a spread, which loses the prototype and getters.

Now the loader builds a new list, and copies tools and extensions with
their property descriptors and prototype. The drop-in test returns a
frozen list. The docs say that hooks and wraps must use `output`.

Found in review of the Pi Durable 1.3.0 update.
Peek tiles now include sessions whose subagents work, with a live sub-tile for each active subagent: its name, state, and current call or words, from its own peek. The subagents board shows the same sub-tiles under each session row, so what each is doing is visible without opening it; a click picks one, and a double click or double tap opens it.

Share nowDoing and the sub-tile in agent-now.js and agent-tile.js, extend the server's peek summary with the session's active agents, and raise the per-tab live peek limit to 20.
Pin the @earendil-works packages at 1.3.0, return tool results as
`output`, and read `content` from drop-in tools.
Peek tiles list the active subagents of a session, and the subagents board
shows a sub-tile for each active subagent, with what it does now.

By Steven Goldade (@smgoldade) in #108.

Tested: format, check, lint, full npm test (350 pass), live check on a copy.
Follow-up to #108 (Steven Goldade). With 20 subagents in one session, a peek
tile grew to about 1,300px and the phone strip covered the conversation.

- Peek tiles show at most 3 subagent rows (1 in the phone strip), one line
  each, waiting ones first in approval order. "N more" opens the board at
  the first one left out.
- The approval box on a session's tile names the subagent that asks.
- In the column, the footer says when only subagents keep a session busy
  ("3 subagents working", "2 subagents need you").
- The board shows 6 sub-tiles per session (3 on a phone), plus the selected
  one, with "Show N more".

Tested: format, check, lint, peeks and subagents tests (24 pass), live check
with 20 subagents and 2 approvals at desktop and phone sizes.
Set the package version and site version. Describe subagents in peek tiles and the Lancet model download on the site.
Interrupt stops the run at once, tool call included, as Stop does, and then
sends the message. It also withdraws the queue, and goes back to Steer after
one use. On a narrow message box the model chip hides while Pi works.

Tested: new app test (fails without the change), check, lint, npm test,
and by hand at phone size.
…l the session runs on now

A fork starts from the agent as of its fork entry, so sending a message again without naming a model left the new session on the model the session had when that message was sent, while the message sheet named the model it runs on now. Name the model and its thinking level in the fork's agent change, whether the model was picked or is the session's own.

The new check sends two messages, switches the session to another model, and sends the second again with the same model: it fails before the fix and passes after it. All 334 tests, check, lint and format checks pass.
…el the session runs on now

Fixes the resend fork keeping the model from the time of the message.
Thanks to wangb (@wangbinyq) for the fix and test.
Discover and Installed use quiet rows with a steady mark and one action, like the menu. An entry walks through Install, Look it over, and Turn it on, which asks once more in place instead of a browser dialog; what it adds shows in plain words. Installed rows open to show settings and details, and a drop-in asks in its row before it turns on. Tag filters keep to one row; corners are squarer.

Tested: npm run check, lint, and the full test suite (353 pass); checked phone and desktop layouts on a copy.
Lower the corner tokens: rows 6px, controls 7px, cards 8px, windows 10px, sheets and PiP 12px. Pills and avatars stay round. Themes that set their own corners keep them.

Tested: npm run check, lint, and the full test suite (353 pass); looked at home, a session, and the Extensions hub on a copy.
No Pocket document set checkpointWhen, so a cold read replayed every delta
ever stored: pocket.spend had 63k (272 ms, 243 ms of startup). Every
document now writes a base after 64 deltas; for these current-only
documents Pi Durable then drops the older revisions. checkpointWhen is not
part of the stored semantics (only scope, history, fork are checked).

On a copy of the live data: startup 600 -> 380 ms, spend cold read
272 -> 0.6 ms. Tested: test/docs.test.ts (fails before), check, lint.
The top bar's Changes count read, parsed, and projected a session's whole
history (images included) on every look: every 4-8 s while Pi works and
after each new entry, per tab; ~100 ms for the biggest sessions. Pi's
edits only need edit/write calls, errors, and codemode results, and
entries never change, so the workspace keeps those facts per session (64
sessions) and reads only entries newer than the last one seen
(minEntryId, which also covers a fork's inherited history).

On a copy of the live data: changes() 98/66/68 ms -> 5 ms per call (the
rest is git), same results for 40 sessions. Tested: changes, sessions,
history tests; a new test of edits since the last look, in a fork too.
…s line

Extensions can now add a panel beside the conversation (pocket.panel, where
Browser and Files dock; a pushed screen on phones), an item in the status
line (pocket.status, with a short form for narrow lines), and a top bar
button (pocket.topbar). The top bar and status line are now slots of named
items (web/slots.js): the app's own (core:…) and extensions' (ext:…), placed
by default order, before/after, pocket.arrange, and then each person's own
choices in Appearance (Top bar buttons, Status line items), which win. The
menu, Lancet Guard, and the connection warning stay fixed. All new members
are experimental in 0.19.0. Built so PR #86's Spark dashboard can be a
drop-in instead of a core change.

Tested: npm run check, lint, and the full npm test (one known load flake in
browser.test.ts passes alone); a Spark-like drop-in on a test copy at
desktop and phone sizes.
Each room update sent a tab the whole message being written, every 90 ms,
so a long answer cost its length squared: a 13.7 KB answer sent 1.6 MB of
live message to each tab. Tabs that name protocol 3 now get only what
changed (the blocks they keep, and the new characters of the block that
grows); web/live.js puts the message together. Older tabs still get the
whole message. The live run is also projected once per state, not once
per tab.

Tested: npm run check, lint; projection and app tests (a round trip over
block changes; a protocol 3 tab rebuilds what an old tab gets whole).
The session list (about 77 KB on the owner's data) went to every tab on
any change that made it be worked out again, such as another tab of the
same person opening a session, whether it changed or not. Each tab now
keeps the JSON it last got, as the board and PiP already do, and an
unchanged list is not sent again.

Tested: npm run check, lint; an app test (fails before the change).
The places' tiles, the buttons on the left of the message box's row, and
the actions under Pi's answers are now slots too, so people can hide and
reorder them in Appearance and extensions can add to them:
pocket.composerButton, pocket.replyAction (its run gets the answer), and
pocket.insertText, which puts text in the message box and never sends.
More under an answer stays fixed; Send, Stop, the mic, and Steer and Queue
stay out of the slot. Places keep their data-place names.

Tested: check, lint, full npm test (mobile-ui rerun after the data-place
fix), a demo drop-in on a test copy at desktop and phone sizes.
The New session picker shows a folder inside the home folder as ~/…, and
a Nix build's sandbox sets HOME to the build directory, where the tests'
own temporary folder is. Comparing the picker's text against the absolute
path therefore timed out there, failing every `nix build`: the one place
the test runs, as a machine without Chromium skips it.

Compare through the app's own shortPath instead.
ExtensionInfo gained readOnlyTools, hooks, sections, and wraps with the
gallery's entry format, while installed.mjs still compared the older
{ name, tools } shape: the package's install check failed as soon as the
source checks passed.

Note both traps in nix/tests/README.md, the sandbox's HOME among them.
Every store change draws the app from the top. A streamed update changes only
`view`, yet the session list (127 rows, mounted off-screen on phones) drew
again each time: 24 of 28 ms per update at 4x CPU on a phone.

`apartFromViews` (ui.js) skips a change to `view` or `pipView` alone; the
session list and the rail use it. Their own state, and any other store change
(`store.set({})` too), still draw them.

Tested: a view-only change now costs ~8 ms instead of ~30 ms (4x, mobile).
The live answer's Markdown was parsed, sanitized, and colored whole on every
update, which grows with the square of its length (about 4 s of a 33 s stream
at 4x CPU). Now `Markdown` with `cache={false}` finds the top-level blocks
(marked's block lexer only) and keeps each block's HTML from the update
before, so only the block that changed, mostly the last, is drawn again.
A whole HTML page still renders as one block. Finished text renders as before.
Review findings: a streaming reply gave no conversation to its images (they
did not point at the server until the answer was whole); live blocks sat in
.md-part, whose rule zeroed headings' top margin, which jumped back at the
end; empty parts (a link definition) could take the cursor's place.
The test now also checks the image path.
…dited in the last 2 s

What: keepsUp refuses once the compressor is ended; the compressor's unsent
input no longer counts as bytes the tab is behind (it has its own cap); a file
changed within 2 s is read again each request (a same-size edit in one clock
tick); send() merges an existing Vary.
Tested: test/compression.test.ts, check, lint.
…installs

Fixes the two test expectations that made nix build fail (#106).
Thanks to Michał Zając (@Quintasan) for the fix.
Adds an optional cwd to host.linkedSessions.start and the human-bound start.
Thanks to Brian Ketelsen (@bketelsen) for the change and tests.
Adds the Since 0.19.0 note to the types, the host reference, the linked-sessions guide and the API versions table. An older Pi Pocket ignores cwd, so the docs say to set minPocket.
Removes the sentence "Do not commit, push, or publish unless the person asks." from docs/index.md and docs/self-editing.md. The base prompt already dropped the same rule in this release.
Removes "Show the diff instead." from the rules list in docs/self-editing.md. It was left alone when ae1532c removed the commit, push and publish rule before it.
A team stopped after 20 messages between its sessions (10 turns each way) until a person wrote. Tanner asked for no turn limit. The send and start checks on that count are gone; the count itself stays in its stored document. The other bounds stay: 10 linked sessions per session, none from a linked session, the team's spend limit, plan mode and take turns. Adds a test of 25 messages between two linked sessions, and updates the docs.
Adds a rule block to PiP's rules: messages to sessions, scheduled messages and watch instructions follow ASD-STE100 at about 80% adherence. PiP's answers to its person do not change. Adds a test that PiP's prompt has the rule and a session's prompt does not, and a line in docs/features.md.
A session could start at most 10 linked sessions; the 11th start failed with a 409 error. Tanner asked for no cap. The count and its check are gone. The other bounds stay: a linked session starts none, a subagent cannot start linked sessions, the spend limit, plan mode, take turns, the message size limit, and replay keys. Adds a test that one session starts 12 linked sessions, and updates the docs.
…done

With no count limit since 0.19.0, a team can loop. The team tool's start reply now says "once", and the linked message asks for no reply to thanks or to messages that ask for nothing. Text only, no hard limit. The guide and the limits page say that the spend limit of the top session is the brake. Adds test/agent-teams.test.ts (fails on the old text). Final review M1.
SECURITY.md and docs/map.md still named the 20-message limit and the 10-session cap that 0.19.0 removed. features.md now names the cwd folder of a linked session, and a comment in linked-sessions.ts no longer calls the count a limit. Final review minors 3 and 4, and 2 nits.
…nal review

web.md shows cwd (since 0.19.0) on HumanLinkedSessions.start. The theme surface marks status-item, extension-panel, data-focus and extension composer buttons since 0.19.0. Placement names composerButton and replyAction, the top bar position matches the slot, the not-in-a-slot lists name Interrupt, limits.md names every id kind, api-stability lists the 0.19 experimental members and the core: slot ids, and a long comment in server-status.js wraps. Final review minor 5 and 8 nits.
Commands.submit aborted the busy run before it found that the request was a retry. It now asks submissionByRequest first, and skips the abort for a request that it knows. Adds a test in test/app.test.ts that fails on the old code. Final review minor 2.
… full

On the old code the retry aborts the second run before it writes an answer, so the test now expects a finished answer instead of reading a missing entry.
An empty or blank cwd went to checkDirectory and gave the home folder. It now counts as no cwd. A relative cwd still resolves against the server process's folder, and the docs and types now say so. Adds a test that fails on the old code. Final review minor 6.
A panel and a top bar button of one module could share an id and make one top bar item twice; a person's reorder then dropped core:people. pocket.topbar and pocket.panel now refuse an id that the other has, and reorder takes each id once. replyAction(...).update() did not redraw the actions that already show: each version of a spec now gets a revision in slotKey, so the label and the run change. Applies the reviewers' patches pp-019-patch-api-1 and -2, with 2 tests in test/extension-ui.test.ts that fail on the old code. Final review minors 7 and 8.
A chosen but unused Interrupt stayed selected for the next run, so Enter stopped it. The send mode now goes back to Steer when Pi stops working. At 320 px, Steer, Queue and Interrupt pushed Stop out of the box while Pi works: at a box of 320 px or less, Attach hides while Pi works and the three get closer; 360 px and 1280 px do not change. 2 tests in test/composer-focus-ui.test.ts fail on the old code. Final review minors 10 and 11.
Pi Durable 1.2 renamed a tool result's content to output. The shim covers a drop-in tool's execute, but a 0.18-style afterTool or wrapTool that returns content gives the model an empty result with no error (rules A2 and A7). POCKET_API is now 2 on the server and in the browser. The oldest supported version is 1: a module with 1, or with none, still loads (A9), and the loaders refuse an older one. The guides and examples that return output export pocketApi = 2, so 0.18 refuses them with a clear message. Tests: a drop-in for 2, 1 or none loads and one for 3 is refused (test/dropins.test.ts), and the same for a browser module (test/extension-ui.test.ts). Both fail on the old code.
…sion checks

The fresh review of this round found: Stop still overflowed for message boxes of 321 to 333 px (screens of 347 to 359 px), so the rule now covers a box of 333 px or less (a 360 px screen's box is 334 px, and does not change). A gallery draft took this Pi Pocket's pocketApi (2) for every module: it now takes the highest pocketApi that the module's files export, else 1. The loaders refuse a pocketApi that is not a whole number. The entry error says "such as 2". A comment names the rare race of 2 same-id interrupts at once. Tests: pocketApi 0 and 1.5 on the server, 0 in the browser, the draft's version, and the composer at 350 px.
…s nothing again

9b68685 set the send mode to Steer each time Pi stopped, which also dropped a chosen Queue. Now only an unused Interrupt goes back to Steer; Steer and Queue stay. a518f3c stored the new spec object even when a module added the same action again, so revisionOf gave it a new revision, slotKey("reply") changed, and the answers drew again. add() now keeps the stored spec when sameSpec says it is the same; a changed field or run still draws again and runs the new run. features.md says when Interrupt goes back. Tests in test/composer-focus-ui.test.ts and test/extension-ui.test.ts fail on the old code. Post-fix check: A2 minor 1 and nit 4.
… sessions

The memory guide told Pi to save whatever it learned that might help later, so Pi proposed lessons for small one-off facts, and the owner had to dismiss each one. GUIDE now names 2 cases: a person asks, or Pi is sure that the lesson will clearly help in many later sessions. On its own, Pi skips one-off facts, niche details, task results, guesses and rare cases, and does not save when unsure. Every safety sentence stays. The tool's description, the Memory bullet in features.md and the Memory sheet's intro say the same. A new test in test/memory.test.ts (sessions and PiP) fails on the old text.
PiP already wrote its tasks to sessions in ASD-STE100. Now PiP asks each session, in its task, to answer in ASD-STE100 too, and every session's prompt says to answer a message marked [via PiP] in ASD-STE100, at about 80% adherence. The session's answers to people do not change. Both files are extension modules, which the server loads again by itself. The PiP test now checks both rules, and fails on the old code; features.md says the same.
…al English

Linked sessions and subagents now use ASD-STE100 (about 80% adherence) for what one agent writes to another, as PiP and sessions already do. The team tool tells Pi to write its team messages so and to ask for answers so; the subagent tool does the same for tasks and reports. Every session's prompt now names the [via linked session …] marker next to [via PiP]: its answer and what it sends back go in ASD-STE100. The wrapper of a linked-session message (src/server/linked-sessions.ts) asks for the reply in ASD-STE100: that file needs Menu → Restart server. Answers to people do not change. A new test in test/agent-teams.test.ts fails when any of the 4 texts is reverted.
@TannerMidd
TannerMidd merged commit d2a3280 into main Oct 21, 2026
2 checks passed
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.

nix run fails on checkPhase

5 participants