Prefer function payloads when malformed OpenAI-compatible tool-call deltas also include an empty custom object, preserving parsed arguments.
Fixes#7160
Co-authored-by: Sunny Young <isunnyyoung@gmail.com>
Adds AssistantMessage.rawStopReason and preserves provider original stop reasons.
Improves certain cases of error messages.
For mistral, change unmapped finish reasons to be "error" instead of "stop".
fixes#7255
Restructured rewrite: concepts (session = tree + lanes + per-lane
operation logs + global facts), records and crash-site traces, recovery
as reduction, then API and implementation top-down. Renames refs to
lanes, generations to tasks; retires leaf entries, de-trees labels and
name, adds deferred provider requests (stopReason deferred, optional
fetchDeferred/cancelDeferred on ProviderStreams, deliberate lane
suspension), SQLite branch cache with tips and reverse lookup, Unix ms
timestamps. Supersedes harness.md.
Running from a linked worktree nested under its main repo loaded the
project's AGENTS.md/CLAUDE.md twice. The ancestor walk climbs from cwd to
the filesystem root, so it picked up the worktree's copy and then the main
repo's copy above it, even though both are the same tracked file.
The walk now drops that one duplicate: the main repo root's copy is skipped
when the worktree root has a context file of the same name. Nothing else
changes. The worktree still inherits the main repo's copy when it has none,
directories above the main repo keep loading, and ordinary repos, sibling
worktrees, submodules and bare layouts are untouched.
Bump TypeBox to 1.3.7 so compiled validators guard array-specific keywords when null is accepted, and cover tool-argument validation with a regression test.
Breaking for extensions: TypeBox 1.3 removes deprecated APIs including Type.Base, Type.Awaited, Type.Promise, Type.AsyncIterator, Type.Iterator, Type.Options, and Value.Mutate. Extensions using these through Pi's bundled TypeBox aliases must migrate to supported TypeBox APIs.
Fixes#7003
Image.render() previously emitted the imageFallback line without
truncateToWidth. Long absolute paths (common when callers pass a
filename) exceeded the terminal width and crashed the TUI.
- imageFallback: home-shorten paths to ~/...; OSC 8 file:// link when
hyperlinks are available and the path is absolute
- Image.render: truncate fallback lines to the render width
- tests for shorten, hyperlink, basename, and width clamp
Co-authored-by: Moritz Hofmann <Moritz.Hofmann@aimentum.de>
llama-server supports OpenAI-spec stream_options.include_usage since
b2b6b55a59 (Sep 2025); any server new enough for router mode sends the
final usage chunk when requested. With the flag hardcoded to false the
provider never requests it, so streamed responses record zero tokens
and session/context accounting stays empty.
Verified against llama-server b10094: with include_usage sent, the
final chunk carries usage (incl. prompt_tokens_details.cached_tokens);
without it, nothing.
Refs: named movable leaf pointers, one active operation per ref, parallel
under one writer. AgentRef surface implemented by AgentHarness for main;
per-ref queues, persisted config, session views, scoped watch() plus
watchSession(). Per-ref recovery and operational-log partitioning (no
parenting; log order is causal order within a ref; session entries carry
no ref, membership derives from parent linkage). Labels and session name
de-treed to global latest-wins records with setter APIs. Named
append-only context invariant (KV cache rationale for deferred writes).
New traces, API examples, and open questions for review.
* fix(coding-agent): guard tree navigation during responses
* fix(coding-agent): simplify tree navigation regression
* fix(coding-agent): abort active run on session replacement and tree navigation
Session replacement (resume, new, fork, import) now settles the active
response in teardownCurrent before disposing the outgoing session, so the
aborted turn and its tool results persist instead of leaving a dangling
tool call. Tree navigation aborts the active response directly once the
user commits, without a confirmation gate.
* fix(ai): send max_tokens for Z.AI providers
Z.AI (zai, zai-coding-cn, api.z.ai, open.bigmodel.cn) ignores
max_completion_tokens and only honors max_tokens, so the configured
output cap never applied and Z.AI fell back to its 65536 default.
Add isZai to detectCompat's useMaxTokens condition.
* fix(ai): set zai max_tokens compat in model generator
Per review feedback: the model generator now maps Z.AI to max_tokens, so
regenerated Z.AI model entries carry compat.maxTokensField="max_tokens"
(Z.AI ignores max_completion_tokens). The runtime detectCompat change is
kept as a fallback for custom/self-hosted Z.AI base URLs not in the catalog,
matching how moonshot/together/nvidia are handled in both places.
Typing a model name in the /model selector only clamped selectedIndex to
the filtered list length, leaving the highlight wherever it happened to
be instead of moving to the best match. Reset selectedIndex to 0 when a
non-empty query is present so the top row is highlighted in both the All
and Scoped tabs. Keep the clamp behavior when the query is cleared.
fixes#7209
* fix(coding-agent): isolate autoload-disabled package test from real home directory
The test asserted result.skills is exactly empty, but resolve() also
scans ~/.agents/skills via getHomeDir(). On machines where that
directory is populated the test failed; CI runners have no
~/.agents/skills, so it always passed there. Stub HOME to the temp
dir with the same save/restore pattern used by neighbouring tests.
* fix(coding-agent): use vi.stubEnv for home directory isolation
Replaces the manual try/finally save/restore with vi.stubEnv plus
vi.unstubAllEnvs in afterEach, shrinking the fix to two added lines.
* fix(coding-agent): plant decoy user skill so the empty-skills assertion cannot pass vacuously
With an empty sandbox, result.skills equal to [] could not distinguish
the package skill being correctly excluded from skill resolution being
broken entirely. A decoy skill in the stubbed HOME's .agents/skills
proves scanning still works while the package skill stays excluded.
* fix(coding-agent): simplify home isolation test
Bedrock profile configured via pi's auth flow was ignored when ambient
AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY were set, because pi forced
config.credentials from those keys and the SDK treats explicit
credentials as overriding config.profile.
Fixes#6957
Extensions have no way to read the session's scoped model set (the models
resolved from --models / enabledModels against the available catalogue --
the same list /scoped-models shows). The only model surfaces on
ExtensionContext are ctx.model (active) and ctx.modelRegistry.getAvailable()
(the whole catalogue, unfiltered), so an extension that wants a scoped
model picker must either enumerate everything or re-implement pi's scope
resolution (minimatch on provider/modelId / bare modelId + enabledModels +
--models) from disk -- duplicating core logic.
Add ctx.scopedModels: readonly ScopedModel[] to ExtensionContext, mirroring
how ctx.model is exposed (field -> bindCore -> lazy getter in createContext()).
Empty when no scoping is configured (all available models usable). Each entry
is { model, thinkingLevel? }.
- types.ts: ExtensionContext.scopedModels + ExtensionContextActions.getScopedModels
- runner.ts: field, bindCore wiring, lazy scopedModels getter
- agent-session.ts: provide getScopedModels: () => this._scopedModels
- test: assert ctx.scopedModels reflects the action (default [] + live ref)
- docs/extensions.md: document the new field
Co-authored-by: ngSoftware <alessandro@pungitore.ch>
Full design for the durable, resumable harness: session log with harness
entries, SessionTree query contract, public API with result unions, watch()
snapshots, events/hooks catalogs, recovery procedures as code, forks,
storage backends, and telemetry integration.