Conversation
- 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
…canvas avatar is active
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.
…applies (local-only endpoints)
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.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v2.59.0
Released: 2026-09-06
Highlights
Coding-agent providers & review harness
Video generation
Eidoverse
Model comparison & AI Providers
Themes
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.data/were routed through guarded primitives.Full Changelog
Full Diff: v2.58.0...v2.59.0