Back to the course home

Chapter 37

Upgrade to Pi 0.83

Learn the new capabilities, TypeBox breaking changes, and a verifiable migration path.

Source baseline: Pi v0.83.0 · commit 845d6ff

Pi 0.83.0 is more than a version-label update. It adds credential export, richer extension context, and more precise streaming state; upgrades TypeBox; and fixes integration issues around RPC, session switching, and resource reloads.

This chapter uses the official v0.83.0 tag (commit 845d6ff) as its baseline. It answers three practical questions: What can you use now? What can break? How do you prove the upgrade is safe?

1. The upgrade map

Area Change in 0.83 Desktop-agent impact
Authentication pi auth print-api-key and print-bearer-token External processes can obtain the current credential in a controlled way
Extension API New ctx.scopedModels Extensions can see the models allowed in the current scope
Streaming New pending stop reason A partial message is no longer mistaken for a completed response
Providers Raw stop reasons are preserved; unknown terminal reasons become errors Logs and UI expose the upstream outcome more faithfully
TypeBox Upgraded to 1.3.7 and deprecated APIs removed Extensions using old APIs must migrate before they compile
OAuth Credentials refresh when less than five minutes remain Long-running tasks are less likely to fail mid-turn
RPC and sessions Fixes for bash events, session replacement, navigation, and persistence Some host-side workarounds can be retired after regression tests pass

If you jump directly from 0.82.0, include the 0.82.1 additions in your review: Claude Opus 5 support, ANTHROPIC_AUTH_TOKEN, outputPad for custom message renderers, and model-catalog cache improvements.

2. Export credentials for an external process

The new commands are useful when a desktop main process, script, or plugin host needs to reuse Pi's authenticated session:

pi auth print-api-key anthropic
pi auth print-bearer-token openai

OAuth credentials are refreshed when fewer than five minutes remain. Treat stdout as a secret: pass it through memory or a child-process environment, never through logs, analytics, crash reports, or renderer state.

const token = (await runPi([
  "auth",
  "print-bearer-token",
  provider,
])).stdout.trim();

spawn(workerPath, [], {
  env: { ...safeBaseEnv, PROVIDER_TOKEN: token },
  stdio: ["ignore", "pipe", "pipe"],
});

OpenRouter login also supports headless environments by accepting a pasted redirect URL or authorization code.

3. TypeBox 1.3.7 is the explicit breaking change

Pi 0.83 removes these deprecated APIs:

Type.Base           Type.Awaited       Type.Promise
Type.AsyncIterator  Type.Iterator      Type.Options
Value.Mutate

Scan extension code before upgrading:

rg 'Type\.(Base|Awaited|Promise|AsyncIterator|Iterator|Options)|Value\.Mutate' .

Do not hide the errors with casts. Replace each use with current TypeBox schema constructors that describe the actual data. If code relied on Value.Mutate, prefer an explicit new value; event-chain behavior becomes easier to reason about and test.

The release also fixes compiled validation for nullable arrays. Test both interpreted and compiled validation when a tool argument accepts “array or null,” rather than preserving the old behavior in a snapshot.

4. Model pending as a real streaming state

A partial streaming message can now carry pending. It means that the snapshot has no terminal outcome yet—not success and not failure.

switch (message.stopReason) {
  case "stop":
  case "length":
    markComplete(message);
    break;
  case "pending":
    keepStreaming(message);
    break;
  case "error":
    showFailure(message);
    break;
}

Do not show a completion badge, enable retry, or persist final usage while the state is pending. Pi 0.83 also preserves raw provider stop reasons. An unmapped terminal reason becomes a provider error, so retain the provider name and original reason in diagnostics.

5. Use ctx.scopedModels in extensions

ctx.scopedModels describes the models available inside the current scope. Model pickers, cost policies, and approval extensions should use it instead of rebuilding a global catalog.

pi.on("session_start", async (_event, ctx) => {
  audit("models available in this session", ctx.scopedModels);
});

This prevents an extension from recommending a globally known model that is unavailable in the current workspace or session. Per-request fetch injection is now inherited as well; verify that custom proxies, tracing, and test doubles still flow through nested calls.

6. Revisit old workarounds—with tests first

Pi 0.83 fixes several integration boundaries:

  • Direct RPC bash commands now pass through the user_bash event, unifying approval and audit hooks.
  • Session replacement and tree navigation abort active streams first and persist the turn being left.
  • Resource metadata survives extension reloads.
  • Concurrent user-bash cancellation and duplicate startup messages after a session switch are fixed.
  • llama.cpp usage accounting and partial directories from failed git packages are fixed.

Do not delete host-side protection immediately. First encode the old failure as a regression test, verify it against 0.83, then remove only the redundant workaround. Authorization, timeouts, and process isolation remain host responsibilities.

7. A reversible upgrade path

npm install @earendil-works/pi-ai@0.83.0 \
  @earendil-works/pi-agent-core@0.83.0 \
  @earendil-works/pi-coding-agent@0.83.0
npm test
npm run build

Verify in this order:

  1. Preserve a known-good lockfile and build artifact.
  2. Scan for and migrate removed TypeBox APIs.
  3. Test text streaming, thinking, tool calls, cancellation, and pending in a minimal session.
  4. Test API-key and OAuth flows; prove secrets never enter logs.
  5. Run a bash approval test through both SDK and RPC integration.
  6. Switch sessions during output, restore, and inspect the session tree.
  7. Reload extensions and verify Skill, prompt, and resource metadata.
  8. Roll out gradually while monitoring provider errors, cancellation, and restore failures.

8. Summary

Pi 0.83 makes system boundaries more explicit: authentication can be handed to external processes under control, model scope is available to extensions, streaming distinguishes waiting from completion, and provider failures retain more evidence. The cost is a required migration away from deprecated TypeBox APIs. The safest upgrade turns every relevant changelog item into a reproducible integration test.

Exercises

  1. Add pending to your UI state machine and prove it cannot trigger completion.
  2. Scan an Extension project for removed TypeBox APIs and write a migration checklist.
  3. Add an approval-event test for direct RPC bash and assert that user_bash is emitted.
  4. Simulate an OAuth token with four minutes remaining and verify refresh on credential export.
  5. Switch sessions during streaming and verify that the abandoned turn restores without duplicate messages.

Primary sources