Long local-LLM SSE streams (e.g. vLLM buffering large tool calls) exceeded
undici's default 300s bodyTimeout and aborted with UND_ERR_BODY_TIMEOUT.
retry.provider.timeoutMs cannot lift this cap because it controls the
provider SDK's AbortController deadline, not undici's per-socket idle timer.
Pass { bodyTimeout: 0, headersTimeout: 0 } to EnvHttpProxyAgent. Provider
SDKs still enforce their own timeouts via retry.provider.timeoutMs.
closes#3715
Bun compiled binaries have an empty process.env when running inside
sandbox environments (e.g. nono on Linux/macOS). This broke API key
detection and model discovery because all process.env.* lookups returned
undefined.
- Add restoreSandboxEnv() helper that reads /proc/self/environ when Bun
is detected and process.env is empty, populating process.env before
any other code runs (coding-agent/src/bun/cli.ts entry point)
- Add getProcEnv() fallback in env-api-keys.ts for direct @mariozechner/pi-ai
consumers that may not go through the coding-agent entry point
- Add unit tests for restoreSandboxEnv
Bun bug oven-sh/bun#27802 causes process.env to be empty when running inside a Linux sandbox (Landlock/seccomp).
This breaks npm resolution because PATH is missing, crashing pi on startup with:
Failed to run npm root -g: Executable not found in $PATH: "npm"
Add getSandboxEnv() to read /proc/self/environ as a fallback on Linux when process.env has zero keys.
Pass this env to all spawn calls in package-manager.ts.
Also fix runCommandSync error handling: check result.error in addition to result.status, and include result.error?.message in the error text so spawn failures (e.g. ENOENT) produce useful diagnostics instead of "null".
Fixes#3806
Previously, registerProvider() overwrote the stored config for a provider,
so a models registration followed by a baseUrl/headers-only override lost
the models. After refresh(), only the override-only config was replayed,
causing extension-provided models to disappear.
Added upsertRegisteredProvider() that merges defined fields into the
existing stored config instead of replacing it. Fields absent from the
incoming config are preserved from the stored config.
Also adds regression tests for dynamic provider override persistence
across refresh.
Strip `index.ts` and `index.js` from extension display paths in both
compact and expanded views of the startup banner. This makes the list more
readable by removing redundant filename noise.
Changes:
- Add `formatExtensionDisplayPath()` helper to centralize path formatting
- Update `getCompactExtensionLabels()` to strip index files from segments
- Update expanded view formatting to use the new helper
- Add comprehensive tests for both views
Fixes#3549Fixes#3559
Custom footers registered via ctx.ui.setFooter(...) whose render()
closure touched ctx would crash pi on /quit: runtimeHost.dispose()
invalidates the extension runner before the TUI stops rendering, so
any still-attached footer render tick hit assertActive() and threw.
Call resetExtensionUI() before runtimeHost.dispose() in the interactive
shutdown path, matching the rebindCurrentSession() pattern. This
disposes the custom footer (clearing its interval) and detaches it
from the TUI tree before the runner goes stale.
closes#3595
Co-authored-by: Mario Zechner <badlogicgames@gmail.com>
Adds terminal.showTerminalProgress setting (default false) and wires it
to /settings. Guards all OSC 9;4 setProgress calls in interactive-mode.
closes#3588
Swaps hardcoded "pi" / ".pi" / "π" for the existing APP_NAME and
CONFIG_DIR_NAME extension points at four sites the original APP_NAME
rollout missed:
- /quit slash-command description
- process.title (cli.ts and bun/cli.ts)
- project-local extensions dir in loader.ts
For the terminal title, adds an APP_TITLE export in config.ts that
keeps pi's "π" glyph when piConfig.name is unset and falls back to
APP_NAME otherwise. Presence of piConfig.name acts as the "has been
rebranded" signal, so pi's own package.json drops the redundant
"name": "pi" (the existing `|| "pi"` fallback in config.ts makes this
a no-op for APP_NAME).
No behavior change for pi itself: process.title, /quit description,
terminal title prefix, and project-local extensions dir all resolve
to the same values as before.
closes#3476
Replace transient pendingWorkingMessage with a persistent workingMessage
field on InteractiveMode, matching the workingIndicatorOptions pattern.
New loaders now use this.workingMessage || this.defaultWorkingMessage
instead of always falling back to the default.
Also add working-message-test.ts extension example.
closes#3566
* fix(typebox): migrate to v1 with extension compat
Replace AJV-based validation with TypeBox-native validation, keep legacy extension imports working (including @sinclair/typebox/compiler), and restore coercion for serialized/plain JSON schemas.
This change closes#3112.
* fix(typebox): use canonical imports and harden coercion
Switch first-party code to canonical typebox imports while retaining legacy extension aliases in the loader.
Remove obsolete runtime codegen guards, expand serialized JSON-schema coercion coverage, and update related tests and fixtures.
Fixes#3112.
---------
Co-authored-by: Mario Zechner <badlogicgames@gmail.com>