Skip to content

Release v2.59.0 - #6356

Merged
atomantic merged 281 commits into
releasefrom
main
Sep 6, 2026
Merged

Release v2.59.0#6356
atomantic merged 281 commits into
releasefrom
main

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Release v2.59.0

Released: 2026-09-06

Highlights

Coding-agent providers & review harness

  • Added the Pi coding agent as a full provider — model picker, review runner, and harness catalog entries.
  • The Codex harness can now run on a local LM Studio or Ollama model, completing account-agnostic local hosting for that harness.
  • The CoS Codex picker now offers the signed-in ChatGPT account's real model catalog, and every model picker now routes through one account-aware resolver instead of divergent per-surface logic.
  • OpenCode gained an attachable Stage 3 pr-reviewer recipe, exposing a live shell session in its agent card.
  • Fixed a stray unrecognized-model telemetry line leaking into Stage 2 reviewer stderr, and shrank the pr-reviewer Stage 3 prompt from ~400KB to ~50KB.

Video generation

  • Added Episode Composer for continuous multi-scene video, backed by a new script-to-beats compiler and a deterministic video-prompt linter that guards against malformed clip input.
  • Added Reactor.inc fast-h3 and fal.ai queue REST API backends to MediaGen and FableLoom.
  • Reactor renders now provision their runtime automatically on first use and tolerate slow downloads instead of failing outright; EXIF orientation is honored when fitting a starting frame.
  • Fixed a cluster of video-hold and Remix-handoff bugs: held cohorts staying aligned with runtime model selection, stranded handoffs waiting correctly for Retry, and video holds surviving sharing imports and damaged snapshots.

Eidoverse

  • Added federated guest teleportation and Mind chat, so a Mind can visit another host's Eidoverse world.
  • Objects can now be explained and given explicit display aliases, and a hosted world can name and describe its own PortOS host.
  • Landscapes and departure navigation are now preserved correctly, and PortOS Commons ships with a grassy pedestrian park.

Model comparison & AI Providers

  • Added reasoning-effort curves, chart axis scaling, and stretch controls to the model comparison view, plus AA-catalog cost sync so hand-entered costs no longer masquerade as AA-published.
  • Added detection and a setup prompt for federated LLM hosts.

Themes

  • Added the Kestrel Neon theme family and brought the other eight themes up to the same visual fidelity, backed by a shared, token-driven effects layer.

Reliability and infra

  • gh-backed background checks (update checks, backoff-gated calls) now back off on consecutive failures instead of retrying every tick, while a manual "check now" is exempted from that backoff.
  • Fixed a data-loss bug where the data.reference seed could wipe CoS settings on an install crossing the Split durable CoS config out of data/cos/state.json into its own file #6182 update.
  • LoRA downloads now share one download slot instead of racing independent ones, and raw filesystem writers under data/ were routed through guarded primitives.
  • Pinokio duplicate model weights can now be reclaimed via verified hardlinks, bounded to safe, valid paths.

Full Changelog

Full Diff: v2.58.0...v2.59.0

atomantic and others added 30 commits September 4, 2026 09:41
- Ignore a stale preview response if a newer edit already started a
  later lint request (request-generation counter).
- Gate Queue episode on !previewLoading too, so editing a passing draft
  into a failing one can't queue against the stale old preview.
- Apply a FableLoom scene import that arrives after mount — VideoGen's
  getLoom() fetch resolves after EpisodeComposer's initial render, so
  the lazy useState initializer never saw it.
- Clear the FableLoom import state when the composer closes, so a later
  open with no loomId/episodeId doesn't inherit a stale import.
- Default a new cast/location bible entry's id to '' instead of a
  throwaway uuid, so an untouched id field can't silently become the
  bible key.
- Disable the Cast/Locations editors while queuing/streaming, matching
  the scene editor's busy-gating.
- Tighten the teleplay cue-line length regex to match its 60-char guard.
feat: add Episode Composer UI for continuous multi-scene video
fix(cos): prevent CoS panel width blowout when non-canvas avatar is active
… update

update.sh runs scripts/setup-data.js (which copies every data.reference file
the install is missing) before scripts/run-migrations.js. The commit that added
migration 339 — lift the durable CoS config out of data/cos/state.json into
data/cos/config.json — also added a data.reference/cos/config.json seed. So on
the single update that crossed that commit, setup-data wrote the seed first,
339 saw its output "already exists" and no-opped, loadConfig() preferred the
seed over its legacy state.json read, and the next saveState() stripped the
real config slice off disk permanently.

