Skip to content

build(deps): Bump the pi group across 1 directory with 3 updates - #5

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/pi-9c7136359c
Open

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/npm_and_yarn/pi-9c7136359c

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Nov 6, 2026 •

Copy link
Copy Markdown

Bumps the pi group with 3 updates in the / directory: @earendil-works/chord, @earendil-works/pi-ai and @earendil-works/pi-durable.

Updates @earendil-works/chord from 1.4.0 to 1.5.0

Release notes

Sourced from @​earendil-works/chord's releases.

v1.5.0

New Features

  • Extension update changelogs: see what changed in installed extension packages on the next fresh launch after an update. Package authors can choose the changelog path, and collapseChangelog condenses the notices. See Show a changelog.
  • Faster streaming: large tool calls are parsed incrementally, and code-heavy answers and tool rows reuse highlighting and rendered Markdown instead of repeating the work on every update. See Keep rendering responsive.
  • Custom self-update commands: use PI_SELF_UPDATE_COMMAND or selfUpdateCommand to update pi through a wrapper or another package manager. See Custom update command.
  • GPT-6.1 Terra: available through OpenAI, Azure OpenAI Responses, and OpenAI Codex, with tool search, mid-conversation thinking-level changes, and thinking off. Codex offers a 500K context window. See Models.

Breaking Changes

  • Tool argument validation runs on @earendil-works/pi-schema. TypeBox features outside JSON Schema (Type.Refine(), Type.BigInt(), Type.Undefined(), codecs, Format.Set() formats) no longer apply to tool arguments; see Tool parameter schemas and TypeBox. Plain JSON Schema tools, including MCP tools, now get the same coercion as TypeBox tools, such as converting numeric strings and wrapping a single value into an array. Validation failures the model sees list pi-schema's messages (- path: missing, - limit: expected number, got string) instead of TypeBox's (must have required properties path, must be number).

