{"slug": "pi-btw-side-chat", "title": "pi: /btw side-chat", "summary": "Developer Thiago Perrotta implemented a '/btw' side-chat extension for the pi coding agent, allowing tangential questions to be answered without polluting the main agent thread. The extension reads a frozen snapshot of the main conversation, calls the model directly in a custom UI overlay, and never appends to the session, preserving the main thread's state. Perrotta reimplemented the feature against the current API of earendil-works/pi, cross-checking with the bundled summarize.ts example, and added it to his dotfiles repository.", "body_md": "♠ [Previously](https://perrotta.dev/2026/07/pi-auto-retry-stalled-requests/).\n\n**Problem statement**: mid-task, I want to ask the LLM agent a quick tangential\nquestion — “will this change cause downtime?” — without polluting the main\nagent thread.\n\nClaude Code has `/btw`\n\n1 for exactly this.\n\nThe upstream extension imports the pre-fork `@mariozechner/*`\n\npackage names,\nwhich are now deprecated. As such, I use\n[earendil-works/pi](https://github.com/earendil-works/pi). Rather than trust a\ndiff I hadn’t read, I reimplemented it against the current API, and\ncross-checked against the bundled `examples/extensions/summarize.ts`\n\n(the\ncanonical example for calling the model from an extension):\n\n``` python\nimport { complete } from \"@earendil-works/pi-ai/compat\";\nimport type { ExtensionAPI, SessionEntry } from \"@earendil-works/pi-coding-agent\";\nimport { convertToLlm, getMarkdownTheme } from \"@earendil-works/pi-coding-agent\";\n\n// ...inside the /btw command handler, once at entry:\nconst branch = ctx.sessionManager.getBranch();\nconst mainMessages = branch\n  .filter((entry): entry is SessionEntry & { type: \"message\" } => entry.type === \"message\")\n  .map((entry) => entry.message);\nconst mainLlmMessages = convertToLlm(mainMessages);   // frozen snapshot\nconst systemPrompt = ctx.getSystemPrompt();\n\n// ...per question, the side-chat calls the model directly:\nconst response = await complete(\n  ctx.model!,\n  { systemPrompt, messages: [...mainLlmMessages, ...btwLlmMessages] },\n  { apiKey: auth.apiKey, headers: auth.headers, env: auth.env, signal },\n);\n```\n\nThe key move is what’s *missing*: the handler never calls `pi.sendMessage()`\n\nor appends anything. It reads the branch once, talks to the model in an\n[ ctx.ui.custom()](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md#custom-ui)\noverlay, and returns. The main session is untouched, so the next real prompt\ndoesn’t pay for any of it.\n\nAdded to [ ~/.pi/agent/extensions/btw.ts](https://github.com/thiagowfx/.dotfiles/blob/1f42eb6f6632f946c23d1278e7e3f18da78d5de8/pi/.pi/agent/extensions/btw.ts).\n\n🤖 *Drafted with *\n\n`/bloggify`\n\n.— § —\n\nReply via [email](mailto:serendipity@perrotta.dev?subject=Reply to: pi: /btw side-chat)", "url": "https://wpnews.pro/news/pi-btw-side-chat", "canonical_source": "https://perrotta.dev/2026/07/pi-/btw-side-chat/", "published_at": "2026-07-22 12:07:54+00:00", "updated_at": "2026-08-04 09:04:27.026961+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "large-language-models"], "entities": ["Thiago Perrotta", "earendil-works/pi", "Claude Code", "@mariozechner", "pi-coding-agent", "pi-ai", "summarize.ts", "btw.ts"], "alternates": {"html": "https://wpnews.pro/news/pi-btw-side-chat", "markdown": "https://wpnews.pro/news/pi-btw-side-chat.md", "text": "https://wpnews.pro/news/pi-btw-side-chat.txt", "jsonld": "https://wpnews.pro/news/pi-btw-side-chat.jsonld"}}