The seed carried alwaysOn: false where DEFAULT_CONFIG carried true, so the
visible symptom was CoS refusing to start after an update until started by
hand, with concurrency caps and Persistent Mind grants back at defaults.
Installs that updated before or after that commit are unaffected.

- Delete the seed. data/cos/config.json is derived, not seeded — loadConfig()
  already returns DEFAULT_CONFIG when it is absent, so the defaults now have
  one source instead of two that drifted.
- Add scripts/lib/migrationOwnedPaths.js, honored by both setup-data copy
  paths, with a test that fails if such a seed reappears.
- Add migration 340: where config.json is still the shipped seed, redo 339's
  lift from state.json, or from the newest quarantined state.json.corrupted.*
  when the live slice is already gone, or delete it so the in-code defaults
  apply. A config.json carrying real settings is never touched — but if a
  quarantined backup holds settings it lacks, the migration names that file so
  a user who re-entered settings by hand can recover the rest.
- Default DEFAULT_CONFIG.alwaysOn to false. It read true only because the seed
  masked it; with the seed gone, a fresh install would start CoS and its
  autonomous jobs unprompted, which "No cold-bootstrap LLM calls" forbids.
- Record the rule in AGENTS.md: a migration that derives data/<x> from existing
  records ships no seed, and gates on the presence of its input rather than the
  absence of its output.

Reordering update.sh to run migrations first was considered and rejected:
run-migrations creates data/, which would send fresh installs down setup-data's
existing-install branch and skip the __PORTOS_ROOT__ substitution, and setup.sh
/ setup.ps1 / npm run setup do not run migrations at all.
issue-reconcile's daily deterministic pre-step now also unblocks issues:
one whose body names its dependency as "Blocked by #N" (documented in the
portos-file-issue skill) has the blocked label stripped, with a comment,
once every named issue is closed. No coordinator agent or LLM call — the
decision is mechanical, mirroring the existing releaseAbandonedClaims step.

Forge-agnostic (GitHub via gh, GitLab via glab). Also lifts the shared
open/closed state normalizer out of layeredIntelligence/forgeFiler.js into
server/lib/forgeIssueState.js so the new module doesn't need a
cross-subsystem import.
…d seed, fix doc drift

The fallback reviewer found no live defect but four gaps, all fixed here:

- docs/features/chief-of-staff.md documented alwaysOn's default as true; the
  seed deletion makes the in-code false the only value a fresh install reads.
- Nothing pinned DEFAULT_CONFIG.alwaysOn = false — now the only thing keeping a
  never-configured install from auto-starting CoS and every work-generation flag
  that defaults on. The nearest existing assertion had also gone vacuous: it
  stored alwaysOn: false in a legacy slice and asserted false, which now passes
  even if the legacy fallback is deleted. Store true there instead, and add a
  test on the default itself.
- Every migration-340 test wrote RETIRED_SEED as its own fixture, so the
  equality guard compared the constant to itself and would pass for any value it
  held — a mistranscribed byte would make the repair no-op on 100% of affected
  installs with CI green. Check in the seed extracted verbatim from ed19786 as
  an independent witness and assert the constant against it.
- rm(configPath) without force: an ENOENT race would propagate out of
  runMigrations() and fail server boot, when "already gone" is what that branch
  wants anyway.
fix: stop the data.reference seed from wiping CoS settings on the #6182 update
… backreference splice duplicating it

`{reviewChecklist}` inlined the entire `/do:review` command with every lib
include (~260KB: argument parsing, five reviewer loops, issue filing, PR
posting) into a sandboxed, network-less stage that can use none of it, and
the placeholder was substituted with a STRING replacement, so a `$`+backtick
inside that body spliced everything before the placeholder back in — seven
copies of the stage prompt in one body. On the local Claude wrapper the
resulting ~100K-token prefill outlived every client timeout
(agent-e057cca7, 2026-09-04).

The public-review actions stage now receives only the five review lenses
(~39KB), and every placeholder uses a function replacer.
…s, the ceiling API_TIMEOUT_MS never reached

A local daemon sends no bytes at all until prefill completes, and against
that silence Claude Code v2.1.260 has four independent ceilings. The one
that actually cancelled every Stage 3 attempt at exactly 6 minutes (the
daemon logged `500 | 6m0s | POST /v1/messages` per retry) is the Bun
runtime's own fetch timeout, which Claude Code only disables when
`API_FORCE_IDLE_TIMEOUT=0` — so PR #6117's `API_TIMEOUT_MS` raise changed
nothing. Measured against a fake stalled /v1/messages on loopback: with
the flag unset or `1` the client re-sends every 361s regardless of the
other knobs; with `0` it waits out the prefill.

