Bscholar/pi rpc#2812
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These tests fail due to an undici content-length validation incompatibility and are unrelated to the desktop app. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Only build macOS arm64 and Linux x64 artifacts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No RELEASE_APP_ID/RELEASE_APP_PRIVATE_KEY secrets configured. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs preventing Pi responses from appearing in chat: 1. sendTurn awaited prompt() which blocks until the entire turn finishes, preventing the caller from returning. Changed to fire-and-forget via runFork (matching Claude adapter pattern). 2. message_update handler checked for wrong properties (.text/.thinking) instead of the actual SDK shape (.type === "text_delta"/.delta). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without a shell, NVM-managed binaries aren't in PATH on SSH hosts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These changes were part of the Pi provider integration but were never staged: Pi added to provider client definitions, removed from "Coming Soon", and default text generation instance updated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SSH auth on some hosts takes 8-11s, and the first HTTP response through a newly-established tunnel can exceed 1s. The 1s probe timeout caused every readiness check to fail even after the tunnel was up, and the 20s total window was too narrow for slow-auth hosts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SSH auth on some hosts takes 8-11s, and the first HTTP response through a newly-established tunnel can exceed 1s. The 1s probe timeout caused every readiness check to fail even after the tunnel was up, and the 20s total window was too narrow for slow-auth hosts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… sidebar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `pack` CLI subcommand to create versioned .tgz tarballs - Update release workflow to build and upload CLI tarballs as release assets - Add `parseReleaseRepository` to extract owner/repo from URLs or slugs - Remote t3 runners prefer GitHub Release URLs when fork repository is configured - Desktop app passes fork repository to SSH tunnel for registry-free installation
So the runtime-derived GitHub Release tarball URLs in resolveRemoteT3CliPackageSpec resolve to this fork's releases instead of upstream pingdotgg/t3code.
The previous rename commit (27f1457) updated the legacy user-data dir name and test fixtures but left the actual stage-label resolver and the DesktopAppStageLabel contract still emitting "Alpha", so the rendered app name and DesktopAppIdentity test diverged. Update the contract literal/schema, the desktop resolver, and the web branding fallback so non-dev/non-nightly builds advertise themselves as "T3 Code (Bscholar)" end-to-end.
Pi SDK fires turn_end after each internal LLM call and agent_end once at the end of the full run. Completing the t3code turn on turn_end fragmented each Pi response into multiple turns: tool activities disappeared as soon as a new sub-turn started (the work log filters by latestTurnId), and the elapsed-time counter reset on every sub-turn boundary. Making turn_end a no-op and letting agent_end drive completeTurn keeps all tool activities and text deltas under the single turnId created by sendTurn, matching how the Claude adapter behaves. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a Pi thread is continued (e.g. page reload or new server session), createAgentSession was always creating a blank session with no history. Now startSession captures the Pi session file path as resumeCursor and stores it via ProviderSessionDirectory (the same mechanism Claude uses). On subsequent startSession calls for the same thread, SessionManager.open is used to load the prior JSONL session file so Pi has full context. Falls back to a fresh session gracefully if the file is inaccessible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Tool rows showed only the tool name because item.completed had no detail or data fields — args were captured at tool_execution_start but never stored or forwarded to item.completed, which is the event the UI renders. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Support GitHub Release tarball distribution for t3 CLI
Align PiAdapter's classifyToolItemType with ClaudeAdapter so Skill, Task, MCP, and image tools get proper canonical types instead of falling through to dynamic_tool_call. Expand summarizePiToolArgs to extract skill names, prompts, and search patterns for richer detail strings in the chat timeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pshot Scan .pi/skills/, .claude/skills/ (project-local) and ~/.pi/agent/skills/ (global) during the PI provider health check. Parse skill frontmatter for name and description, map to ServerProviderSkill entries, and include them in the provider snapshot so the chat composer can offer them in the slash command picker. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… mode Replaces createAgentSession (npm SDK, in-process) with a spawned `pi --mode rpc` subprocess. Commands are sent as JSON lines to stdin (prompt, abort, get_state); AgentSessionEvent objects and RpcResponse objects are read back from stdout. Per-session lifecycle is managed by a Scope.Closeable; an outgoing Queue drives the stdin writer fiber; get_state is used after startup to capture the session file path for the resume cursor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When Pi emits extension_ui_request events over RPC stdout, route them through the T3 Code user-input system: - select / confirm → surface as user-input.requested with structured options; respondToUserInput maps answers back to extension_ui_response - input / editor → auto-cancel (no free-text equivalent in contracts) - notify / setStatus / setWidget / setTitle / set_editor_text → auto-acknowledge so Pi isn't left waiting On session stop, outstanding requests are cancelled before the process is killed so Pi doesn't deadlock. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ect and freeform
Pi's RPC fallback uses method:"input" for two cases:
- Multi-select: title encodes options as a numbered list ("Q?\n1. A\n2. B").
Parse the list, surface as multiSelect:true question; map selected labels
back to 1-based comma-separated indices for Pi's response protocol.
- Freeform: surface with options:[] so T3's auto-added Other field handles
free-text entry; answer string passed through as value directly.
editor requests are still cancelled (no useful mapping).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fire-and-forget methods (notify/setStatus/setWidget/setTitle/set_editor_text)
don't expect a response — stop sending spurious { value: "" } replies
- editor expects { value: "..." } back like input, not cancellation —
wire it through the same freeform path
- Expand PendingExtensionUI.method to include "editor"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 7 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
| const stateDir = path.join(baseDir, isDevelopment ? "dev" : "userdata"); | ||
| const userDataDirName = isDevelopment ? "t3code-dev" : "t3code"; | ||
| const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Alpha)"; | ||
| const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : "T3 Code (Bscholar)"; |
There was a problem hiding this comment.
Alpha userData not migrated
High Severity
The legacyUserDataDirName was updated from "T3 Code (Alpha)" to "T3 Code (Bscholar)". This prevents resolveUserDataPath from finding existing user data in "Alpha" directories, causing a fresh user profile to be created and prior settings, SSH config, and Electron data to be lost on upgrade.
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
| } | ||
| if (modelSelection?.model) { | ||
| spawnArgs.push("--model", modelSelection.model); | ||
| } |
There was a problem hiding this comment.
Pi launchArgs never applied
Medium Severity
The PiAdapter's startSession function omits PiSettings.launchArgs when spawning the Pi RPC process. This prevents user-configured arguments from being applied, unlike other drivers that support these settings.
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
|
|
||
| yield* context.writeCommand({ type: "prompt", message: promptText }).pipe( | ||
| Effect.catchDefect(() => completeTurn(context, "failed", "Failed to send prompt.")), | ||
| ); |
There was a problem hiding this comment.
Pi turns ignore attachments
Medium Severity
The sendTurn implementation only forwards the string input to Pi's prompt RPC, completely ignoring any attachments from ProviderSendTurnInput. This results in Pi receiving incomplete prompts when users include images or files.
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
| echo "tag=v$version" >> "$GITHUB_OUTPUT" | ||
| echo "name=T3 Code v$version" >> "$GITHUB_OUTPUT" | ||
| echo "is_prerelease=false" >> "$GITHUB_OUTPUT" | ||
| echo "make_latest=true" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
Prerelease versions marked latest
Medium Severity
The release_meta step now always sets is_prerelease to false and make_latest to true. This means any version provided with a pre-release suffix (e.g., 1.2.3-beta.1) will be incorrectly published and promoted as the latest stable GitHub release.
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
| homePath: "", | ||
| customModels: [], | ||
| launchArgs: "", | ||
| }, |
There was a problem hiding this comment.
Tests enable Pi probing
Low Severity
Several registry tests disable Codex, Claude, Cursor, and OpenCode probes to avoid spawning on CI, but set piAgent.enabled: true, which still runs the Pi --version health check. That breaks the stated isolation and can add flaky PATH-dependent behavior to Codex-focused cases.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
|
|
||
| if (context.turnState) { | ||
| yield* completeTurn(context, "completed"); | ||
| } |
There was a problem hiding this comment.
Inflight Pi turn marked completed
Medium Severity
If sendTurn is called while a Pi turn is still active, the adapter calls completeTurn with state completed before sending the new prompt. The Pi agent may still be running the previous work, so the UI can close a turn prematurely while RPC work continues.
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
| release-assets/*.exe | ||
| release-assets/*.blockmap | ||
| release-assets/*.yml | ||
| release-assets/*.tgz |
There was a problem hiding this comment.
Mac updater blockmaps not published
Medium Severity
The release job still collects *.blockmap files into artifacts but the GitHub release upload lists no longer include release-assets/*.blockmap. Mac builds that rely on electron-updater blockmaps may fail or fall back when updating from the published release.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 9678427. Configure here.
| const stateResponse = yield* Deferred.await(stateDeferred).pipe( | ||
| Effect.timeoutOption(5000), | ||
| ); |
There was a problem hiding this comment.
🟢 Low Layers/PiAdapter.ts:838
The pendingRequests entry for stateReqId is never cleaned up when Deferred.await times out at lines 838-840. If the 5-second timeout fires, the map entry remains orphaned until the session closes, leaking one entry per timed-out session startup. Consider deleting the entry on timeout using Effect.tap or Effect.ensuring.
const stateResponse = yield* Deferred.await(stateDeferred).pipe(
Effect.timeoutOption(5000),
+ Effect.tap(() => Effect.sync(() => pendingRequests.delete(stateReqId))),
);🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/PiAdapter.ts around lines 838-840:
The `pendingRequests` entry for `stateReqId` is never cleaned up when `Deferred.await` times out at lines 838-840. If the 5-second timeout fires, the map entry remains orphaned until the session closes, leaking one entry per timed-out session startup. Consider deleting the entry on timeout using `Effect.tap` or `Effect.ensuring`.
Evidence trail:
apps/server/src/provider/Layers/PiAdapter.ts line 835: `pendingRequests.set(stateReqId, stateDeferred)` — entry added. Lines 838-840: `Deferred.await(stateDeferred).pipe(Effect.timeoutOption(5000))` — timeout does not delete entry. Line 598: `context.pendingRequests.delete(msg['id'])` — only deletion path, requires a response to arrive. Lines 613-665 (`stopSessionInternal`): `pendingExtensionUIs` explicitly cleaned at 625-631 with `.clear()`, but no cleanup for `pendingRequests` at all. Line 664: `sessions.delete(context.session.threadId)` removes context reference (GC-eligible), but entry persists in map until that point.
| with: | ||
| node-version-file: package.json | ||
| - name: Build t3 server package | ||
| run: bun run build --filter=t3 |
There was a problem hiding this comment.
🟠 High workflows/release.yml:191
bun run build --filter=t3 passes --filter=t3 to the build script instead of interpreting it as a workspace filter. Bun requires top-level flags before subcommands, so the filter is ignored and the build may fail or run against the wrong packages. Use bun --filter=t3 run build to ensure the filter is applied.
| run: bun run build --filter=t3 | |
| - name: Build t3 server package | |
| run: bun --filter=t3 run build |
🤖 Copy this AI Prompt to have your agent fix this:
In file .github/workflows/release.yml around line 191:
`bun run build --filter=t3` passes `--filter=t3` to the build script instead of interpreting it as a workspace filter. Bun requires top-level flags before subcommands, so the filter is ignored and the build may fail or run against the wrong packages. Use `bun --filter=t3 run build` to ensure the filter is applied.
| echo "Invalid release version: $raw" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
There was a problem hiding this comment.
🟠 High workflows/release.yml:59
The version validation regex on line 55 accepts pre-release suffixes like -beta.1, but lines 64-65 unconditionally set is_prerelease=false and make_latest=true. When a user dispatches the workflow with version 1.2.3-beta.1, the GitHub Release is published as the latest stable release, directing all users to a pre-release build instead of respecting the pre-release semantics.
🤖 Copy this AI Prompt to have your agent fix this:
In file .github/workflows/release.yml around line 59:
The version validation regex on line 55 accepts pre-release suffixes like `-beta.1`, but lines 64-65 unconditionally set `is_prerelease=false` and `make_latest=true`. When a user dispatches the workflow with version `1.2.3-beta.1`, the GitHub Release is published as the latest stable release, directing all users to a pre-release build instead of respecting the pre-release semantics.
| rel="stylesheet" | ||
| /> | ||
| <title>T3 Code (Alpha)</title> | ||
| <title>T3 Code (Bscholar)</title> |
There was a problem hiding this comment.
🟠 High web/index.html:92
The <title> was changed to T3 Code (Bscholar), where "Bscholar" appears to be the PR author's username rather than an intentional product stage name. This displays a personal identifier to all users in browser tabs, bookmarks, and search results. Consider reverting to a stage name like (Alpha) or confirming if this change is intentional.
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/web/index.html around line 92:
The `<title>` was changed to `T3 Code (Bscholar)`, where "Bscholar" appears to be the PR author's username rather than an intentional product stage name. This displays a personal identifier to all users in browser tabs, bookmarks, and search results. Consider reverting to a stage name like `(Alpha)` or confirming if this change is intentional.
There was a problem hiding this comment.
🟡 Medium
t3code/packages/contracts/src/settings.ts
Line 505 in 9678427
Adding piAgent to ServerSettings.providers at line 416 without a corresponding entry in ServerSettingsPatch.providers breaks the patch API for Pi settings. When users attempt to modify Pi settings, the request will fail validation because piAgent is missing from the patch struct at lines 505-511. Every other provider has a matching patch schema, so piAgent should have one too.
🤖 Copy this AI Prompt to have your agent fix this:
In file packages/contracts/src/settings.ts around line 505:
Adding `piAgent` to `ServerSettings.providers` at line 416 without a corresponding entry in `ServerSettingsPatch.providers` breaks the patch API for Pi settings. When users attempt to modify Pi settings, the request will fail validation because `piAgent` is missing from the patch struct at lines 505-511. Every other provider has a matching patch schema, so `piAgent` should have one too.
Evidence trail:
packages/contracts/src/settings.ts lines 411-417 (ServerSettings.providers includes piAgent), lines 505-511 (ServerSettingsPatch.providers omits piAgent), lines 461-490 (CodexSettingsPatch, ClaudeSettingsPatch, CursorSettingsPatch, OpenCodeSettingsPatch all defined, no PiSettingsPatch). PI_PLAN.md line 110 and 125 mention PiSettingsPatch only in a planning doc.
ApprovabilityVerdict: Needs human review 4 blocking correctness issues found. This PR introduces a new Pi Agent provider integration alongside major CI/CD workflow changes, branding modifications, and default behavior changes. Multiple high-severity unresolved comments identify potential data loss, broken release semantics, and questionable branding changes that require human assessment. You can customize Macroscope's approvability policy. Learn more. |
Release workflow: - Detect pre-release versions (containing '-') and avoid promoting them as the latest stable GitHub release. Feeds through a release_channel output to resolve-previous-release-tag.ts. - Use 'bun --filter=t3 run build' so the workspace filter actually applies (bun requires top-level flags before subcommands). - Include release-assets/*.blockmap in the published files so electron-updater delta updates keep working on macOS. Pi provider: - Apply PiSettings.launchArgs to the spawned 'pi --mode rpc' process, matching the ClaudeAdapter parseCliArgs pattern. - Forward ProviderSendTurnInput.attachments to Pi's prompt RPC as inline base64 ImageContent so image turns no longer drop the attached files. - When sendTurn is called while a turn is still active, send an abort to Pi first and mark the prior turn as 'interrupted' (not 'completed') so the UI doesn't claim a settled result. - Clean up the get_state pendingRequests entry on timeout via Effect.ensuring to prevent per-startup leaks. Contracts: - Add PiSettingsPatch and wire it into ServerSettingsPatch.providers so the patch API actually accepts Pi settings updates. Tests: - Disable piAgent in the two Codex-focused ProviderRegistry tests that were spawning a real 'pi --version' probe and breaking the stated CI isolation.


What Changed
Why
UI Changes
Checklist
Note
High Risk
Large new provider adapter and release-pipeline changes affect core agent execution and how CLI/desktop are shipped; remote install now depends on GitHub release assets.
Overview
Adds a first-class Pi Agent provider that runs the
piCLI in RPC mode and maps its JSONL events (turns, tools, streaming text, extension UI prompts) into T3’s provider runtime, plus settings/UI wiring and structured text-generation via Pi.Fork-oriented release and distribution: CI/release runners move to GitHub-hosted images; the release workflow is trimmed to manual stable releases (no tag push, nightly cron, npm publish, Vercel deploy, Discord, or Windows/signing paths). Releases build macOS/Linux desktop artifacts and attach a
t3CLI.tgzfrom a newcli packcommand instead of publishing to npm. Desktop remote SSH installst3from GitHub Release tarball URLs whenrepository.urlpoints at the fork.Product defaults and branding: Stage label Alpha → Bscholar across desktop/web; default auto-bootstrap and text-generation provider shift from Codex to Claude. Telemetry defaults to off. Composer/model UI keys trait options by provider instance id and limits the model picker sidebar to ready instances.
Some server tests around OTLP proxying and live CLI-over-running-server are removed or narrowed.
Reviewed by Cursor Bugbot for commit 9678427. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add Pi Agent provider with RPC adapter, text generation, and settings integration
piAgentprovider driver (PiDriver.ts, PiAdapter.ts) that manages Pi Agent sessions, turns, and UI interactions via the@earendil-works/pi-coding-agentpackage.PiTextGeneration(PiTextGeneration.ts) that spawns the Pi CLI with a 180s timeout, collects JSON output, and validates it against a schema.piAgentin contracts (model.ts, settings.ts), provider registry, and UI metadata, with default modelsclaude-sonnet-4-6andclaude-haiku-4-5.claudeAgent/claude-haiku-4-5).AlphatoBscholaracross desktop, web, and contracts.📊 Macroscope summarized 9678427. 35 files reviewed, 6 issues evaluated, 0 issues filtered, 5 comments posted
🗂️ Filtered Issues