Compare commits

..

4 Commits

Author SHA1 Message Date
joakimp fb2cb2305c release(v1.12.2): pi 1.4.0, pi-studio v0.11.1, Node 24.22.1 + git-lfs 3.8.1 security
Lint / skill-floor (push) Successful in 8s
Lint / hadolint (push) Successful in 10s
Lint / actionlint (push) Successful in 23s
Lint / doc-drift (push) Successful in 14s
Publish Docker Image / promote-base-latest (push) Blocked by required conditions
Publish Docker Image / update-description (push) Blocked by required conditions
Publish Docker Image / lint-gate (push) Successful in 24s
Publish Docker Image / resolve-versions (push) Successful in 12s
Publish Docker Image / base-decide (push) Successful in 10s
Publish Docker Image / build-base (push) Successful in 42m52s
Publish Docker Image / smoke (push) Successful in 5m33s
Publish Docker Image / build-variant (push) Has started running
Publish Docker Image / smoke-studio (push) Failing after 7m2s
Publish Docker Image / build-variant-studio (push) Has been skipped
Patch release. pi 1.3.0 -> 1.4.0 (audited, notes next to ARG PI_VERSION in
Dockerfile.variant). pi-studio v0.10.2 -> v0.11.1 (-studio tags; pane echo
now defaults to Summary, see Upgrade notes). The base rebuilds because
rootfs/ changed (8ae1659), so Node v24.22.1 (8 CVEs), git-lfs v3.8.1
(4 CVEs), uv 0.13.5 and agent-browser 0.39.2 come in from latest.
BASE_REBUILD_DATE marker refreshed while the base rebuilds anyway.
check-doc-drift.sh: OK (pi-version and pi-studio named above v1.12.1).
2026-10-23 00:07:49 +02:00
joakimp 8ae1659273 setup-lan-access: Include ~/.ssh/config even if it appears after start
Lint / skill-floor (push) Successful in 11s
Lint / hadolint (push) Successful in 17s
Lint / actionlint (push) Successful in 22s
Lint / doc-drift (push) Successful in 13s
Same rule as c693aa3 (ssh-lan.conf). `Include ~/.ssh/config` went into
~/.ssh-local/config only if ~/.ssh/config was readable at container start.
A file that appeared later, e.g. on a host provisioned after its first
`docker compose up`, was ignored until a restart. Entries ADDED to a file that
already existed were never affected. Measured on tor-ms22 2026-10-19,
expectations written first:
- ssh reads Included files on every connection. A Host added after the config
  was generated resolved with no rerun.
- the bind mount is live: on the Mac, a created file, an atomic replace
  (write + mv) and an append each showed in the container within a second.
So this fixes only the file-appears-later case. The line is now always
written. A file that exists but is unreadable is still left out (ssh would
abort every connection), now with a warning on stderr.

Generated config compared with v1.12.1's: byte-identical when ~/.ssh/config
exists at start (DEVBOX_LAN_ACCESS=jump and auto, with and without
ssh-lan.conf), and stderr is the same too. When the file is absent, the only
difference is the added Include block.
scripts/smoke-test.sh: two new checks, run here the way exec_test runs them
(sh -c). Against v1.12.1, "Included even if created after start" FAILS and
"unreadable left out" PASSES. Against this script both pass, and so do the two
ssh-lan.conf checks.

CHANGELOG Unreleased also names pi-studio v0.10.4 (c83940d), because
check-doc-drift (and so lint on main) had gone red: upstream released 0.10.3
and 0.10.4 right after v1.12.1. 0.10.4 fixes a 0.10.2 Stop regression for
macOS uid -2. Not reachable in the image: 0.10.2's own readStopProcessTable
parsed this container's live ps output (9 rows, min uid 1000). Control: a
macOS-style -2 row throws. The lockfile packages are unchanged, and 0.10.4
loads into pi 1.3.0 with the same 4 studio_* tools, rc 0.

check-doc-drift rc 0, lint-shell rc 0, shellcheck warnings 0 before and after.
2026-10-19 08:25:14 +02:00
joakimp 3b1c67a9bf release(v1.12.1): ssh-lan.conf Include fix, pi-studio v0.10.2, gitea-mcp v1.10.0
Lint / skill-floor (push) Successful in 11s
Lint / hadolint (push) Successful in 27s
Lint / doc-drift (push) Successful in 28s
Lint / actionlint (push) Successful in 37s
Publish Docker Image / lint-gate (push) Successful in 22s
Publish Docker Image / resolve-versions (push) Successful in 12s
Publish Docker Image / base-decide (push) Successful in 9s
Publish Docker Image / build-base (push) Successful in 42m39s
Publish Docker Image / smoke (push) Successful in 5m24s
Publish Docker Image / smoke-studio (push) Successful in 8m23s
Publish Docker Image / build-variant (push) Successful in 16m59s
Publish Docker Image / update-description (push) Successful in 16s
Publish Docker Image / promote-base-latest (push) Successful in 18s
Publish Docker Image / build-variant-studio (push) Successful in 17m33s
Patch release. The fix itself is c693aa3: setup-lan-access.sh now writes the
ssh-lan.conf Include whether or not the file exists at container start.

