Skip to content

fix(ai-providers): read the system prompt and tools from the Pi 0.86+ transcript - #620

Open
JS-banana wants to merge 1 commit into
openpi-dev:mainfrom
JS-banana:fix/antigravity-transcript-context
Open

JS-banana wants to merge 1 commit into
openpi-dev:mainfrom
JS-banana:fix/antigravity-transcript-context

Conversation

@JS-banana

Copy link
Copy Markdown
Contributor

Problem

Closes #619.

Pi 0.86.0 changed a custom provider's stream input from Context to a normalized TranscriptContext: the system prompt and tool declarations are folded into a leading transcript system message, and later system messages carry prompt/tool deltas. The Antigravity adapter still read context.systemPrompt / context.tools, so that leading system message fell through to the tool-result branch and became a functionResponse with no name:

GenerateContentRequest.contents[0].parts[0].function_response.name: Name cannot be empty.

Every request on Pi 0.86+ failed, and independently of the 400 the system prompt and all tool declarations were dropped. Cursor read the same two fields and silently fell back to its generic chat-only prompt with no tools.

Value

google-antigravity works again on Pi 0.86+ while the declared Pi 0.85.1 baseline keeps working unchanged, and the failure class is closed at the shared cause rather than per provider.

Approach

  • New extensions/ai-providers/transcript.ts: replays transcript system messages into the prompt and tool set currently in effect, mirroring Pi's getCurrentSystemPrompt / getCurrentTools without importing helpers that do not exist before Pi 0.86. applyTranscript returns the old Context untouched, so Pi <= 0.85.1 behavior is preserved exactly.
  • Antigravity buildRequestBody resolves the transcript once; convertMessages receives conversation messages only; buildTools takes the resolved tool list.
  • Google converter: only assistant turns are rewritten, and non-conversation roles or nameless tool results no longer become a functionResponse.
  • Cursor: buildCursorRequest / streamCursor resolve the transcript at the entry point.
  • No dependency, lockfile, or peer-range change — this lands independently of the baseline discussion in chore(deps): define and enforce the supported Pi baseline #328.

Validation

  • Differential against Pi 0.87.1's own replay (getCurrentSystemPrompt / getCurrentTools) across five message sets — leading system message; later section patch with toolsRemoved / toolsAdded; content plus sections; two system messages; no system message. Prompt text and tool set are identical in all five.
  • tests/extensions/ai-providers/antigravity.test.ts 58/58 and cursor.test.ts 35/35. The four new regression tests fail when the provider changes are stashed, so they pin the regression rather than the implementation.
  • Real account on Pi 0.87.1, extension loaded with -ne -ns -np -nc --no-session -e extensions/ai-providers/index.ts:
  • node --test: 1888 passed / 0 failed / 5 skipped. bun run lint, format:check, check:config-contract, check:docs-contract, check:discipline pass. tsc --noEmit reports 20 errors, identical to the baseline on this checkout (missing node-pty / @xterm/*): 0 new.

Impact

… transcript

Pi 0.86.0 changed a custom provider's stream input from `Context` to a normalized
`TranscriptContext`: the system prompt and tool declarations are folded into a
leading transcript system message. The Antigravity adapter still read
`context.systemPrompt` / `context.tools`, so that system message fell through to
the tool-result branch and became a `functionResponse` with no name — Cloud Code
Assist rejects it as `contents[0].parts[0]`, failing every request. Even without
that 400, the system prompt and all tool declarations were silently dropped.

Add a shared transcript resolver that replays system messages into the prompt and
tool set currently in effect, mirroring Pi's `getCurrentSystemPrompt` /
`getCurrentTools` without importing helpers that do not exist before Pi 0.86, and
keep the `Context` shape working unchanged. The Google converter now skips
non-conversation roles and nameless tool results instead of emitting an invalid
`functionResponse`.

Cursor read the same two fields and silently fell back to its generic chat-only
prompt with no tools; it resolves the transcript as well.

Verified:
- differential: resolveTranscript matches Pi 0.87.1 getCurrentSystemPrompt /
  getCurrentTools across five message sets
- antigravity 58/58 and cursor 35/35; the new regression tests fail without the fix
- real google-antigravity turns on gemini-3.8-flash-high and claude-sonnet-4-6:
  plain reply plus a read-tool round trip
- node tests 1888 passed / 0 failed; typecheck has no new errors (the 20
  pre-existing ones come from node-pty/@XTerm not being installed here)
@JS-banana

Copy link
Copy Markdown
Contributor Author

CI note: Background terminals (Windows) failed on streamAntigravity bounds the full SSE lifetime after the first event.

That test is pre-existing and sets timeoutMs: 5, so the first SSE read has a 5 ms budget; on the Windows runner it lost that race and reported the first-event timeout (Timed out waiting for the first SSE event) instead of the lifetime timeout (next SSE event). It passed on Node 22.19.0 / 24 / 26 and in Web E2E in this same run, and this branch does not touch the SSE timing or failover path.

The Windows job is currently failing on other branches for unrelated tests — docs-only #618 failed it on not ok 215, and feat/session-child-admission failed it on not ok 804 — so this reads as the existing Windows flake rather than a regression from this change. I cannot re-run the job myself (no admin rights on this repository); a re-run should clear it.

This branch has not been deployed

No deployments
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.

bug(antigravity): Pi 0.86+ 下 provider stream 收到 TranscriptContext,每次请求都以 400 失败

1 participant