Sets that flag plus the two post-first-byte idle watchdogs (30-minute
ceiling) for local-backed Claude harnesses only, and allowlists them
through both public-review env boundaries.
…nd from a permission dialog nobody can approve

Two ways agent-e057cca7 hung for hours while every reaper saw a busy
session:

- The provider kept failing the same request and Claude Code kept
  re-sending it (`API error · Retrying in 0s · attempt N/10`); the repaint
  reset the idle clock each time. `createRetryStallGate` tracks one
  request's retry ladder and fails the run over to a fallback provider once
  the banner has advanced twice across ten minutes — a verdict decided at a
  sighting, never by the clock, so a fast ladder that then succeeded is
  left alone.

- Once prefill completed, the local model asked to Read an absolute path
  outside its worktree and the hardened `acceptEdits` recipe painted a
  permission dialog for a human who was not there. `createToolPermissionGate`
  declines it (arrow-down to the last "No" option, whose number varies by
  tool) and nudges the quiet session back to work; eight declines fail the
  run. The pipeline hand-off also stops pointing a stage at
  data/cos/agents/<prev>/output.txt — outside every worktree — when that
  output is already inlined in full.
- `{reviewLenses}` is its own placeholder on the Stage 3 body instead of a
  promptKey-keyed branch inside `{reviewChecklist}`; the stage body is
  unpersisted, so only the integrity snapshot moves.
- The OOM nudge and the tool-permission nudge share one
  `createSilenceNudgeGate` (cooldown, settle, arm window, budget) instead of
  two copies of the state machine and its constants.
- The tool-permission question pattern is shared with the post-mortem
  analyzer's AWAITING_INPUT_MARKERS so live and after-the-fact detection
  cannot drift.
- The pipeline hand-off never points a stage outside its worktree once the
  producer handed over the output inline; a clipped inline says so.
- Decline keystroke uses SUBMIT_KEY; cooldown is checked before the tail is
  rebuilt; comments trimmed to the load-bearing facts.
… through the cooldown, watch only claude sessions

- The permission gate's exhaustion budget is counted in declines, so a build
  that lets the model continue after a rejection (never quiet, never nudged)
  still caps at eight instead of looping to the max-runtime ceiling.
- The screen tail keeps accumulating through the repaint cooldown, so a
  different dialog painted seconds after a decline is still seen and
  declined once the cooldown lifts.
- createSilenceNudgeGate keeps the OOM gate's original order: a sighting
  while a nudge is pending neither restarts the window nor refreshes the
  cooldown, so a box that outlasts its window can re-arm once it stops.