Upstream survey on 2026-10-19 (check-doc-drift check 9, npm, PyPI, tags, and
the GitHub/Gitea release APIs for every base tool pinned to `latest`):
- moved: pi-studio v0.10.1 -> v0.10.2 (4b4abd1), pi-toolkit 3760d90 -> 7c05901,
  gitea-mcp v1.9.0 -> v1.10.0
- unchanged: pi 1.3.0, mempalace 3.11.0, pi-atelier v0.16.0, pi-obsmem 022da5e,
  pi-extensions, pi-fork, mempalace-toolkit, skillset snapshot, and every
  other base tool
Audits, all on tor-ms22:
- pi-studio 0.10.2: lockfile packages identical to 0.10.1's. Loaded into
  pi 1.3.0, it registers the same 4 studio_* tools, rc 0.
- pi-toolkit: the new model IDs are in pi 1.3.0's bedrock registry, and they
  reach fresh volumes only (the settings merge fills only missing keys).
- gitea-mcp 1.10.0: stdio tools/list gives 58 -> 61 tools, none removed, and
  stdout carries JSON only.

CHANGELOG: Unreleased -> v1.12.1 with the components table and the audits.
README: "Since v1.12.1". Dockerfile.base: BASE_REBUILD_DATE marker refreshed;
the base rebuilds anyway because rootfs changed.
check-doc-drift rc 0, lint-shell, the link-pi contract tests, skill-floor and
the Dockerfile directives check: all clean.
2026-10-19 00:37:51 +02:00
joakimp c693aa341d setup-lan-access: Include ssh-lan.conf even if it is created after start
Lint / skill-floor (push) Successful in 13s
Lint / doc-drift (push) Failing after 13s
Lint / hadolint (push) Successful in 17s
Lint / actionlint (push) Successful in 20s
The Include for ~/.config/devbox-shell/ssh-lan.conf was written only if the
file was readable at container start. A file created afterwards was visible in
the container and silently ignored. Hit on EMB-3HXGNXJ0 on 2026-10-18: the
tor-ms22 override was skipped, ssh fell through to the Mac's passphrase key,
and BatchMode got "Permission denied (publickey,...)" until the script was
rerun by hand.

The line is now always written. Measured with this image's OpenSSH 10.0p2,
expectations written before each run:
- file missing, and also its parent directory missing: Include skipped, rc 0
- file created after the config was written: read on the next ssh, no rerun
- a directory at that path: ignored, rc 0
- file present but unreadable: ssh aborts EVERY connection ("Can't open user
  config file"). So that case is still left out, now with a warning on stderr.
- world-writable file: "Bad owner or permissions". The old code Included that
  case too, so nothing changes there.
When the file exists at start, the generated config is byte-identical to
v1.12.0's. When it is absent, the only difference is the added Include block.

scripts/smoke-test.sh: two new checks, "Included even if created after start"
and "unreadable left out". Run here, the way exec_test runs them: against the
v1.12.0 script the first FAILs and the second PASSes; against this one both
PASS. README "Naming LAN peers": creating the file no longer needs a restart.
CHANGELOG: Unreleased > Fixed.

lint-shell: clean. check-doc-drift still fails, as it already did on HEAD:
pi-toolkit 3760d90->7c05901 and pi-studio 76db01f->4b4abd1 have moved upstream
since v1.12.0 and are not named in the CHANGELOG. That is release prep and is
not addressed here.
2026-10-19 00:17:23 +02:00
6 changed files with 336 additions and 14 deletions
+211
View File
@@ -11,6 +11,217 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
---
## v1.12.2 — 2026-10-22
Patch release. Headline: **pi `1.4.0`** (audited, see *Changed* below), plus
two upstream **security releases** baked into the base image: **Node
`v24.22.1`** (8 CVEs) and **git-lfs `v3.8.1`** (4 CVEs). Also **pi-studio
`v0.11.1`** (`-studio` tags only), uv `0.13.5`, agent-browser `0.39.2`, and
`~/.ssh/config` is Included even if it appears after start (*Fixed*, below).
Surveyed 2026-10-22. Unchanged since v1.12.1: mempalace `3.11.0` (PyPI
`latest`), pi-atelier `v0.16.0` (`3b42825`, newest tag),
pi-observational-memory `022da5e` (= `master`), pi-toolkit `7c05901`,
pi-extensions `4c1d28d`, pi-fork `e69725c`, mempalace-toolkit `d5a03f4`. The
skillset snapshot needs no refresh (`vendor-mempalace-skill.sh --check`: OK,
still `96b840e`). The base image is rebuilt because `rootfs/` changed, so apt
and the base tools pinned to `latest` re-resolve. Of those, Node, git-lfs, uv
and agent-browser moved upstream (*Changed*, below). gitea-mcp `v1.10.0`, nvim
`0.12.6`, fzf `0.74.5`, gitleaks `8.30.1`, bat `0.26.1`, eza `0.23.5`, zoxide
`0.10.0`, gosu `1.19`, micro `2.0.15`, tealdeer `1.9.0`, typst `0.15.1`, yq
`4.54.1` and playwright `1.64.1` match their newest releases. Each was compared
against the v1.12.1 image on tor-ms22.
### Components that move with this release
| Component | Baked in v1.12.1 | v1.12.2 | Why |
|---|---|---|---|
| **pi** | `1.3.0` | **`1.4.0`** | audited, see *Changed* below |
| **pi-studio** (`-studio` tags) | `v0.10.2` (`4b4abd1`) | **`v0.11.1`** (`8a410b4`) | newest tag, see *Changed* below |
| **Node** (base) | `v24.22.0` | **`v24.22.1`** (or later: NodeSource `node_24.x` at build time) | security release, see *Changed* below |
| **git-lfs** (base) | `v3.8.0` | **`v3.8.1`** (or later: `latest` at build time) | security release, see *Changed* below |
| uv (base) | `0.13.1` | **`0.13.5`** (or later: `latest` at build time) | see *Changed* below |
| agent-browser (base) | `0.39.1` | **`0.39.2`** (or later: `latest` at build time) | see *Changed* below |
### Upgrade notes
- **pi-studio's REPL pane now echoes each send by default** (`-studio` tags
only). From 0.11.0, *Pane echo* defaults to **Summary**, as in pi-repl: the
tmux pane shows each submitted block (up to 20 lines or 2,000 characters)
above its output. Before, the default was **Off**. The same default applies
to the agent's `studio_repl_send` tool when it passes no `echoMode`. A choice
of **Full** made in Studio is kept. To get the old quiet pane back, choose
**Off** under the REPL pane's **More**, or set
`PI_STUDIO_REPL_ECHO_MODE=off` for the agent tool. What Studio *records* is
unchanged: only the code and its output.
### Changed
- **pi `1.3.0` → `1.4.0`** (npm `latest`, published 2026-10-21). No breaking
changes listed. Fixes that land on how this fleet uses pi: compaction and
branch summaries no longer fail with "generation hit the token cap" when
reasoning uses most of the old fixed summary limit (#9512, #9075); resumed
sessions and `/tree` no longer leave extension tools inactive (#11062); a
session no longer breaks after recovering from a reply cut off at the output
limit (#11227); codemode scripts wait up to 60 s for a free worker instead
of failing (#11184); the terminal is restored when pi exits without its
normal teardown (#11170); concurrent extension dialogs no longer lose a
promise (#11166); the `grep` tool runs `rg --no-config` (#11203); and
extensions can import `@earendil-works/pi-ai/models` (#11156). Audited
2026-10-22 on tor-ms22. The only API changes that could reach us are the
attribution headers that `before_provider_headers` handlers no longer see,
`generateSummary()` ignoring `reserveTokens`, and two removed exports
(`mergeProviderAttributionHeaders`, `getPiUserAgent`). None is used by any
companion, fleet extension or mempalace-toolkit. pi-agent-core changes only
by the new optional `identity` field, so obsmem's `finishTurn` turn cap is
untouched. All 58 names our extensions import from pi resolve in 1.4.0.
With a copy of tor-ms22's real `~/.pi/agent`, `pi -p` exits 0 with nothing
on stderr. The TUI loads the same extensions as on 1.3.0, with no
extension issues. Atelier paints in both renderers, and the bridge registers
its 45 tools. Full audit next to `ARG PI_VERSION` in `Dockerfile.variant`.
- **pi-studio `v0.10.2` → `v0.11.1`** (`8a410b4`, `-studio` tags only). Five
upstream releases, 2026-10-19 to 2026-10-22. 0.10.3: IPython prints a value
once (`Out[5]: …`), not twice, and after a Quick send the cursor returns to
the input box. 0.10.4: Stop works again on a Mac that has a process running
as `nobody`. That was a 0.10.2 regression: macOS lists that uid as `-2`, and
the 0.10.2 parser refused to stop anything. **It does not affect the
image**: inside the container, `ps` shows only the container's own
processes, and none has a negative uid. Measured 2026-10-19 on tor-ms22.
0.10.2's own `readStopProcessTable` parsed the live process table (9 rows,
min uid 1000). Control: a macOS-style `-2` row made it throw "Could not parse
process ownership data". 0.10.5: the Quick-send result shows while the
cursor stays in the box, and the Prompt toolbar fits on one row on a
13-inch screen. 0.11.0: the REPL pane runs Ruby (irb), Java (JShell), Octave,
MATLAB, gnuplot and C++ (Cling) when they are on `PATH` (none is baked into
this image), a send ends within seconds when its runtime crashes instead of
waiting out the 20 s timeout, and the pane-echo default changes (see
*Upgrade notes*). 0.11.1: a shorter run status beside **Stop**, and the line
above a response shows which one you are on ("9/9"). Dependencies are
unchanged: `package-lock.json` differs from 0.10.2's only in the package's
own version, and `ws` is still the only runtime dependency. Loaded into pi
`1.4.0` with `pi --no-extensions -e <clone>/index.ts -p`, it registers the
same four tools (`studio_repl_status`, `studio_repl_send`,
`studio_export_pdf`, `studio_export_html`), rc 0, nothing on stderr.
Control: without `-e`, none.
- **Node `v24.22.0` → `v24.22.1`** (base, NodeSource `node_24.x`). An LTS
security release with 8 CVEs in http, dns, tls, permission, crypto and
brotli. On 2026-10-21 NodeSource still served only `24.22.0`, so a rebuild
then would not have picked it up. On 2026-10-22 it serves
`24.22.1-1nodesource1` for both arm64 and amd64.
- **git-lfs `v3.8.0` → `v3.8.1`** (base). A security release (2026-10-22)
with 4 CVEs. Among them: remote commands sent over SSH are now fully quoted
(CVE-2026-69260), and Cookie headers are no longer forwarded to another host
after a redirect (CVE-2026-69261).
- **uv `0.13.1` → `0.13.5`** (base). Bundled root certificates updated,
dropped GET and HEAD requests retried, `uv export` faster with extras, lower
peak memory when resolving, and new CPython builds (3.15.1, 3.14.9, 3.13.17).
No breaking changes.
- **agent-browser `0.39.1` → `0.39.2`** (base). Bug fixes only. The ones an
agent notices: a click or double-click on a hidden, disabled or off-viewport
element now returns an error instead of reporting success, an ambiguous
stale element reference fails instead of picking another element, and an
unreadable or invalid policy file denies actions instead of being ignored.
### Fixed
- **`~/.ssh/config` is Included even if it appears after the container
started.** Same rule as the `ssh-lan.conf` fix in v1.12.1. `setup-lan-access.sh`
wrote `Include ~/.ssh/config` into `~/.ssh-local/config` only if
`~/.ssh/config` was readable at container start, so a file that appeared
later (a host provisioned after its first `docker compose up`) was ignored
until a restart. Entries *added* to a file that already existed were never
affected: ssh reads Included files on every connection, and the bind mount is
live. Measured 2026-10-19 on tor-ms22: on the Mac, a newly created file, an
atomic replace (write + `mv`, as editors do) and an append each showed up in
the container within a second. The line is now written whether or not the
file exists. A file that exists but is unreadable is still left out, with a
warning on stderr, because ssh would abort every connection. When the file
exists at start the generated config is byte-identical to v1.12.1's (checked
with and without `ssh-lan.conf`). When it is absent, the only difference is
the added Include block. `scripts/smoke-test.sh` gains two checks, Included
when created after start and left out when unreadable. Against the v1.12.1
script the first fails and the second passes; against this one both pass.
## v1.12.1 — 2026-10-19
Patch release. Headline: **`ssh-lan.conf` is Included even if it is created
after the container started** (*Fixed*, below). Also **pi-studio `v0.10.2`**,
pi-toolkit `7c05901` (new Bedrock defaults for fresh volumes) and gitea-mcp
`v1.10.0`. Surveyed 2026-10-19; no other upstream moved. Unchanged since v1.12.0:
pi `1.3.0` (npm `latest`), mempalace `3.11.0` (PyPI `latest`), pi-atelier `v0.16.0`
(`3b42825`, newest tag), pi-observational-memory `022da5e` (= `master`),
pi-extensions `4c1d28d`, pi-fork `e69725c`, mempalace-toolkit `d5a03f4`. The
skillset snapshot needs no refresh (`vendor-mempalace-skill.sh --check`: OK,
still `96b840e`). The base image is rebuilt because `rootfs/` changed, so apt
and the base tools pinned to `latest` re-resolve. Of those, only gitea-mcp moved
upstream: `v1.9.0` → `v1.10.0`. uv `0.13.1`, Node `v24.22.0`, nvim `0.12.6`,
fzf `0.74.5`, gitleaks `8.30.1`, bat `0.26.1`, eza `0.23.5`, zoxide `0.10.0`,
git-lfs `3.8.0`, gosu `1.19`, micro `2.0.15`, tealdeer `1.9.0`, typst `0.15.1`,
yq `4.54.1`, agent-browser `0.39.1` and playwright `1.64.1` match their newest
releases. Each was compared against the v1.12.0 image on tor-ms22.
### Components that move with this release
| Component | Baked in v1.12.0 | v1.12.1 | Why |
|---|---|---|---|
| **pi-studio** (`-studio` tags) | `v0.10.1` (`76db01f`) | **`v0.10.2`** (`4b4abd1`) | newest tag, see *Changed* below |
| pi-toolkit | `3760d90` | **`7c05901`** (or later: CI resolves `main` at build time) | `settings.example.json` defaults move to Claude 5.5 on Bedrock, see *Changed* below |
| gitea-mcp (base) | `v1.9.0` | **`v1.10.0`** (or later: `latest` at build time) | additive, see *Changed* below |
### Changed
- **pi-studio `v0.10.1` → `v0.10.2`** (`-studio` tags only). Windows fixes. The
REPL waits for the runtime's prompt before saying it started. Stopping a
session ends its processes. Code goes only to the exactly named session. The
selected session follows what you choose. One behaviour change for agents: a
`studio_repl_send` that names no session now fails when Studio shows none
selected ("No REPL session is selected in Studio …"). Before, it picked a
session. Audited 2026-10-19 on tor-ms22. Dependencies are unchanged:
`package-lock.json` packages are identical to 0.10.1's, `ws` is the only
runtime dependency, and pi peers are `*`. Loaded into this image's pi `1.3.0`
with `pi --no-extensions -e <clone>/index.ts -p`, it registers the same four
tools as 0.10.1 (`studio_repl_status`, `studio_repl_send`, `studio_export_pdf`,
`studio_export_html`), with no extension error and rc 0. Control: the same
run against the baked 0.10.1.
- **pi-toolkit `3760d90` → `7c05901`:** `settings.example.json` now defaults to
`eu.anthropic.claude-opus-5-5`. `enabledModels` becomes opus/sonnet/haiku `-5-5`,
and the observational-memory and pi-fork `fast`/`balanced`/`deep` models move
to the same family. Also a `*.bak-*` gitignore. **Only a fresh
`devbox-pi-config` volume gets these defaults.** On an existing volume, the
entrypoint's settings merge fills in only *missing* keys and keeps arrays
verbatim, so a model you already set stays. All three IDs are in pi
`1.3.0`'s built-in `amazon-bedrock` registry, and `eu.anthropic.claude-opus-5-5`
is this session's default model on tor-ms22.
- **gitea-mcp `v1.9.0` → `v1.10.0`** (base, `latest` at build time). Additive: three
new tools (`compare_commits`, `get_commit_status`, `create_commit_status`) and
new methods on existing ones. The fix that matters for stdio clients: logs go
to stderr, and `-d` no longer writes to stdout, where it corrupted the protocol.
Measured 2026-10-19 with a stdio `initialize` + `tools/list` against
gitea.jordbo.se: 1.9.0 lists 58 tools, 1.10.0 lists 61. None were removed,
and stdout carried JSON only. The image only bakes the binary; nothing in it
configures it.
### Fixed
- **`~/.config/devbox-shell/ssh-lan.conf` is Included even if it is created
after the container started.** `setup-lan-access.sh` wrote the
`Include ~/.config/devbox-shell/ssh-lan.conf` line only if the file was
readable at container start. A file created afterwards was visible in the
container and silently ignored, so ssh fell through to `~/.ssh/config`. Hit on
EMB-3HXGNXJ0 on 2026-10-18: its `tor-ms22` override (container key, no
passphrase) was skipped. ssh then used the Mac's passphrase-protected key, and
`BatchMode` failed with `Permission denied (publickey,…)`. Rerunning the script
inside the container fixed it. The line is now written whether or not the file
exists. With this image's OpenSSH 10.0p2, an `Include` whose file (or parent
directory) is missing is skipped. A file created later is read on the next
connection, and a directory at that path is ignored. A file that **exists but
is unreadable** makes ssh abort every connection (`Can't open user config
file`), so that one case is still left out, now with a warning on stderr.
Output is byte-identical to v1.12.0 when the file exists at start.
`scripts/smoke-test.sh` gains two checks: Included when created after start,
and left out when unreadable. Against the v1.12.0 script the first fails and
the second passes. README *Naming LAN peers* no longer says that creating the
file needs a restart.
## v1.12.0 — 2026-10-18
Minor release. Headlines: **pi 1.3.0**, **mempalace 3.11.0**, **pi-atelier
+1 -1
View File
@@ -14,7 +14,7 @@
# content-addressed over this file, so any byte change invalidates the
# cache. Recommended cadence: once per release for security updates.
#
# BASE_REBUILD_DATE: 2026-10-18 (v1.12.0 — mempalace 3.11.0, link-pi-extensions.sh, studio-expose and skill doc fixes; apt and base tools re-resolve (upstream on 2026-10-18: uv 0.13.1, gitea-mcp 1.9.0, Node v24.22.0; nvim 0.12.6, fzf 0.74.5 unchanged); previous marker 2026-10-14 / v1.11.1)
# BASE_REBUILD_DATE: 2026-10-22 (v1.12.2 — setup-lan-access.sh always Includes ~/.ssh/config; apt and base tools re-resolve (upstream on 2026-10-22: Node v24.22.1 and git-lfs 3.8.1 security releases, uv 0.13.5, agent-browser 0.39.2; gitea-mcp 1.10.0, nvim 0.12.6, fzf 0.74.5 unchanged); previous marker 2026-10-19 / v1.12.1)
#
# ── Lineage note ─────────────────────────────────────────────────────
# Adapted from opencode-devbox/Dockerfile.base (commit before v1.16.2).
+36 -1
View File
@@ -317,7 +317,42 @@ ARG USER_NAME=developer
# extension with no [Extension issues], the bridge registers its 45 tools,
# /om:status, /om:view and the /studio commands are offered, and atelier
# paints ACTIVITY/READY/TOOLS in both renderers (controls with -ne: none).
ARG PI_VERSION=1.3.0
#
# v1.12.2: 1.3.0 -> 1.4.0 (npm latest, published 2026-10-21T22:48Z). MEASURED
# 2026-10-22 on tor-ms22 from isolated npm prefixes for 1.3.0 and 1.4.0, with
# the published tarballs unpacked side by side:
# 1. NO `### Breaking Changes` section in 1.4.0. Behaviour changes, none of
# which reaches us: attribution headers come from pi-ai's client identity,
# so `before_provider_headers` handlers no longer see them, and
# `generateSummary()` ignores `reserveTokens`. 0 uses of either
# (`before_provider_headers`, `generateSummary`, `reserveTokens`) in
# pi-extensions, pi-fork, obsmem, pi-atelier and pi-studio. Control: the
# same grep finds `registerTool` in 18 files.
# 2. pi-agent-core 1.3.0 -> 1.4.0 is NOT byte-identical this time, but every
# changed line in dist (4 files) is the new optional `identity` field and
# `DEFAULT_AGENT_IDENTITY`; 0 changed lines mention finishTurn, so obsmem's
# turn cap is untouched. pi-tui adds only exit-restore methods
# (`restoreOnExit`, `getExitRestorePrefix/Suffix`, #11170); atelier's
# internals TuiMainScreen / renderLayoutFrame are in the same 10 dist
# files. The extension loader only gains the `@earendil-works/pi-ai/models`
# alias (#11156). pi-mcp and pi-codemode dist are byte-identical.
# 3. Public API: the only removed .d.ts exports are
# `mergeProviderAttributionHeaders` (pi-coding-agent) and `getPiUserAgent`
# (pi-ai). 0 consumers. All 58 runtime-imported names across pi-toolkit,
# pi-extensions, pi-fork, obsmem, pi-studio (0.10.2 and 0.11.1),
# pi-atelier, mempalace-toolkit, ~/.pi/agent/extensions and /opt/fleet-pi
# resolve in 1.4.0, and in 1.3.0 (control: an injected fake name is
# reported missing).
# 4. engines.node is still `>=22.19.0`. Peer ranges: atelier >=0.84.0, fork
# >=0.75.0, obsmem and studio `*`.
# Exercised before tagging, with a copy of tor-ms22's real ~/.pi/agent
# (pi-studio swapped for v0.11.1): `pi -p` answers PONG, rc 0 in 10 s (1.3.0:
# 9 s), nothing on stderr. In the TUI the [Extensions] list is the same as on
# 1.3.0, with no [Extension issues]. The bridge registers its 45 tools, and
# TOOLS shows 58 / 64 on both. /om:status, /om:view and the /studio commands
# are offered, and atelier paints ACTIVITY/READY/TOOLS in both renderers
# (control with -ne: none).
ARG PI_VERSION=1.4.0
ARG PI_TOOLKIT_REF=main
ARG PI_EXTENSIONS_REF=main
# Repo URLs default to the canonical gitea origin but are overridable so a
+16 -8
View File
@@ -1174,12 +1174,20 @@ published image (they are a fact about your LAN, not the image). Alternatively,
set `DEVBOX_LAN_AUTOJUMP_PRIVATE=1` to ProxyJump *any* RFC1918 address through
the host without naming peers (see `.env.example`).
Once the file exists it is re-read on every connection, so *edits* take effect
immediately — no container or session restart. **Creating it for the first time
does need one restart**, because `setup-lan-access.sh` only emits the
`Include ~/.config/devbox-shell/ssh-lan.conf` line when the file is already
readable at container start (`if [ -r "$SSH_LAN_CONF" ]`). Until then ssh never
looks at it — which reads exactly like "my override is being ignored".
ssh reads the file on every connection, so creating it and editing it both take
effect immediately — no container or session restart. Since v1.12.1,
`setup-lan-access.sh` writes the `Include ~/.config/devbox-shell/ssh-lan.conf`
line whether or not the file exists yet (ssh skips an `Include` whose file is
missing). Images up to and including v1.12.0 wrote that line only if the file
was readable at container start. There, a file created afterwards is ignored
until a restart, or until you run `bash /usr/local/lib/pi-devbox/setup-lan-access.sh`
in the container.
That looks exactly like "my override is being ignored". One case still needs
action: a file that exists but the container cannot read is left out, because
ssh would otherwise refuse every connection. The script then prints a warning at
start. Two other permission traps apply to this file as to any ssh config.
ssh rejects it if it is world-writable (`Bad owner or permissions`). And on native
Linux, the host user's uid must match the container's `developer` (1000).
#### Giving the container its own key for a peer
@@ -1289,7 +1297,7 @@ persisted volumes survived, and pi runtime wiring is intact:
```bash
./scripts/recreate-sanity-check.sh # auto-detects variant
./scripts/recreate-sanity-check.sh --expected-image-version 1.8.9 # assert the pi-devbox release tag
./scripts/recreate-sanity-check.sh --expected-version 1.3.0 # assert the pi coding agent version
./scripts/recreate-sanity-check.sh --expected-version 1.4.0 # assert the pi coding agent version
```
Those are **two different versions**, and the flags are not interchangeable:
@@ -1328,7 +1336,7 @@ resolved to `latest` at build time:
| Component | Pin | Where |
|---|---|---|
| pi | `1.3.0` | `ARG PI_VERSION` — `Dockerfile.variant` |
| pi | `1.4.0` | `ARG PI_VERSION` — `Dockerfile.variant` |
| pi-obsmem | `022da5e5554d04dfdc876ed7dac4a42c6b83dfd4` | `ARG PI_OBSMEM_REF` — `Dockerfile.variant` |
| pi-atelier | `v0.16.0` | `ARG PI_ATELIER_REF` — `Dockerfile.variant` |
| mempalace | `3.11.0` | `ARG MEMPALACE_VERSION` — `Dockerfile.base` |
@@ -51,8 +51,8 @@
# HOST-OWNED PEER POLICY (portable; keeps this image generic)
# Named LAN peers are facts about a *specific* host's network, not about the
# image — a roaming laptop sees different LANs. So we never bake peer names
# here. Instead, if the host bind-mounts ~/.config/devbox-shell/ssh-lan.conf
# (the same devbox-shell bridge dir used for shared aliases), we Include it
# here. Instead we Include ~/.config/devbox-shell/ssh-lan.conf (the host
# bind-mounts the devbox-shell bridge dir, also used for shared aliases)
# *before* ~/.ssh/config. That file holds the host's own jump overrides, e.g.
# Host pve pve-2 pbs-vm
# ProxyJump host
@@ -151,8 +151,22 @@ EOF
# Optional host-owned named-peer jump overrides (portable: lives on the host,
# not in the image). Included BEFORE ~/.ssh/config so its ProxyJump wins.
#
# Included even when the file does not exist yet. ssh skips an Include whose
# file is missing, and reads it on the next connection once it appears.
# Through v1.12.0 the Include was written only if the file was readable at
# container start. A file created later was then visible in the container but
# ignored, until a restart (EMB-3HXGNXJ0, 2026-10-18: its tor-ms22 override
# was skipped, so ssh fell back to a passphrase key and BatchMode failed).
# Only a file that EXISTS but cannot be read is left out. ssh aborts every
# connection on one ("Can't open user config file"), which would break all
# ssh, git included. A directory at that path is harmless (ssh ignores it).
# Measured with this image's OpenSSH 10.0p2; scripts/smoke-test.sh checks
# both cases.
SSH_LAN_CONF="${HOME}/.config/devbox-shell/ssh-lan.conf"
if [ -r "$SSH_LAN_CONF" ]; then
if [ -e "$SSH_LAN_CONF" ] && [ ! -r "$SSH_LAN_CONF" ]; then
echo "setup-lan-access: $SSH_LAN_CONF exists but is not readable, so it is not Included (ssh would refuse every connection). Fix its permissions, then rerun this script or restart the container." >&2
else
LAN_CONF_BLOCK=$(cat <<'EOF'
# Host-owned named-peer jump overrides (bind-mounted; edit on the host).
@@ -182,8 +196,17 @@ EOF
fi
fi
# Same rule as ssh-lan.conf above: Included even when ~/.ssh/config does not
# exist yet, so a file that appears after container start (a host provisioned
# after its first `docker compose up`) is read on the next ssh. Entries added to
# a file that already existed were always picked up: ssh reads Includes on every
# connection and the mount is live. Through v1.12.1 the line was written only if
# the file was readable at start. Only a file that EXISTS but cannot be read is
# left out, because ssh would then abort every connection, git included.
INCLUDE_BLOCK=""
if [ -r "${HOME}/.ssh/config" ]; then
if [ -e "${HOME}/.ssh/config" ] && [ ! -r "${HOME}/.ssh/config" ]; then
echo "setup-lan-access: ${HOME}/.ssh/config exists but is not readable, so it is not Included (ssh would refuse every connection). Fix its permissions, then rerun this script or restart the container." >&2
else
INCLUDE_BLOCK=$(cat <<'EOF'
# Your own target hosts. Scope reset to match-all so this Include applies to
+45
View File
@@ -696,6 +696,51 @@ exec_test "git core.sshCommand matches sidecar presence" '
else
[ -z "$cmd" ] || exit 1; echo "no sidecar, correctly unset"
fi'
# ssh-lan.conf is Included whether or not it exists at container start. Until
# v1.12.0 the Include was written only if the file was readable at start, so a
# file created later was silently ignored (EMB-3HXGNXJ0, 2026-10-18). ssh skips
# an Include whose file is missing, and picks the file up once it appears, with no
# restart. The exception is a file that exists but cannot be read: ssh then
# aborts every connection, so that case is still left out. 644 is set
# explicitly because ssh rejects a world-writable config file.
exec_test "ssh-lan.conf Included even if created after start" '
T=$(mktemp -d) && mkdir -p $T/.config/devbox-shell &&
HOME=$T DEVBOX_LAN_ACCESS=jump bash /usr/local/lib/pi-devbox/setup-lan-access.sh >/dev/null 2>&1 &&
grep -qx "Include ~/.config/devbox-shell/ssh-lan.conf" $T/.ssh-local/config &&
HOME=$T ssh -F $T/.ssh-local/config -G lanpeer 2>/dev/null | grep -qx "hostname lanpeer" &&
printf "Host lanpeer\n HostName 10.9.9.9\n" > $T/.config/devbox-shell/ssh-lan.conf &&
chmod 644 $T/.config/devbox-shell/ssh-lan.conf &&
HOME=$T ssh -F $T/.ssh-local/config -G lanpeer 2>/dev/null | grep -qx "hostname 10.9.9.9"
r=$?; rm -rf $T; exit $r'
exec_test "unreadable ssh-lan.conf left out (ssh would abort)" '
T=$(mktemp -d) && mkdir -p $T/.config/devbox-shell && : > $T/.config/devbox-shell/ssh-lan.conf &&
chmod 000 $T/.config/devbox-shell/ssh-lan.conf &&
HOME=$T DEVBOX_LAN_ACCESS=jump bash /usr/local/lib/pi-devbox/setup-lan-access.sh >/dev/null 2>&1 &&
! grep -q "ssh-lan.conf$" $T/.ssh-local/config &&
HOME=$T ssh -F $T/.ssh-local/config -G lanpeer >/dev/null 2>&1
r=$?; rm -rf $T; exit $r'
# ~/.ssh/config gets the same rule as ssh-lan.conf above: Included whether or
# not it exists at container start, except when it exists but cannot be read.
# Entries added to a file that already existed at start were always picked up
# (ssh reads Includes on every connection, and the mount is live). This covers
# only a file that appears after start, e.g. a host provisioned after its first
# `docker compose up`. Through v1.12.1 that file was ignored until a restart.
exec_test "~/.ssh/config Included even if created after start" '
T=$(mktemp -d) &&
HOME=$T DEVBOX_LAN_ACCESS=jump bash /usr/local/lib/pi-devbox/setup-lan-access.sh >/dev/null 2>&1 &&
grep -qx "Include ~/.ssh/config" $T/.ssh-local/config &&
HOME=$T ssh -F $T/.ssh-local/config -G ownpeer 2>/dev/null | grep -qx "hostname ownpeer" &&
mkdir -p $T/.ssh && printf "Host ownpeer\n HostName 10.8.8.8\n" > $T/.ssh/config &&
chmod 600 $T/.ssh/config &&
HOME=$T ssh -F $T/.ssh-local/config -G ownpeer 2>/dev/null | grep -qx "hostname 10.8.8.8"
r=$?; rm -rf $T; exit $r'
exec_test "unreadable ~/.ssh/config left out (ssh would abort)" '
T=$(mktemp -d) && mkdir -p $T/.ssh && printf "Host x\n" > $T/.ssh/config &&
chmod 000 $T/.ssh/config &&
HOME=$T DEVBOX_LAN_ACCESS=jump bash /usr/local/lib/pi-devbox/setup-lan-access.sh >/dev/null 2>&1 &&
! grep -qx "Include ~/.ssh/config" $T/.ssh-local/config &&
HOME=$T ssh -F $T/.ssh-local/config -G ownpeer >/dev/null 2>&1
r=$?; chmod 600 $T/.ssh/config; rm -rf $T; exit $r'
# The vendored mempalace snapshot is refreshed MANUALLY per release (see
# rootfs/usr/local/share/pi-devbox/skills/VENDORED.md). Through v1.8.4 it also
# silently SHADOWED the live skillset copy, so staleness was invisible — and the