Added

  • Extension packages can now show their changelog after an update. Pi reads pi.changelogPath from the package's package.json, or CHANGELOG.md in the package root, and on the next fresh launch shows entries newer than the last seen version under "Extension Updates". The collapseChangelog setting condenses this to one line per package (#5958, #8790)
  • pi.exec() accepts a maxBuffer option that limits stdout and stderr combined, and its result has a truncated field that is true when output exceeded the limit (#11360)
  • Device-code login (for example GitHub Copilot) now opens the verification page and copies the code when you press Enter. Providers can turn either action off with openBrowserOnConfirm: false or copyCodeOnConfirm: false (#9282, #9301)
  • pi update can update Pi through a wrapper or another package manager. Set PI_SELF_UPDATE_COMMAND (for example mise use -g npm:{packageName}@{version}) or the global selfUpdateCommand setting; pi update runs it when pi.dev reports a newer release and fills {packageName}, {version}, and {installSpec} from that release. See Custom update command (#7756, #5607)
  • Extensions can format the user's configured keybindings with keyText() and keyHint() from the tool render context and the message and entry renderer options. Importing these functions from @earendil-works/pi-coding-agent can resolve the extension's own copy of the package, which does not see pi's keybindings, so hints such as keyHint("app.tools.expand", "to expand") rendered without the key. The docs now recommend the injected functions (#4748)
  • Added persisted session-entry messageId and ISO timestamp to MarkdownTransformContext. They are available when rendering saved entries and undefined for live rendering (#7952 by @​vipentti)
  • Added a blocked option to inline ctx.ui.custom() components so approval prompts and other custom dialogs can report that pi is waiting for user input. Overlays ignore the option. See Custom screens and overlays (#11428)
  • Added Usage.cost.totalFromProvider to distinguish billed totals from catalog estimates in SDK and extension usage data; combined usage is marked provider-reported only when every part is (#11178)
  • Added concrete responseModel metadata to messages from pi-messages providers such as Radius, including when a virtual model is requested. See AssistantMessage (#11265)
  • Added GPT-6.1 Terra for OpenAI, Azure OpenAI Responses, and OpenAI Codex, with tool search and mid-conversation thinking-level changes. It supports thinking off; Codex offers a 500K context window

Changed

  • Extensions keep importing Type, Static and TSchema from @earendil-works/pi-ai: Pi maps that import to pi-ai's compat entry plus the TypeBox it bundles, now that pi-ai itself no longer re-exports TypeBox, and its StringEnum there stays a TypeBox TUnsafe<T>. The built-in tool factories (createReadTool() and the others) return TypeBoxAgentTool<typeof schema>, which keeps params typed for TypeBox.
  • Tightened system prompt
  • pi update for installs from the pi.dev installer no longer passes --min-release-age=0 to npm ci. The release lockfile pins every package, so the flag had no effect, and security scanners that block it stopped the update (#11262)
  • /model and /scoped-models now show a model's name next to its ID when the name adds words the ID lacks, such as a renamed models.json override (#5916, #11347)
  • MCP OAuth sign-in now uses pi's Client ID Metadata Document by default when the authorization server supports it for public clients, and registers a client dynamically otherwise, like Codex, Claude Code, and opencode. Clients registered before stay in use while their redirect URI matches. The new default oauth.clientRegistration: "auto" registers when clientName or a non-default callbackUrl is set; "dcr" always registers. pi mcp add --oauth-client-registration sets the mode (#11426)
  • Changed the default OpenAI model from gpt-5.5 to gpt-6.1-sol, including after ChatGPT sign-in when no model is selected (#11442)
  • Updated the built-in model catalog to models.dev v2. Realtime and image-only models are no longer offered as chat models; newer OpenAI and Google chat models are included

Fixed

  • Updated glob matching to prevent denial of service from amplified glob patterns.
  • Fixed a saved "Do not trust" decision being ignored for projects without trust-requiring resources, for example a folder with only AGENTS.md. pi auto-trusted such projects, so ctx.isProjectTrusted() returned true and package commands treated them as trusted. A saved decision in trust.json now takes precedence; auto-trust applies only when no decision is saved (#10947)
  • Fixed Shift+Enter queueing a follow-up instead of inserting a newline in Apple Terminal setups that send ESC + Return. pi now checks the local Shift key state for that sequence, as it already did for plain Return; Alt+Enter without Shift still queues a follow-up (#11559)
  • Fixed the working status (OSC 7501) disappearing in Rex and iTerm2 during runs in regular TUI mode. Rendered messages wrote OSC 133 prompt markers to the terminal, which the terminal treats as a new shell prompt; regular mode no longer writes them (#11519)
  • Fixed compaction, branch summaries, and bug-report summaries in SDK sessions passing the stored OAuth token back to the model runtime as an explicit API key. Provider auth that treats explicit keys differently from stored OAuth now sees the OAuth login, as normal prompts do (#11520)
  • Fixed a redundant system prompt update (tools, rules) when an extension set the same active tools in a different order, for example a reversed pi.setActiveTools() list. Tools that stay active now keep their order and newly activated tools are appended (#11495)
  • Fixed pi.exec() crashing pi with RangeError: Invalid string length when a command printed more output than fits in a string. The default output cap is the smaller of 256 MiB and the runtime string limit; past the cap, the command is terminated and the result has truncated: true and a nonzero exit code (#11360)
  • Fixed pi.exec() never returning when a command ignored SIGTERM after a timeout or abort. pi now sends SIGKILL 5 seconds later, as intended
  • Fixed pi install and pi remove adding ever deeper nested entries to the npm package-lock.json when the agent directory is reached through a symlink, such as ~/.pi linked into a dotfiles repo. pi now passes the resolved real path to npm (#11358)
  • MCP sign-in now rejects authorization endpoints and other OAuth metadata URLs that are not http: or https:. Before, a malicious MCP server could supply a file: or custom protocol URL, which pi passed to the platform URL handler.
  • Automatic fd and rg downloads no longer fetch x86-64 binaries on ARM32 and other architectures without a prebuilt release. pi now warns and recommends installing the tool with the system package manager (#11209)
  • Fixed /model search ranking letters scattered over an earlier part of an entry above a real match, for example opus listing gpt-oss-120b and claude-sonnet-4.5 before claude-opus-4.1 (#11220, #11296)
  • Fixed /resume search matching scattered letters anywhere in session messages and IDs, for example 1024 matching inside a session UUID. Session names and working directories still match fuzzily; IDs and message text now match only as exact substrings (#11220, #11024, #11296)

... (truncated)

Changelog

Sourced from @​earendil-works/chord's changelog.

[1.5.0] - 2026-11-02

Breaking Changes

  • Schemas are JSON Schema checked by @earendil-works/pi-schema instead of TypeBox, which is no longer a dependency of Chord. Contracts, self.state(...) schemas and method signatures take pi-schema builders or any JSON Schema object (TypeBox-built schemas still validate, being JSON Schema, but type as unknown); handler and reference types come from pi-schema's Static. Validation, input defaults and the stripping of unknown fields work on any JSON Schema by its shape (before, defaults and stripping applied only to TypeBox-built schemas). format is an annotation, no longer checked: str({ format: "email" }) accepts "nope" (TypeBox checked the formats it knew); check a format in the handler where it matters. An invalid error's message is now <path> <message> with pi-schema's paths and messages (a missing field at its own path, missing; expected string, got number instead of must be string), and its details are pi-schema's ValidationErrors ({ path, keyword, message }) instead of TypeBox's errors. Link frames, descriptions, fingerprints and identities are unchanged for schemas with the same JSON. Migration, keeping a contract's fingerprint (it is computed from the schema's JSON): Type.String/Number/Integer/Boolean/Null/Unknown/Array/Union/Optional(...) become str/num/int/bool/nul/unknown/arr/union/opt(...) with the same options; Type.Object(p) becomes obj(p, { open: true }) (pi-schema's obj is closed by default) and Type.Object(p, { additionalProperties: false }) obj(p); Type.Enum(values) becomes oneOf(values); Type.Literal(v) becomes { type: typeof v, const: v } (pi-schema's lit(v) has no type); Type.Record(Type.String(), v) becomes { type: "object", patternProperties: { "^.*$": v } } (pi-schema's map(v) uses additionalProperties); Type.Intersect(list) becomes { allOf: list }; Type.Function(params, result) and Type.Void() become { type: "function", parameters, returnType } and { type: "void" }; Type.Unsafe<T>(schema) becomes schema as Schema<T>; Type.Immutable(schema) becomes the schema (cast its Schema<readonly T[]> type); opt(...) marks only an obj property; Static and TSchema come from pi-schema (Static, Schema). pi-schema's own lit, map and closed obj give other JSON and so other fingerprints, which matters only while peers of both versions talk.
  • traceIn(options, fn, context) takes the context last (was traceIn(context, options, fn)), like the rest of the API.
  • Watch listeners take one change object before the context: WatchHandle.start((change, context) => …) with change: WatchChange<T> = { value, ops, retired }, and NativeWatchHandle.start(({ value }, context) => …) with NativeWatchChange<T>. A change is plain data, those fields only; the frame's context is the second argument. Watch.advance(change, context) takes the same object, and a frame delivered alone, with nothing folded into it, reaches the listener as the object the source passed. Migration: start(async (value, ops, ctx) => …) becomes start(async ({ value, ops }, ctx) => …), start(async (value, ctx) => …) on a native watch start(async ({ value }, ctx) => …), and advance(value, ops, ctx) becomes advance({ value, ops, retired: false }, ctx).
  • SqliteExecutor.runCounted(sql, ...params) runs one statement like run and resolves with the rows it inserted, updated or deleted, as SQLite's changes() counts them (index writes and rows changed by triggers do not count). The Node driver takes it from node:sqlite; the Durable Object driver reads SELECT changes() after the statement (the cursor's rowsWritten also counts index writes), so run stays as cheap as before. Migration: custom SqliteDatabase and SqliteExecutor implementations add runCounted.
  • Retirement is a flag, not an end: WatchEnd loses retired, and Watch.retire(context) is removed. A source reports a retirement with advance({ value, ops, retired: true }, context), whose value is what the address shows after it, and the watch goes on: the next call has retired: true, folded with every other pending frame, so a document created at the address afterwards arrives in the same or a later call as [["r", value]]. endAfterDelivery() tells a watch its source sends nothing more: it takes no more frames, delivers what is pending, and ends ended after its last call returned, at once when nothing is pending and no call runs; it stays attached until then, so the source's end() (such as at its close), stop(), cancellation, or a throwing listener still end it first. Migration: a listener that waited for closed to give retired or tested for a null value tests change.retired; a source that called retire(ctx) calls advance({ value, ops, retired: true }, ctx), then endAfterDelivery() when its owner ends with the retirement.
  • Watch listeners get a tagged call, and a watch stays open through a failure its source recovers from: WatchCall<T> is { kind: "value", value, ops, retired } or { kind: "failed", error }, and NativeWatchCall<T> is { kind: "value", value } or { kind: "failed", error } (replacing WatchChange<T> and NativeWatchChange<T>). After a failed call watch.value keeps the last value and the next value arrives as [["r", value]]; while the listener is busy, a failure drops the pending values (value then failed folds into the failure) and a value replaces a pending failure (failed then value folds into the value, as r). Watch.advance(call, context) takes a WatchCall, a value call delivered alone still reaches the listener as the object the source passed (no allocation), and Watch.fail(error, context) and NativeWatch.fail(error, context) send a failure. Migration: start(async ({ value, ops }, ctx) => …) becomes start(async (call, ctx) => { if (call.kind === "failed") return; const { value, ops } = call; … }), a native start(async ({ value }, ctx) => …) likewise; advance({ value, ops, retired }, ctx) becomes advance({ kind: "value", value, ops, retired }, ctx).
  • WatchEnd is tagged by kind instead of reason, and a watch whose acquisition context is aborted ends aborted instead of cancelled: { kind: "stopped" | "aborted" | "ended" }, { kind: "listener_error", error }, or { kind: "failed", error } (a final failure only). Watch.cancel() is abort(), observeCancellation(signal) is observeAbort(signal), and end(failure?) takes the end: end({ kind: "ended" }) or end({ kind: "failed", error }). Migration: end.reason === "cancelled" becomes end.kind === "aborted", every other end.reason becomes end.kind; watch.end() becomes watch.end({ kind: "ended" }), watch.end({ error }) becomes watch.end({ kind: "failed", error }).

Added

  • Chord v2's store (@earendil-works/chord/next) gains ChordStore.commitSeries(commits, receive, ctx): several commits in one backend transaction, each at its own seq with its own outbox row, the store metadata written once. Each commit's check runs on a step that sees the commits before it, before its writes; a check that throws refuses that commit and stores the ones before it, while a failed statement or write rolls the whole series back, and a failed COMMIT stops the store. It replaces commitInBatch, which is removed. Durable's StoreStorage stores each Session commit call through it.
  • Chord v2 has a package export again, @earendil-works/chord/next (experimental): every layer's API for apps (contracts, data, documents, hosts, jobs, links, state, the store), and, marked internal, the store's surface for adapters (StoreOwnerTables, OwnerTable, the documents engine's reads and writes, the commit step, applyBatch). v2's store gains one id allocator for every id in it, claims per table and per document namespace, Durable's Storage semantics for a refused commit (it rejects that commit only; an unknown outcome stops the store), document histories none, latest and rewindable with reads at a point, archives carrying document revisions, and batch apply; v2 objects gain self.follow (another runtime's synchronous observers as sources) and persisted jobs (persist: { resume }, a stored abort(id, ctx), deferred state changes).
  • Chord v2 (@earendil-works/chord/next) gains registries, writables, wake signals and root scopes (slice 8): defineRegistry<C, V>(name, { version }) tokens shared by plugins, served by the defining plugin's facet with host.serve(R, { compose, accepts? }); contributions from facets (defineFacet((host, self) => self.contribute(R, value))) and host-lifetime objects (StoreSelf.contribute), plain values or Chord watchables, in a table sorted by plugin name and composed lazily (once at the end of load's setups, then when read or followed, in the batch that changed the table); readers through self.registry(R) (a declared dependency: an object activates once the registry has a value, and fails transient with the new reason unavailable/undeclared when nobody serves it) and host.registry(R, ctx), a RegistryValue<V> with observe(observer, ctx); older contributions and readers adapted by accepts; the helpers keyedEntries and orderedEntries; the onError kinds registry_conflict, registry_failed, contribution_failed and contribution_refused. self.writable(initial) is an in-process value an attempt sets (identity compared); native sources (writables, registry values, ref.state) are derive sources. self.retryOn(...sources) retries a failed object when a registry value changes. host.root(fn, context, { principal? }) runs code outside objects with a handler's ctx, sealed when fn settles, context aborts or the host closes.
  • Chord v2 gains plugins (slice 7c): src/next-node/plugins (Node, to be exported as @earendil-works/chord/next/plugins) finds plugin directories, builds each facet with esbuild (now an optional peer dependency of @earendil-works/chord, imported only there), evaluates it with vm.Script, maps plugin stack traces (mapStack), installs plugin dependencies, watches plugin and base code, and rolls reloads out across hosts. host.load and host.reload work per plugin: a plugin whose setup, factory or declarations fail is left out at load ({ failed }) or kept on its old code at reload (failed in the report, reload(facets, ctx, { failed }) for build and evaluation failures), with a closure rule keeping a consumer old when its provider was kept old; an abort after a reload's commit rejects with details.committed.
  • Chord v2 gains HTTP and workers (slice 10): src/next-node/http (to be exported as @earendil-works/chord/next/http) builds a Web-standard fetch handler from the routes, wraps, sockets, hosts, relays and CSP sources plugins contribute to the registry Http, with authenticators from the registry Authenticators, responses as descriptions (json, html, bytes with ranges, redirect, error; fileResponse), and a security baseline on by default (provenance by the socket and Host, foreign requests anonymous, Origin checks, default headers with a per-response CSP nonce, signed cookies, safe downloads); Chord's own Node listener as a host-lifetime object, nodeRequest for apps mounting it on their own Node server, the links endpoint and plugin sockets with a heartbeat, closed at once when their contribution leaves. ws is an optional peer dependency, imported only there. src/next-node/workers (createWorkers, workerMain) runs a forked worker per address of a host kind, started on demand, stopped when idle, never restarted by itself (a failure stays until retry or a rollout), its plugins built by the server and shipped. The plugins component defines one definition per facet kind (kinds: { name: { platform, hostPackages } }, modules given at plugins.load(host, { kind, core, modules })), splits loading into createPluginRuntime() (no esbuild), offers scripts(kind), rolls out with one result per host (RolloutStep returns { hosts }), and resolves cross-plugin imports by plugin name (@app/notes/contract). Kernel: host.route returns a RouteHandle (retry, suspend) and a route has no backoff timer (a failed resolve fails its holders waiting); host.root(fn, ctx, { principal: undefined }) runs anonymous; a link reads origin only on parent and route links (a protocol error on a child link) and drops a per-origin record once it is idle.
  • Chord v2, experimental and not exported yet: the next object runtime in src/next (contracts, hosts and objects, documents, derives, links and relays, routines and jobs, the store), with its tests and runnable examples in test/next and its design in docs/next (design.md, the decision log decisions.md, the slice plan slices.md). Its API changes with every slice until it gets an entry point.
  • NativeWatchable<T>: an in-process value that changes and can be watched (value, watch(context) returning a NativeWatchHandle<T>), the in-process counterpart of Watchable<T>.
  • WatchHandle<T> and WatchEnd, the one watch API, moved from Durable: a serialized observation with value, start(listener), stop(), and closed; the listener gets a WatchChange<T> ({ value, ops, retired }) and the context. WatchEnd reasons are stopped, cancelled, ended, listener_error, and failed.
  • Watch<T>, the one watch handle implementation, moved from Durable: a source creates one per watch and feeds it with advance(change, context) (retired: true for a retirement), end() or end({ error }), endAfterDelivery(), and observeCancellation(signal). Frames that arrive while the listener runs (or before start) are delivered in one call with the newest value, their ops folded by PendingOps and the newest frame's context; past 10,000 pending ops after folding, the call gets one root replacement [["r", newest]] instead (replaces the collapse after 100 undelivered frames); termination drops the listener and every reference into the source.
  • Watchable<T>: a value that changes and can be watched (value, watch(context) returning a WatchHandle<T>), the common interface of documents, derives and other observable values.
  • NativeWatchHandle<T> and its implementation NativeWatch<T>: the same lifecycle for in-process values, with listener ({ value }, context) (NativeWatchChange<T>) and no ops: one call per value in order, and past 100 pending values one call with the value of the optional replace() or the newest value.
  • PendingOps and PendingTake in @earendil-works/chord/delta: the pending ops of one slow consumer, kept raw and folded while they wait (appends to one path join, a primitive set replaces earlier leaf ops on its path, r drops everything before it); size counts the pending ops after folding; take() empties the queue and hands over the folded ops, or replace past 10,000 pending ops. Received batches and op tuples are never mutated.
  • ownedSlice(text, start, end?) in @earendil-works/chord/delta: a slice of a string that does not keep the whole string alive (#9265)
  • appendText(draft, key, text) in @earendil-works/chord/delta: append to a string in a draft at O(text) cost, recorded as an append op without comparing the old and new strings (a plain draft[key] += text makes prepare() read the whole string, O(length) per change); StringKeys<T> names the keys it accepts (#9265)

Changed

  • Chord v2 (@earendil-works/chord/next) checks its schemas with @earendil-works/pi-schema instead of TypeBox: its helpers (str, num, int, bool, nul, lit, oneOf, union, obj, arr, map, opt, bytes, unknown) build on pi-schema's builders and keep the JSON contracts were fingerprinted with, so every fingerprint, normal form and stored record stays the same (a test records them and reads a store written before the switch). Static and Schema (was TSchema) come from pi-schema; opt(...) marks an object field or a table column and is no schema elsewhere (a top-level opt had no effect before); a schema with a non-enumerable key (a TypeBox schema's metadata) is refused at declaration; map(bytes()) is accepted now (it was refused while bytes() carried a TypeBox refinement). format is an annotation, no longer checked ({ type: "string", format: "email" } accepts "nope"; TypeBox checked the formats it knew). A failed check's message comes from pi-schema (args/title: expected string, got integer), its details.path escapes ~ and / (JSON Pointer); reasons and codes are unchanged. Declaring and compiling are cheaper (a 20-method contract 1.07 ms to 0.86 ms, a validator's first use 172 µs to 48 µs), a check costs the same (0.25 µs), the message envelope check up to 100 ns more per message.
  • The delta tracker keeps its one open change in a plain field and needs a WeakRef only for a second change open at the same time (one per change before). A WeakRef target survives the scavenges of its job, so a run of changes in one job promoted every change's overlay to old space: 200,000 small changes in one job now promote 2 MB instead of 47.5 MB and take 3.1 µs each instead of 3.8 µs (3.0 instead of 3.5 µs when they span jobs). A draft dropped without prepare() or abort() now stays referenced by its tracker until the tracker's next beginChange() or adopt().
  • applyImmutable(), applyImmutableBatches() and tracker revisions copy each container on a changed path with spread (or Object.assign() onto a null-prototype object) instead of one Object.defineProperty() per key, about 25 times faster per container (#9265)

Fixed

  • A host a route resolves is admitted even when the relayed reference that asked for it is released, its client disconnects, or its principal is refused or revoked while the route resolves. Before, the parent never read the new host's link: a spawned worker's own references to its parent (its lifetime: "host" objects' upstream references) waited until they failed unavailable/ready_timeout, reported as a possible reference cycle across hosts.
  • The Node SQLite driver keeps the 256 most recently used prepared statements per connection instead of every statement it ever prepared, so callers generating SQL of unbounded shapes (a query builder) no longer grow memory without bound.
  • Fixed append ops from the delta tracker and diffRevisions() keeping the whole string they were cut from alive (V8 represents a slice as a view of its source), so a delta chain, a watch backlog or a replica built from appends to a growing text held one copy of the text per commit (#9265)
Commits
  • f9a2636 Release v1.5.0
  • 513f6f1 fix: keep Chord example dependencies out of releases
  • 87f69db docs(chord): app kit principals and users
  • 2dd44fb docs(chord): app kit shape
  • 7edaa13 docs(chord): testing a plugin alone
  • 95e2015 docs(chord): single-file plugins
  • ee7f61f docs(chord): the plugin model's points 3, 4, 5 and 8, imports and builds
  • 6ab6c1e docs(chord): plugins import only the app's versioned API
  • c9f4c7b docs(chord): fold the cross-plugin import rule into design and slices
  • e1669ee docs(chord): contracts come from where the plugin's code came from
  • Additional commits viewable in compare view

Updates @earendil-works/pi-ai from 1.4.0 to 1.5.0

Release notes

Sourced from @​earendil-works/pi-ai's releases.

v1.5.0

New Features

  • Extension update changelogs: see what changed in installed extension packages on the next fresh launch after an update. Package authors can choose the changelog path, and collapseChangelog condenses the notices. See Show a changelog.
  • Faster streaming: large tool calls are parsed incrementally, and code-heavy answers and tool rows reuse highlighting and rendered Markdown instead of repeating the work on every update. See Keep rendering responsive.
  • Custom self-update commands: use PI_SELF_UPDATE_COMMAND or selfUpdateCommand to update pi through a wrapper or another package manager. See Custom update command.
  • GPT-6.1 Terra: available through OpenAI, Azure OpenAI Responses, and OpenAI Codex, with tool search, mid-conversation thinking-level changes, and thinking off. Codex offers a 500K context window. See Models.

Breaking Changes

  • Tool argument validation runs on @earendil-works/pi-schema. TypeBox features outside JSON Schema (Type.Refine(), Type.BigInt(), Type.Undefined(), codecs, Format.Set() formats) no longer apply to tool arguments; see Tool parameter schemas and TypeBox. Plain JSON Schema tools, including MCP tools, now get the same coercion as TypeBox tools, such as converting numeric strings and wrapping a single value into an array. Validation failures the model sees list pi-schema's messages (- path: missing, - limit: expected number, got string) instead of TypeBox's (must have required properties path, must be number).

Added

  • Extension packages can now show their changelog after an update. Pi reads pi.changelogPath from the package's package.json, or CHANGELOG.md in the package root, and on the next fresh launch shows entries newer than the last seen version under "Extension Updates". The collapseChangelog setting condenses this to one line per package (#5958, #8790)
  • pi.exec() accepts a maxBuffer option that limits stdout and stderr combined, and its result has a truncated field that is true when output exceeded the limit (#11360)
  • Device-code login (for example GitHub Copilot) now opens the verification page and copies the code when you press Enter. Providers can turn either action off with openBrowserOnConfirm: false or copyCodeOnConfirm: false (#9282, #9301)
  • pi update can update Pi through a wrapper or another package manager. Set PI_SELF_UPDATE_COMMAND (for example mise use -g npm:{packageName}@{version}) or the global selfUpdateCommand setting; pi update runs it when pi.dev reports a newer release and fills {packageName}, {version}, and {installSpec} from that release. See Custom update command (#7756, #5607)
  • Extensions can format the user's configured keybindings with keyText() and keyHint() from the tool render context and the message and entry renderer options. Importing these functions from @earendil-works/pi-coding-agent can resolve the extension's own copy of the package, which does not see pi's keybindings, so hints such as keyHint("app.tools.expand", "to expand") rendered without the key. The docs now recommend the injected functions (#4748)
  • Added persisted session-entry messageId and ISO timestamp to MarkdownTransformContext. They are available when rendering saved entries and undefined for live rendering (#7952 by @​vipentti)
  • Added a blocked option to inline ctx.ui.custom() components so approval prompts and other custom dialogs can report that pi is waiting for user input. Overlays ignore the option. See Custom screens and overlays (#11428)
  • Added Usage.cost.totalFromProvider to distinguish billed totals from catalog estimates in SDK and extension usage data; combined usage is marked provider-reported only when every part is (#11178)
  • Added concrete responseModel metadata to messages from pi-messages providers such as Radius, including when a virtual model is requested. See AssistantMessage (#11265)
  • Added GPT-6.1 Terra for OpenAI, Azure OpenAI Responses, and OpenAI Codex, with tool search and mid-conversation thinking-level changes. It supports thinking off; Codex offers a 500K context window

Changed

  • Extensions keep importing Type, Static and TSchema from @earendil-works/pi-ai: Pi maps that import to pi-ai's compat entry plus the TypeBox it bundles, now that pi-ai itself no longer re-exports TypeBox, and its StringEnum there stays a TypeBox TUnsafe<T>. The built-in tool factories (createReadTool() and the others) return TypeBoxAgentTool<typeof schema>, which keeps params typed for TypeBox.
  • Tightened system prompt
  • pi update for installs from the pi.dev installer no longer passes --min-release-age=0 to npm ci. The release lockfile pins every package, so the flag had no effect, and security scanners that block it stopped the update (#11262)
  • /model and /scoped-models now show a model's name next to its ID when the name adds words the ID lacks, such as a renamed models.json override (#5916, #11347)
  • MCP OAuth sign-in now uses pi's Client ID Metadata Document by default when the authorization server supports it for public clients, and registers a client dynamically otherwise, like Codex, Claude Code, and opencode. Clients registered before stay in use while their redirect URI matches. The new default oauth.clientRegistration: "auto" registers when clientName or a non-default callbackUrl is set; "dcr" always registers. pi mcp add --oauth-client-registration sets the mode (#11426)
  • Changed the default OpenAI model from gpt-5.5 to gpt-6.1-sol, including after ChatGPT sign-in when no model is selected (#11442)
  • Updated the built-in model catalog to models.dev v2. Realtime and image-only models are no longer offered as chat models; newer OpenAI and Google chat models are included

Fixed

  • Updated glob matching to prevent denial of service from amplified glob patterns.
  • Fixed a saved "Do not trust" decision being ignored for projects without trust-requiring resources, for example a folder with only AGENTS.md. pi auto-trusted such projects, so ctx.isProjectTrusted() returned true and package commands treated them as trusted. A saved decision in trust.json now takes precedence; auto-trust applies only when no decision is saved (#10947)
  • Fixed Shift+Enter queueing a follow-up instead of inserting a newline in Apple Terminal setups that send ESC + Return. pi now checks the local Shift key state for that sequence, as it already did for plain Return; Alt+Enter without Shift still queues a follow-up (#11559)
  • Fixed the working status (OSC 7501) disappearing in Rex and iTerm2 during runs in regular TUI mode. Rendered messages wrote OSC 133 prompt markers to the terminal, which the terminal treats as a new shell prompt; regular mode no longer writes them (#11519)
  • Fixed compaction, branch summaries, and bug-report summaries in SDK sessions passing the stored OAuth token back to the model runtime as an explicit API key. Provider auth that treats explicit keys differently from stored OAuth now sees the OAuth login, as normal prompts do (#11520)
  • Fixed a redundant system prompt update (tools, rules) when an extension set the same active tools in a different order, for example a reversed pi.setActiveTools() list. Tools that stay active now keep their order and newly activated tools are appended (#11495)
  • Fixed pi.exec() crashing pi with RangeError: Invalid string length when a command printed more output than fits in a string. The default output cap is the smaller of 256 MiB and the runtime string limit; past the cap, the command is terminated and the result has truncated: true and a nonzero exit code (#11360)
  • Fixed pi.exec() never returning when a command ignored SIGTERM after a timeout or abort. pi now sends SIGKILL 5 seconds later, as intended
  • Fixed pi install and pi remove adding ever deeper nested entries to the npm package-lock.json when the agent directory is reached through a symlink, such as ~/.pi linked into a dotfiles repo. pi now passes the resolved real path to npm (#11358)
  • MCP sign-in now rejects authorization endpoints and other OAuth metadata URLs that are not http: or https:. Before, a malicious MCP server could supply a file: or custom protocol URL, which pi passed to the platform URL handler.
  • Automatic fd and rg downloads no longer fetch x86-64 binaries on ARM32 and other architectures without a prebuilt release. pi now warns and recommends installing the tool with the system package manager (#11209)
  • Fixed /model search ranking letters scattered over an earlier part of an entry above a real match, for example opus listing gpt-oss-120b and claude-sonnet-4.5 before claude-opus-4.1 (#11220, #11296)
  • Fixed /resume search matching scattered letters anywhere in session messages and IDs, for example 1024 matching inside a session UUID. Session names and working directories still match fuzzily; IDs and message text now match only as exact substrings (#11220, #11024, #11296)

... (truncated)

Changelog

Sourced from @​earendil-works/pi-ai's changelog.

[1.5.0] - 2026-11-02

Breaking Changes

  • The main entry no longer re-exports TypeBox (Type, Static, TSchema), and typebox is no longer a dependency. Tool.parameters is any JSON Schema object (Tool<TParameters extends object>): @earendil-works/pi-schema's builders, which also type the arguments through pi-schema's Static, TypeBox schemas, or plain objects. validateToolCall()/validateToolArguments() validate with pi-schema (compile(schema, { coerce: true }).parse()): every schema gets the same coercion, the superset of TypeBox's Value.Convert rules (which only applied to TypeBox-built schemas) and pi-ai's own, so plain JSON Schema tools (MCP tools) now also get, for example, null to "null" for a required string, "3.5" to 3 for an integer and a single value wrapped into an array. The error text lists pi-schema's messages (- edits.0.oldText: missing, - limit: expected number, got string) instead of TypeBox's. StringEnum() returns a pi-schema Schema<T> with the same JSON ({ type: "string", enum }). The model and compat schemas (providers/model-schema, providers/compat-schema) are built with pi-schema, with the same JSON Schema, and their types come from pi-schema's Static; ProviderCompat is exported. Migration:

    // Before
    import { Type, type Static, StringEnum } from "@earendil-works/pi-ai";
    const params = Type.Object({ path: Type.String(), mode: StringEnum(["a", "b"]) });
    type Params = Static<typeof params>;
    // After: pi-schema builders (closed objects; pass { open: true } to allow other properties)
    import { StringEnum } from "@​earendil-works/pi-ai";
    import { obj, type Static, str } from "@​earendil-works/pi-schema";
    const params = obj({ path: str(), mode: StringEnum(["a", "b"]) });
    type Params = Static<typeof params>;
    // Or keep TypeBox, imported from typebox itself (its schemas are JSON Schema)
    import { Type, type Static } from "typebox";

  • Tool arguments are validated as JSON Schema only; nothing runs through TypeBox's own validator any more, so TypeBox features outside JSON Schema no longer apply to tool arguments:

    • Type.Refine() refinements are not run: arguments they would reject are accepted.
    • Type.BigInt(), Type.Undefined() and Type.Void() reject every JSON argument (TypeBox converted to them).
    • Codecs (Type.Codec()) do not decode: the tool receives the encoded JSON value.
    • Formats registered with TypeBox's Format.Set() are not checked; the standard formats plus url and json-pointer-uri-fragment are.
    • Property names are names, not regular expressions: "a.b" no longer also converts the argument axb.
    • additionalProperties converts only undeclared properties; TypeBox also converted declared ones with it when an object had more than one property.
  • A $ref that names nothing (a missing definition, an external document) rejects only arguments at its place (- t: unresolved reference #/definitions/Missing) instead of failing every call of the tool, as TypeBox's rejection did before.

  • appendGrammarToolInputJsonDelta() (api/constrained-sampling) takes the input delta instead of the whole input so far and no longer closes the input; close it with the new closeGrammarToolInputJson() (#9265)

Added

  • Added optional openBrowserOnConfirm and copyCodeOnConfirm to device_code auth events and OAuthDeviceCodeInfo. Both default to true; set them to false to stop clients from offering to open the verification page or copy the code (#9282, #9301)
  • Added StreamingJsonParser, an incremental, tolerant parser for JSON text that arrives in pieces (each piece is read once; snapshots of the partial value never change once returned), and ToolCallArgumentsStream, which every provider now uses for streamed tool-call arguments; its append() also returns the changes each piece made (JsonChange: a value set at a path, or text appended to a string), for writers that keep their own copy (#9265)
  • Added GPT-6.1 Terra to the OpenAI, Azure OpenAI Responses, and OpenAI Codex providers with official pricing ($1 input, $0.05 cached input, $1.25 cache writes, $5 output, long-context tier above 272k), tool search, and mid-conversation effort changes. Unlike GPT-6.1 Sol it supports off (reasoning effort none); Codex serves it with a 500K context window
  • Added Usage.cost.totalFromProvider to distinguish provider-reported billed totals from catalog estimates. Missing or false means an estimate; combined usage is marked provider-reported only when every part is (#11178)
  • Added responseModel to pi-messages terminal events, preserving the concrete model reported by providers such as Radius when a virtual model is requested (#11265)

Changed

  • The faux provider streams partial tool-call arguments, so toolcall_delta partials show growing arguments as real providers do (#9265)
  • The built-in model catalog is generated from models.dev's v2 catalog, and its per-provider rules moved from scripts/generate-models.ts into one TypeScript definition per provider under catalog/. Models that models.dev types as realtime or image models (gpt-realtime-2.1, gemini-3.1-flash-live-preview, gemini-3.1-flash-lite-image) are no longer offered as chat models, and models.dev's newer OpenAI and Google chat models (chat-latest, gpt-5.6-cyber, gemini-robotics-er-2-preview) are now included

Fixed

  • Fixed Claude models that accept mid-conversation system messages rejecting a session with a 400 (role 'system' must precede an 'assistant' message) when a system update was followed by an assistant reply with nothing to send to Anthropic, such as an empty reply or another model's reasoning-only reply (#11091)
  • Fixed Claude models with mid-conversation effort failing with Invalid effort level on accounts whose organization caps effort below high, even at low or medium thinking, by sending a fixed low top-level effort (#10063)
  • Fixed streamed tool-call arguments being re-parsed in full on every delta, which made CPU time and memory grow quadratically with the size of a tool call (a 90 KB write on Anthropic peaked at about 140 MB of heap; a 500 KB one took minutes). Every provider and the pi-messages client now read each delta once, and keep the raw argument text and their other stream state (partialJson, partialArgs, index, customInput, streamIndex, Bedrock's encrypted reasoning bytes) off the content blocks, so it no longer reaches partial messages or transcripts of failed streams (#9265)
  • Fixed grammar (custom) tool input streaming copying the whole input so far on every delta (#9265)

... (truncated)

Commits
  • f9a2636 Release v1.5.0
  • ddbe02c docs: audit changelogs and add release highlights
  • 6536f80 feat(ai,agent,coding-agent): validate tool arguments with pi-schema, drop Typ...
  • b4c2e92 fix: remove vulnerable dependency chains
  • c719d5b fix(ai): do not retry Zhipu usage limit errors (code 1308) (#11572)
  • 2e0f175 feat(ai): refactor provider definitions (#11368)
  • 068e972 fix(ai): remove adaptive thinking metadata test that duplicated generator logic
  • 44f6851 fix(ai): use Anthropic Messages for OpenRouter ~anthropic latest aliases
  • d3398f8 fix(ai): detect LiteLLM ContextWindowExceededError as context overflow (#11494)
  • 842c731 fix(ai): fall back on Bedrock strict tool limits
  • Additional commits viewable in compare view

Updates @earendil-works/pi-durable from 1.4.0 to 1.5.0

Release notes

Sourced from @​earendil-works/pi-durable's releases.

Description has been truncated

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code labels Nov 6, 2026
Bumps the pi group with 3 updates in the / directory: [@earendil-works/chord](https://github.com/earendil-works/pi/tree/HEAD/packages/chord), [@earendil-works/pi-ai](https://github.com/earendil-works/pi/tree/HEAD/packages/ai) and [@earendil-works/pi-durable](https://github.com/earendil-works/pi/tree/HEAD/packages/durable).


Updates `@earendil-works/chord` from 1.4.0 to 1.5.0
- [Release notes](https://github.com/earendil-works/pi/releases)
- [Changelog](https://github.com/earendil-works/pi/blob/main/packages/chord/CHANGELOG.md)
- [Commits](https://github.com/earendil-works/pi/commits/v1.5.0/packages/chord)

Updates `@earendil-works/pi-ai` from 1.4.0 to 1.5.0
- [Release notes](https://github.com/earendil-works/pi/releases)
- [Changelog](https://github.com/earendil-works/pi/blob/main/packages/ai/CHANGELOG.md)
- [Commits](https://github.com/earendil-works/pi/commits/v1.5.0/packages/ai)

Updates `@earendil-works/pi-durable` from 1.4.0 to 1.5.0
- [Release notes](https://github.com/earendil-works/pi/releases)
- [Changelog](https://github.com/earendil-works/pi/blob/main/packages/durable/CHANGELOG.md)
- [Commits](https://github.com/earendil-works/pi/commits/v1.5.0/packages/durable)

---
updated-dependencies:
- dependency-name: "@earendil-works/chord"
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pi
- dependency-name: "@earendil-works/pi-ai"
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pi
- dependency-name: "@earendil-works/pi-durable"
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: pi
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot changed the title build(deps): Bump the pi group with 3 updates build(deps): Bump the pi group across 1 directory with 3 updates Nov 6, 2026
@dependabot
dependabot Bot force-pushed the dependabot/npm_and_yarn/pi-9c7136359c branch from 22f467e to fab60ee Compare November 6, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants