Skip to content

fix(pi): read the prompt and tools from Pi 0.86's transcript context - #245

Open
KhangHLe wants to merge 1 commit into
cortexkit:mainfrom
KhangHLe:fix/pi-0.86-transcript-context
Open

KhangHLe wants to merge 1 commit into
cortexkit:mainfrom
KhangHLe:fix/pi-0.86-transcript-context

Conversation

@KhangHLe

@KhangHLe KhangHLe commented Sep 20, 2026

Copy link
Copy Markdown

Closes #244.

What breaks

Pi 0.86 hands streamSimple a normalized TranscriptContext{ messages } only. The host system prompt and the tool declarations are folded into a leading role: "system" message (later system messages can carry toolsAdded / toolsRemoved / sections deltas) and are meant to be read back with pi-ai's getCurrentSystemPrompt / getCurrentTools (docs, v0.86.0).

buildAnthropicRequest (convert.ts) and the tool_use name un-mapping (stream.ts) read context.systemPrompt / context.tools. On that shape both are undefined, so every request on Pi ≥ 0.86 goes out with the billing header + Claude Code identity as the whole system prompt and no tools. HTTP 200 and a fluent reply from a bare model that says it has no bash tool — nothing errors.

What this does

resolveRequestContext(context, helpers = piAi) in convert.ts resolves { systemPrompt, tools, messages } from either shape:

  • no role: "system" message → the raw Context fields, unchanged (Pi < 0.86 behaves exactly as before);
  • system messages present and pi-ai exports the transcript helpers → collapseSystemMessages / getCurrentSystemPrompt / getCurrentTools (the same functions Pi's built-in providers use, so sections and tool removals resolve identically);
  • system messages present, no helpers → a minimal local replay (text joined in order, toolsRemoved then toolsAdded) rather than dropping the prompt.

The helpers are reached through the namespace import and a typeof check, not named imports, so the extension keeps loading on a pi-ai that lacks them. Note that Pi's extension loader aliases @earendil-works/pi-ai to the host's bundled copy, so which branch runs is decided by the running Pi, not by this repo's lockfile (currently pi-ai 0.85.1) — which is why the tests pin both branches with explicit helper sets instead of relying on whatever bun install resolved.

buildAnthropicRequest and streamCortexKitAnthropic (plus the two internal option types that carry context) accept Context | RequestContext. convertMessages already skips the system role, so nothing else changes on the wire.

Verification

  • bun test src/tests: 120 pass (114 before + 6 new: raw Context unchanged; prompt + tools from the leading system message; later system messages replayed; helper branch used when present, with call order asserted; local replay when absent; buildAnthropicRequest end-to-end from a transcript context).
  • Mutations: bypassing resolveRequestContext in buildAnthropicRequest → the end-to-end test fails; forcing the helper check to false → the helper-branch test fails.
  • bun run typecheck clean on the lockfile's pi-ai 0.85.1; biome check clean.
  • Also ran the suite in a scratch install with pi-ai 0.86.1 (helpers present): 120 pass, and breaking the local replay there changes nothing, i.e. the helper branch is the one running.
  • Live: the same fix in a vendored copy, on pi 0.86.1 — pi -p --no-extensions -e <ext> --model anthropic-personal/claude-haiku-4-5 --tools bash --system-prompt "Your name is Wren…" "State your name, then run … with the bash tool" goes from "I'm Claude … I don't actually have access to a bash tool" to running the command and answering "Wren".

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Restores Pi ≥ 0.86 compatibility by reading the host system prompt and tools from its normalized transcript before building Anthropic requests. Previously those values came from missing Context fields, so requests omitted the host prompt and tools; tool-call name mapping now uses the same resolved tool set. Closes #244.

  • Uses @earendil-works/pi-ai transcript helpers when available, with a local replay fallback for hosts without them.
  • Preserves raw Context behavior for Pi < 0.86 and removes transcript system messages before message conversion.
  • Adds coverage for legacy contexts, transcript updates, helper selection, fallback replay, and end-to-end request conversion.

Written for commit 5b87538. Summary will update on new commits.

Review in cubic

RetriggerConfidence Score: 5/5

The PR appears safe to merge, with no outstanding new issues introduced since the previous review.

Summary

This PR restores Pi 0.86+ compatibility by resolving system prompts and tool declarations from normalized transcript system messages while preserving the legacy raw-context path.

  • Reconstructs the current prompt and tools with host-provided pi-ai transcript helpers when available.
  • Provides a compatibility fallback for hosts without those helpers.
  • Uses the resolved tools when mapping streamed tool-call names.
  • Adds coverage for legacy contexts, normalized transcripts, system-message deltas, helper selection, fallback replay, and request construction.
  • No changes were made after the previous review.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Pi request context] --> B{Contains system messages?}
  B -- No --> C[Use legacy systemPrompt and tools fields]
  B -- Yes --> D{Transcript helpers available?}
  D -- Yes --> E[Collapse system messages]
  E --> F[Resolve current prompt and tools]
  D -- No --> G[Replay prompt and tool deltas locally]
  C --> H[Build Anthropic request]
  F --> H
  G --> H
  F --> I[Map streamed tool-call names]
  G --> I
Loading

Reviews (2) · Last reviewed commit: "fix(pi): read the prompt and tools from ..."

Pi 0.86 hands `streamSimple` a normalized `TranscriptContext` — `{ messages }`
only. The host system prompt and the tool declarations are folded into a
leading `role: "system"` message (later system messages may carry
`toolsAdded` / `toolsRemoved` / `sections` deltas) and are meant to be read
back with pi-ai's `getCurrentSystemPrompt` / `getCurrentTools`
(packages/coding-agent/docs/custom-provider.md § Custom Streaming API).

`buildAnthropicRequest` and the `tool_use` name un-mapping in `stream.ts`
read `context.systemPrompt` / `context.tools`, which are `undefined` on that
shape, so every request on Pi >= 0.86 went out with the billing header and
the Claude Code identity as the whole system prompt and no tools. HTTP 200,
a fluent reply from a bare model that says it has no bash tool.

`resolveRequestContext()` resolves the prompt, tools, and message list from
either shape: raw `Context` fields when the transcript has no system message
(Pi < 0.86, unchanged); pi-ai's transcript helpers when it does. The helpers
are reached through the namespace import and a `typeof` check rather than
named imports, so the extension still loads on a pi-ai that lacks them; if
system messages are present without the helpers (only reachable on an older
pi-ai, e.g. this repo's lockfile) a minimal local replay keeps the prompt and
tools instead of dropping them.

Tests: raw Context unchanged; prompt + tools read from the leading system
message; later system messages replayed (appended text, tool add/remove);
buildAnthropicRequest sends both from a transcript context. Verified green
against the lockfile's pi-ai 0.85.1 (local replay branch) and against
pi-ai 0.86.1 in a scratch install (helper branch — breaking the local replay
there changes nothing, so the helpers ran).

Closes cortexkit#244
@KhangHLe
KhangHLe force-pushed the fix/pi-0.86-transcript-context branch from ef0f649 to 5b87538 Compare September 20, 2026 17:22
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.

pi 0.86: streamSimple gets a TranscriptContext — system prompt and tools are never sent

1 participant