- Only claude sessions are watched for the dialog (it is Claude Code chrome;
  another vendor's agent would only ever echo it).
- `permission-prompt-loop` is registered in COMPLETION_REASON_ANALYSES so the
  post-mortem does not scrape the dialog chrome from the transcript.
Unstick the pr-reviewer Stage 3 agent: lean prompt, real local-daemon timeouts, and TUI recovery for retry ladders and permission dialogs
…e-2 reviewer stderr

The claude-ollama / claude-ollama-tui providers redirect the real Anthropic
claude binary's ANTHROPIC_BASE_URL at a local Ollama endpoint serving models
like gemma3:27b. The binary's own SDK doesn't recognize those model names and
logs a one-time-per-model `[claude-code:unrecognized_model]` warning to
stderr — harmless telemetry, but confusing in reviewer output since it names
"claude-code" while running under an Ollama-backed OpenCode/claude-ollama
review stage. Filter it out at both places stderr is tagged and surfaced
(agentCliSpawning.js and cos-runner/index.js).
suppress claude CLI unrecognized-model telemetry from reviewer stderr
…gent card exposes a Shell session (#6238)

A Stage 3 ("PR Code Review & Actions") run on an OpenCode-backed TUI provider was
always forced headless: the OPENCODE vendor row declared no sandboxed-actions
recipe, so supportsTuiPublicReviewActionsProvider answered false and the agent
card showed "No shell". The headless argv is a one-shot `opencode run …`, which
cannot become an interactive session by dropping flags the way Claude's recipe
does.

Add a sandboxed-actions recipe to the OPENCODE row. Headless runs keep today's
`run`-prefixed argv byte-for-byte; the attachable (`tui: true`) shape is the
bare binary — OpenCode's real TUI entry point — with `--agent build` pinned and
`-m` namespaced by the same prefixOpencodeModel call, and the spawner pastes the
prompt as it does for every other TUI. Verified under node-pty against a local
Ollama backend: the pasted prompt renders, Enter submits it, and the build
agent completes a tool call. The recipe matches any spawnable OpenCode binary
(MTPLX and gateway backends included), unlike the Ollama-only no-tool gate.

The recipe adds no sandbox — the headless run already had full tool access with
the disposable worktree as isolation — so it carries `enforces: false` and the
schedule UI keeps reporting the choice as worktree-only rather than OS-sandboxed.
…ch argv builders a recipe supplies

A recipe now carries `spawnArgs` (headless) and/or `tuiSpawnArgs` (attachable)
instead of `tui: true` / `enforces: false` flags: attachable means a row
supplies `tuiSpawnArgs`, enforced means it supplies `spawnArgs`. OpenCode's
actions row supplies only the attachable builder, so it falls through to the
ordinary headless argv, stays worktree-only in the schedule UI, and is left out
of the 'install one of these' suggestion — all structurally, with no per-row
opt-outs. Also homes OPENCODE_BUILD_AGENT beside OPENCODE_PUBLIC_REVIEW_AGENT,
composes isLocalOpencodeProvider from the shared binary matcher, and factors
the OpenCode model-append/envelope helpers so the two argv shapes share them.
feat: avoid opening duplicate tabs in CDP browser on update
…vider-preset

feat: add a disabled-by-default Slotstream AI Providers preset
…5992)

- Add machine-local orchestration profiles CRUD endpoints in server/routes/settings.js and store backing in server/services/orchestrationProfiles.js
- Ship built-in profiles in server/lib/orchestrationProfile.js (heavy-planner, max-deliberation)
- Add Orchestration tab in Settings (/settings/orchestration) with full profile editor and role assignment controls
- Add orchestration profile picker in CoS TaskAddForm with direct/orchestrated toggle and per-role provider/model/effort overrides
- Add orchestration profile picker in /ai runner panel and overflow menu link
- Update API route catalog and tests
feat: add client orchestration profile pickers and settings library (#5992)
A "resolve and merge" trigger on a PR whose branch was still checked out
in a /do:next claim-* worktree retried git worktree add against it for 5
cooldown cycles and then blocked permanently, because
findAdoptableWorktreeForBranch's requireAgentId root gate rejected the
claim-shaped directory name before the existing allowLiveClaim carve-out
(used by branchReconcile's dispatch side) ever got a chance to apply.

Fix the root-gate check in worktreeOwnershipReason to let a claim-shaped
id through when the caller opts in, and thread allowLiveClaim from
findAdoptableWorktreeForBranch through agentWorkspacePrep's review-loop
and PR-remediation follow-up paths (isNonCommittingCoordinatorTask), so
these tasks take over the branch's existing worktree and land the merge
instead of stalling on it.
Give OpenCode an attachable Stage 3 pr-reviewer recipe so its agent card exposes a Shell session
allow resolve-and-merge follow-ups to adopt a live /claim worktree
…ail-safe

- evaluateMemoryPressurePolicy: track sustained-pressure samples against the
  hysteresis exit threshold (not the base threshold) once wasUnderPressure is
  true, so memory sitting in the dead-band still counts toward the sustained
  window instead of starving eviction indefinitely.
- reapIdleDaemons idle-timeout pass: skip a daemon that's already stopped
  instead of re-stopping it, which was overwriting a prior pressure-release
  reason and could preempt the pressure-aware pass on the same tick.
- reapIdleDaemons idle-timeout pass: fail-safe isPinned reads to true (pinned)
  on error, matching the pressure-aware pass, so a flaky settings read can't
  stop a keepLoaded daemon.
atomantic and others added 29 commits September 5, 2026 16:14
Reclaim duplicate Pinokio model weights from Models Status
updateChecker's 30-min scheduler interval always exceeds the 15-min
backoff cap #6345 hardcoded, so the cooldown from a failed release
check expires before every subsequent tick and never actually
suppresses a retry -- a silent no-op for its only production caller.

execGh now takes an optional backoffMaxMs override; branch-reconcile
and the other pollers keep the default (appropriate for their much
faster tick rate), and updateChecker passes 3x its own interval so
repeated failures actually widen the gap between real attempts.
feat(models): add axis scaling and chart stretch controls to model comparison
fix: automatically provision Reactor on first render
feat: add warm sequential video render batches
…racter cap

reactor.inc's fast-h3 rejects a prompt over 800 characters outright rather
than truncating it, so "Enhance with AI" reliably produced a richer prompt
the render then refused — and the same for "Prompt from media", which writes
into the same field.

Both AI paths now receive the backend's budget, state it in the LLM prompt as
a hard limit, and clamp the answer on a sentence or word boundary if the model
overshoots anyway. The clamp is reported rather than silent, so a trimmed
prompt reads as "trimmed to fit the render limit" instead of the AI losing
detail on its own.

The budget VideoGen sends is the cap MINUS the style-preset prefix, since that
prefix is part of what PortOS submits — enhancing to exactly 800 characters
would still be rejected once the preset is prepended.

Adds clampToCharLimit() to server/lib/textUtils.js: unlike the existing
marker-appending helpers (clampText, truncateForTelegram), it appends nothing,
because a marker would count against the same renderer cap.
feat(media): keep AI-enhanced prompts inside the render backend's character cap
The Quick Task dashboard widget squeezed its description textarea down to
one character per line. TaskAddForm's compact row keyed its layout off
viewport breakpoints, but a dashboard tile is ~250px wide on a 2560px
screen — so every `sm:` was unconditionally true and pinned the wide
side-by-side row into the narrowest column. The expanded options and the
reviewer table (a ~32rem grid) overflowed the tile the same way.

Both components now declare `@container` on their own root and key their
layout off container queries. The container lives on the component rather
than the grid cell because ReviewerPicker also renders in drawers, the
settings page and the schedule panel, none of which provide a container
ancestor — a cell-level one would leave those stuck in the stacked form.

Also guards the flex/grid items that hold selects with `min-w-0` so they
shrink instead of overflowing, and wraps the rows whose contents can't.
The hover-vs-touch `md:` affordances stay viewport-based; they really are
about the device.

Records the convention in the dashboard AGENTS.md so the next page
component reused as a widget doesn't reintroduce it.
@md (448px) sat well below the viewport `sm:` (640px) it replaced, so a
phone would have gone horizontal where it used to stack — leaving the
textarea ~200px. @XL (576px) is roughly that old threshold once page
padding is subtracted, and it's where the textarea, the 10rem app picker
and the Add button each still get usable width.

Same reasoning moves the provider/model/effort row to @lg: two 10rem
selects plus a flexible model select don't fit in 448px.
fix(ui): lay out the task form by container width, not viewport
Pi's provider entries carry no local-runtime/gateway marker and pin
modelsArgs, so usesHarnessCatalog() now correctly includes pi-cli/
pi-tui alongside the other self-listing harnesses — the shipped-list
gate test just hadn't been updated for it, which failed CI.
Add Pi coding agent CLI/TUI providers and review runner
Migration 343-persistent-mind-accepted-routes gated on schemaVersion <= 7
and expected to run before 342-persistent-mind-thinking-presets bumped the
mind's schema straight to 8. Filename sort runs 342 first, so on any
install applying both migrations in one pass, 342 already stamped
schemaVersion=8 before 343 checked its `> 7` gate — skipping the
missing-route sentinel that keeps stale queued messages from resuming
with an unverified thinking preset. Renaming it to 342-persistent-mind-
accepted-routes.js sorts it before the thinking-presets migration.
…ndorSpawnConfig

The second `supportsPublicReviewPosture` check after the tui/spawnArgs
branches could never fire: the top-of-function check already guarantees
recipe.spawnArgs exists whenever this code is reached, so the block and
its "may fall through" comment described a path that no longer exists.
Also corrected a stale comment claiming OpenCode's sandboxed-actions
recipe is attachable on every backend — it was withdrawn from that
posture entirely (OpenCode ships no OS sandbox), matching the existing
test that rejects both its headless and attachable forms.
If the sink's write callback rejected (e.g. exceeding the per-member size
cap) before createWriteStreamGuarded's promise resolved, fail() ran with
out still null so out?.destroy() no-opped. The promise then resolved and
unconditionally assigned the now-orphaned stream to out, leaking its fd
and the staged temp file for the rest of the process lifetime.
The GitHub zombie-issue prompt already dropped the issue title to avoid
feeding attacker-controlled text to the LLM coordinator, but the GitLab
path still interpolated z.title raw — GitLab has no actor-trust screening
equivalent to forgeActorTrust.js, so it had strictly weaker hardening
than GitHub for the same prompt-injection surface.
generateVideo accepted and recorded negativePrompt in job history, but
buildRequestBody never received it, so it had no effect on generation —
continuous-video episodes routed through fal.ai silently ignored a
user's negative prompt. Fold it into the prompt as an "Avoid:" clause,
the same fallback grok.js uses for a provider with no native field.
@atomantic
atomantic merged commit 52a913a into release Sep 6, 2026
13 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants