Skip to content

v0.16.0

Choose a tag to compare

@github-actions github-actions released this 25 Aug 22:00
· 65 commits to main since this release
775e231

An @-mentioned path arrives unambiguous

The composer sends @ui/src/App.tsx — a path relative to the browser root, which is
also the agent's own sandbox root. That should be enough. But there is no structured
wire field for a mention: the model reads it as plain text and resolves it itself, and a
bash call earlier in the turn can leave it assuming a different working directory. The
agent mis-resolved an @-mentioned path roughly half the time.

Every mention that both resolves under the browser root and exists on disk is now
rewritten to its absolute form before the prompt reaches the runtime. A mention that is
not a real path — @someone in prose, a typo — is left exactly as it was typed;
resolving alone was not enough to decide that, because it deliberately tolerates a
missing tail, and a typo would have become a confident absolute path to nothing.

You never see it. The bubble carries the text you typed while the turn runs, and the
same substitution is reversed when a reconnect replays the conversation — the SDK
persists what reached the model, so without that reversal a page reload would have
leaked the absolute path into the bubble that had just shown the relative one.

A refused Work Plan call says which field it refused

v0.15.0 gave every work_plan operation its own typed schema. That fixed what the model
could read. It did not fix what the model is told when it gets something wrong, and a
ten-branch union is exactly where that goes bad: the whole schema is validated, so every
branch that fails contributes its complaint.

Asked for a five-task plan with dependencies, a small model wrote dependsOn on its
creation tasks — the natural way to express a plan that has dependencies — and got back:

Validation failed for tool "work_plan":
  - root: must not have additional properties
  - action: must be equal to constant
  - root: must not have additional properties
  - action: must be equal to constant
  - tasks.2: must not have additional properties
  - plan: must have required properties plan
  - root: must not have additional properties

Six of those seven lines describe actions the model never asked for. The word
dependsOn appears nowhere. That model repaired by guessing, at the cost of three extra
calls; a smaller one gave up on the plan entirely.

The tool now publishes one object schema whose action is an enum, and each
operation's requirements are checked where they can be named:

tasks[2].dependsOn is not accepted
unknown dependency: task_1
action=remove_task requires taskId

And creation accepts dependencies, where a model writes them:

{ "action": "create", "title": "Ship dark mode",
  "tasks": [{ "id": "a", "title": "Add the theme tokens" },
            { "id": "b", "title": "Wire the toggle", "dependsOn": ["a"] }] }

A dependency may name a task declared further down — a plan is written in the order the
work reads, not in dependency order. update_task also accepts its changed fields
beside taskId rather than only inside changes. The tool ships a worked example in
its guidelines, which it never had.

Persisted plans are unchanged, and every operation that worked before works the same.

The bubble tells the truth about what was sent, and what failed

Two things a reader was being told wrongly.

A prompt showed nothing until the server accepted it. The user bubble is broadcast
only once the runtime takes the prompt — deliberately, so a refused prompt leaves no
ghost behind — and acceptance waits for session creation, runtime start-up, and, on a
loaded provider, the request being taken. At the start of a conversation that was
seconds in which the composer had emptied and the transcript had not moved: long enough
to wonder whether the message was lost. The prompt now appears the moment it is sent,
dimmed, and gives way to the real bubble when it arrives. A refusal takes it away with
it.

A provider failure showed its markup. Providers do not fail in JSON — a proxy in
front of one answers with a whole HTML document, and that document was reaching the
bubble intact:

504 <html><body><h1>504 Gateway Time-out</h1> The server didn't respond in time. </body></html>

The words are recovered and the markup dropped, where the text enters the protocol
rather than where it is drawn — so a reopened session reads the same sentence the turn
showed. A message somebody wrote to be read is never reworded.

update runs the npm this platform can execute

On Windows, npm is npm.cmd, a batch file that CreateProcess cannot execute. Every
one of the three places that reach for npm — the registry probe, the global-root probe,
and the installer itself — failed with ENOENT before npm ever started.

It landed on exactly the deployment this code exists to serve: an air-gapped site whose
Nexus address lives only in its .npmrc, the file npm alone reads. The probe's failure
was swallowed, the public registry was used instead, and the operator was told:

[pi] could not check for updates: getaddrinfo ENOTFOUND registry.npmjs.org

Had the check passed, the install would have failed too, reported as the installer exited with 1 — npm blamed for a process that never ran.

Nothing needs to be configured on the affected host now: npm config get registry
answers with the internal address, and npm install -g reads the same .npmrc by
itself. A check that does fall back to the public registry now says why: getaddrinfo ENOTFOUND registry.npmjs.org (asked the public registry: spawn npm ENOENT).


Upgrading: nothing to change. Persisted Work Plans, sessions and configuration load
unchanged. An operator on Windows behind an internal registry should upgrade with
npm install -g pi-outpost this once — the copy they are running is the one that cannot
fetch. Running pi-outpost from npm needs Node ≥ 24; the executables attached below
require Node ≥ 26 to build.