Rebase fork onto upstream 0.9.1 with durable task coordination - #24
Merged
William Zhang (phytal) merged 128 commits intoSep 4, 2026
Merged
Conversation
…rimeIntellect-ai#1387) * feat(daemon): add rlm spawn ledger module * feat(daemon): record rlm spawns, renames, and deletions in the spawn ledger * test(daemon): cover rlm spawn ledger semantics, seeding, and wiring * fix(daemon): harden rlm spawn ledger reads, writes, and depth checks - enforce spawn invariants at append (never write what the reader rejects) - verify depth monotonicity only between ledger-known depths; drop+log a contradictory edge instead of failing the whole family - tolerate one torn final line (no trailing newline) on read; truncate it before the next append; interior malformed lines stay fail-closed - skip v:1 records with unknown ops (forward compat); v!==1 fails loudly - shared registry seed source (header-id read, tolerant LWW registry parse) - canonicalSessionPath for all path keys; realpath the sessions dir - flush() to await durable appends; document multi-writer O_APPEND reality * feat(daemon): serve supervisor siblings from the spawn ledger - supervisor holds its own ledger instance; the three catalog.siblings call sites (named create, saved rename reservation, saved-name check) now read ledger-backed siblings - offline rename_saved_session at the supervisor appends a ledger rename - worker spawn appends are awaited before admission returns (no self-heal exists for a lost spawn record) - ledger delete is appended after the registry tombstone succeeds * test(daemon): cover ledger hardening and supervisor siblings wiring * fix(daemon): await and self-heal the rlm ledger delete - recordRlmSubagentDeletion awaits the ledger delete (tombstone-first ordering kept) - a retried deletion over an existing tombstone finishes a ledger delete lost to a crash instead of leaving a permanent ghost live edge - TODO at the spawn-append failure log: revisit failing admission once the ledger is the messaging authority * fix(daemon): address PR PrimeIntellect-ai#1387 bot findings on the rlm spawn ledger - await the ledger rename at the active-session rename write point so a rename is durable before its name reservation is released - sessionRow strips header-claimed parentSessionPath/rlmDepth: topology in ledger rows is exclusively ledger-sourced (fork headers no longer leak a parent onto root rows) - seeding is atomic: collect all records, publish via temp file + rename; an interrupted seed leaves no ledger file and re-seeds next time; a racing live append wins and suppresses the seed - siblings() falls back to a lone root-shaped row when the target's edge was reconciliation-dropped (parent file gone) but the child file exists - drop the unused public rlmLedgerFamily/rlmLedgerSiblings wrappers on AgentDaemon (tests use the ledger via daemon internals) * fix(daemon): byte-exact torn-tail repair and no-clobber seed publish - truncateTornTailSync works on raw buffers with byte offsets: string indices diverge from byte offsets on multi-byte UTF-8 names, so the old truncate could cut into a preceding valid record and poison the ledger; also hardened cross-process with a byte-stable double read and same-fd fstat/ftruncate (residual race stays documented) - seed publish uses linkSync (EEXIST => live append wins, seed dropped) instead of existsSync+renameSync, whose clobbering rename could lose a racing append with no self-heal * fix(daemon): bound the repair-path read and add a link-less seed publish fallback - readAllSync and the torn-tail repair enforce RLM_LEDGER_MAX_BYTES before any file-sized allocation, throwing the same loud bounded-read error as replaySync (outside the swallowing repair try-block) - seed publish falls back to check-then-rename when linkSync fails with anything but EEXIST (filesystems without hard links); EEXIST still means the racing live append wins; fallback path is logged * fix(daemon): never publish a seed that exceeds the ledger read bounds A seed past RLM_LEDGER_MAX_BYTES/RECORDS would publish a ledger every replaySync refuses to read. Check the single serialized payload against both bounds before publishing and skip seeding entirely (flat families, the documented degradation mode) — logged, not thrown, so the guard cannot recreate the seedAttempted-sticks failure shape.
…dger (PrimeIntellect-ai#1390) * feat(daemon): add per-child rlm subagent display files * feat(daemon): serve passive rlm subagents from the ledger and stop writing registries * refactor(daemon): drop the unconsumed catalog siblings walk * test(daemon): cover display files and legacy registry metadata fallback * refactor(daemon): share rlm subagent metadata field spreading * refactor(daemon): derive the legacy registry entry type from the passive entry * fix(daemon): resolve deleted-child paths for job cleanup and harden display writes * fix(daemon): fail admission on lost spawn records and never clobber-publish seeds
…alogs (PrimeIntellect-ai#1445) Commits the current `npm run generate-models` output. 92 models added, 33 removed (net 1163 -> 1222) across 14 providers. Highlights: DeepSeek V4 Flash/Pro dated snapshots (workers-ai, fireworks, huggingface), gemini-3.7-flash (google, copilot), grok-4.5/4.6 and kimi-k3 on copilot, glm-5.3 on opencode-go, and the GPT-5.x family on cloudflare-ai-gateway; retired claude-opus-4-1 aliases, gemini-2.0-flash, and ling-3.0-flash routes. The build regenerates this file from live catalogs anyway; committing keeps the checked-in file from drifting further and keeps local keyless test runs representative.
…tellect-ai#1388) * feat(coding-agent): add ctrl+j toggle to expand edit diffs independently of tool output * feat(coding-agent): make ctrl+j sole owner of edit-diff visibility and hint the summary line * fix(coding-agent): stop duplicating the ctrl+j hint on collapsed edits Collapsed built-in edits showed the hint twice: on the edit header and on the summary line. The header hint now renders only when the diff is expanded (where no summary line exists); collapsed rows keep the single hint on the summary line. * fix(coding-agent): keep the ctrl+j hint visible while no summary line renders Gating the header hint on expansion assumed the collapsed summary line always carries the cue, but that summary only mounts once a successful result with a countable diff lands. During the preview-only window and on error rows the diff was expandable with no visible hint. The header now keeps the hint whenever the summary line is absent (mirroring its mount condition) and yields it once the summary renders, so exactly one hint is visible in every state. * fix(coding-agent): advertise the collapse key on expanded ipython diff headers * fix(coding-agent): move the expanded-diff collapse hint to the truncated cell header
…ff (PrimeIntellect-ai#1392) * feat(coding-agent): add ctrl+j toggle to expand edit diffs independently of tool output * feat(coding-agent): make ctrl+j sole owner of edit-diff visibility and hint the summary line * feat(coding-agent): always show the edit summary line and render the diff inline beneath it * fix(coding-agent): suppress edit summary on failed edits, unify summary path formatting, trim dead exports * fix(coding-agent): color the edit header as error when execution fails after a successful preview * fix(coding-agent): always show the ctrl+j hint on edit summary rows The edit-diff hint was threaded through showExpandHint, the flag that restricts the ctrl+o hint to the latest tool row. Since the agent almost always runs more tools after an edit, edit rows stopped being "latest" immediately and the ctrl+j hint effectively never appeared. The ctrl+j hint now renders on every edit summary row, matching the always-visible thinking (ctrl+t) and agent-message (ctrl+p) hints. The latest-row gating still applies to the ctrl+o hint on the header line. * fix(coding-agent): stop duplicating the ctrl+j hint on collapsed edits Collapsed built-in edits showed the hint twice: on the edit header and on the summary line. The header hint now renders only when the diff is expanded (where no summary line exists); collapsed rows keep the single hint on the summary line. * docs(coding-agent): correct the ctrl+j hint comment to match showHint The hint renders on every tool row, but within a row only on the last file's summary line — the comment claimed every summary row. * fix(coding-agent): keep the ctrl+j hint visible while no summary line renders Gating the header hint on expansion assumed the collapsed summary line always carries the cue, but that summary only mounts once a successful result with a countable diff lands. During the preview-only window and on error rows the diff was expandable with no visible hint. The header now keeps the hint whenever the summary line is absent (mirroring its mount condition) and yields it once the summary renders, so exactly one hint is visible in every state. * fix(coding-agent): advertise the collapse key on expanded ipython diff headers * fix(coding-agent): move the expanded-diff collapse hint to the truncated cell header * fix(coding-agent): stabilize summary-line truncation across the ctrl+j toggle and reuse countChangedLines
PrimeIntellect-ai#1367) * fix(coding-agent): say 'type to search sessions' in the agents view splash * refactor(coding-agent): drop the dead 'type to start' splash fallback * refactor(coding-agent): change the splash hint fallback in place instead
…ntellect-ai#1449) * refactor(daemon): extract a safe lease-renew loop for the shutdown admission RenewableRegistryRecord owns the shutdown-admission lease-renew loop: the unref()'d interval, a single-flight refresh shared by timer-fired and direct assertOrRenew calls (whose rejection reaches direct awaiters), a stopped/lost re-check inside the guarded section so a renew that loses a race with stop() can never write, and disposal. This replaces DaemonShutdownAdmission's hand-rolled loop, which had a latent race: direct assertOrRenew calls bypassed the single-flight slot, so release() could return while a renew was still queued on the registry guard and the record could be rewritten after removal. Public semantics (assertOrRenew, release awaiting in-flight refresh, lease timings) are unchanged. * fix(daemon): move the supervisor registry out of $TMPDIR macOS com.apple.bsd.dirhelper deletes files older than three days under $TMPDIR daily at 03:35. The supervisor ownership registry (owner.json/ scope.json, startup fences, the shutdown admission) lived there only because it rode along with the socket directory, whose location is forced by the 104-byte sun_path limit — a constraint JSON records do not share. Any supervisor alive past three days lost its record and wedged permanently with supervisor_generation_stale on every command. The registry now defaults to ~/.prime/supervisor-owners: durable, global per user (ownerConflicts must see every daemon on the box, so it must not shard per agent dir), with the PRIME_AGENT_INTERNAL_DAEMON_SUPERVISOR_ REGISTRY_DIR override unchanged. All registry tenants move together. No record migration: records are per-process-lifetime. A running old-build daemon keeps its tmpdir records and works until restarted; its leftover records self-clean via dirhelper within three days. During the overlap a new-build daemon sees an empty registry and fails at socket bind instead, which surfaces like any bind conflict and unwinds acquire cleanly. * fix(daemon): disambiguate the two ownership-lost error messages Both daemon-supervisor.ts (this.ownership undefined: never acquired or already released) and DaemonSupervisorOwnershipLostError (record on disk missing or replaced) emitted the identical 'no longer owns its registry entry' string, making the failure mode impossible to tell apart from the message alone. Each message is now distinct and appends the socket path, the registry dir where available, and the remedy (restart the daemon; sessions are preserved). Both keep code: supervisor_generation_stale. * test(daemon): cover the release-overtakes-renew admission race Pin the RenewableRegistryRecord hardening at its consumer: a direct assertOrRenew queued behind a held registry guard while release() runs must reject with the admission-lost error and must not rewrite shutdown-admission.json after removal. * fix(daemon): read the legacy $TMPDIR registry during the move window A new-build CLI could not see a still-running pre-move daemon's owner record: persistDaemonStartupFenceFromOwner scanned only the new (empty) registry and threw AFTER prepare_update_restart had already drained and fenced the old supervisor, whose updateRestartPhase never leaves 'prepared' without a shutdown — wedging the standard upgrade path. The worker-auth validation (assertDaemonSupervisorOwnerCurrent) had the same blind spot for a new-build worker under an old-build supervisor. Owner-record READS now fall back to the legacy tmpdir location (read-only, no abandoned-dir reclaim — old-build daemons own that location's lifecycle; unlocked relative to old-build writers, acceptable because records are rename-atomic). Writes, including fences, go only to the new registry. The fallback is resolved structurally where registryDir is resolved: it exists iff no env override and no explicit registryDir, so tests with explicit registries can never leak reads to the machine's real tmpdir. Remove after one release. acquire's conflict scan keeps no fallback: real socket contention is still caught at bind by the socket lease. * test(daemon): cover the worker-auth legacy fallback; filter legacy fence matches Add the missing test for assertDaemonSupervisorOwnerCurrent's legacy read (a pre-move owner claim validates through an injected legacy dir and never falls back for an explicit registry), replace the silent processStartId early-out in the fence-via-legacy test with a visible assertion, and filter legacy fence matches by the caller-held token/pid so stale legacy leftovers cannot produce a spurious multiple-owners failure.
…fact paths (PrimeIntellect-ai#1450) * refactor: canonical session artifact path helpers in session-manager * fix(daemon): drop a deleted rlm child's nested artifact dir, keep the transcript * feat(daemon): bounded per-parent reaper for tombstoned rlm child artifact dirs * fix(daemon): re-sweep artifact dirs resurrected by teardown snapshots * test(daemon): artifact-dir deletion, best-effort rm, reaper bounds, path helper equivalence * fix(daemon): guard degenerate session-file names in the artifact reaper Also sweep every tombstoned edge on retry-heal, skip the chmod rm-failure test as root, and pin the depth-2 transcript boundary. * docs: trim redundant policy comments to repo discipline * fix(daemon): re-sweep artifact dirs even when child teardown throws * fix(daemon): never let jobs-store errors mask a child deletion Pre-round-2 a cancel throw on the healthy path propagated as a deletion failure. Swallowing it is deliberate: both tombstones are durable by this point, the reaper and retry-heal converge on leftover state, and a deletion should not fail over jobs-store bookkeeping. * revert(daemon): drop the orphan-artifact reaper, keep the deletion hook New children never orphan artifact dirs once the deletion hook exists; the pre-fix garbage is a one-time mess not worth a permanent mechanism in the deletion path. The degenerate-basename guard keeps direct coverage via deleteSessionArtifacts. * fix(ai): stabilize Cloudflare gateway model test --------- Co-authored-by: Seth <seth@primeintellect.ai>
…imeIntellect-ai#1479) * Show subagent reasoning effort in agents view * Fix scoped subagent effort labels * Fix agents view effort regression coverage * Hide disabled subagent effort labels * Prioritize subagent model in agents view * Preserve legacy subagent effort labels
* chore(ai): regenerate the model catalog fixes ENG-5286 * fix(coding-agent): repoint the Cerebras default after zai-glm-4.7 left the catalog Also finish the Cloudflare gateway Claude id rename in the handoff test. fixes ENG-5286 * Keep handoff models aligned with catalog --------- Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com> Co-authored-by: Seth <seth@primeintellect.ai>
…-ai#1279) * fix(coding-agent): resume interrupted work after auto-compaction A threshold compaction that intentionally stopped a mid-task tool loop never resumed the loop when the compaction itself failed or was skipped (resumeAfterFailure only fired for requested compactions). And with an active goal, a threshold stop landing after an assistant text turn left the goal stuck active forever after a successful compaction, because nothing re-entered the loop and continue() cannot resume from an assistant-last context. Widen the failure-resume gate to threshold compactions (overflow stays excluded on purpose) and queue the goal continuation as a session input before compaction, mirroring the autonomous-mode compensation, with the goal taking exclusive priority over autonomous continuation to match _getContinuationMessages. * fix(coding-agent): withdraw the queued goal continuation on cancelled compaction The goal continuation is admitted as a session input before a threshold compaction runs, so when the user cancelled that compaction the finally block's session-input pump still delivered it and restarted the agent. Cancel the queued action on the aborted branch only (skip/fail must keep delivering it), roll back the queue-time continuationsUsed increment only when an action was actually cancelled so a stale marker cannot corrupt completed-goal bookkeeping, and trim the previously added comments down to the genuinely subtle rationale. * test(coding-agent): make compaction continuation fixture deterministic * test(coding-agent): remove ineffective faux usage overrides * test(ai): use stable Cloudflare Anthropic model id --------- Co-authored-by: Alex Zhang <alex.lx.zhang@gmail.com> Co-authored-by: az <altzhang@mit.edu>
…rimeIntellect-ai#1188) * prompt: disincentivize blocking sleep in kernel, bash cells, and bash tool Add a control-loop rule to the RLM system prompt forbidding blocking sleep patterns across all execution modes: time.sleep() loops in Python, sleep in %%bash cells, and sleep in the bash tool. A blocked cell or command holds the turn open, wastes wall-clock, and prevents user interaction. The agent should instead kick off work, record its handle, end the turn, and check results later. Expands PR PrimeIntellect-ai#1034 which only covered time.sleep() in Python to also cover sleep in bash cells and the bash tool. closes PrimeIntellect-ai#1034 * prompt: bound total wait instead of poll interval 'Short intervals' permitted sleep(5)-loops that block the turn just as long as one big sleep — the harm is total blocked wall-clock, not interval size. Allow only a single bounded wait when completion is imminent; any longer wait or any sleep loop means end the turn. * prompt: make long-running RLM work nonblocking * prompt: add proactive progress and clear technical prose * prompt: limit user progress updates to root agents * prompt: emphasize parallel work and outcome updates * prompt: clarify safe async shell usage --------- Co-authored-by: Seth <seth@primeintellect.ai>
…Intellect-ai#1316) * feat(coding-agent): add async bash() to IPython kernel Add an async bash() function to the RLM bootstrap code in ipython.ts that uses asyncio.create_subprocess_exec to run shell commands without blocking the kernel event loop. Unlike %%bash cells (which block the kernel until the command finishes), await bash('...') keeps the kernel responsive to interrupts and other messages while the process runs. Supports optional timeout (raises TimeoutError) and cwd parameters. Returns a _PrimeAgentBashResult with stdout, stderr, and returncode. Update the RLM system prompt to prefer await bash('...') over %%bash cells. * fix(coding-agent): bound async bash subprocesses * fix(coding-agent): fail closed for bash on Windows * fix(coding-agent): render async bash output * fix(coding-agent): clean completed bash sessions * fix(coding-agent): validate bash output bounds * fix(coding-agent): bound rendered bash output * fix(coding-agent): escalate bash group cleanup * fix(coding-agent): preserve bounded UTF-8 output * docs(coding-agent): qualify async bash platforms * fix(coding-agent): clarify bash truncation units * refactor(coding-agent): keep async bash POSIX-only * docs(coding-agent): guide background bash tasks closes PrimeIntellect-ai#1034 * docs(coding-agent): simplify background bash guidance * fix(coding-agent): preserve goal continuation after compaction * docs(coding-agent): explain continuation deduplication --------- Co-authored-by: Seth <seth@primeintellect.ai>
…-ai#1505) * chore: remove redundant comments * chore(tui): remove redundant export comments * chore(coding-agent): remove redundant example comments * chore(ai): remove redundant comments * chore(coding-agent): remove obsolete core comments * chore(coding-agent): remove redundant test comments * chore(coding-agent): remove redundant test comments * chore(coding-agent): remove redundant rpc test comments * chore(coding-agent): remove redundant CLI comments * chore(agent): remove redundant comments * chore(tui): remove redundant source comments * chore(coding-agent): remove redundant mode comments * chore(coding-agent): remove redundant mode comments * chore(coding-agent): remove redundant test comments * chore(ai): remove redundant source comments * chore(coding-agent): remove redundant session comments * chore(coding-agent): remove redundant core comments * chore(coding-agent): remove redundant test comments * chore(tests): remove redundant test comments * chore: remove unused internal helpers * docs: restore public API contracts * chore(coding-agent): restore invariant documentation * chore(coding-agent): clarify agent family contracts * style(coding-agent): format restored contracts * fix: restore intentional catch explanations
…raw \n (PrimeIntellect-ai#1522) * fix(coding-agent): let a raw \n insert a newline instead of toggling edit diffs 0.7.3 added app.edits.expand with the default ctrl+j (PrimeIntellect-ai#1388). The custom editor dispatches app actions before the base editor handling, and a raw "\n" byte decodes as ctrl+j — but that byte is exactly what Shift+Enter sends in terminals that map it to a literal newline (the mapping our own keys.ts comments recommend for Ghostty), and ctrl+j is itself a traditional newline key. Since 0.7.3, that input toggled edit diffs and Shift+Enter stopped producing newlines outside kitty-protocol terminals. Skip app-action dispatch for the raw "\n" byte in the editor and in the subagent-line key handler so it reaches the newline handling; ctrl+j still triggers the toggle via the kitty CSI-u encoding, which is unambiguous. * Trim the collision comments to one line each
* feat(coding-agent): auto-stash editor draft when opening the agents view and restore it on session reopen * fix(coding-agent): skip agents-view auto-stash for whitespace-only drafts * fix(coding-agent): keep init statuses visible when restoring an on-open stash * test: stub restorePromptStashOnOpen in startup-run fakes and bound the hook spin run() now calls this.restorePromptStashOnOpen() right after init(). The Prime CLI onboarding tests drive the real run() on plain fake objects, so the missing method made run() reject immediately: three tests failed and two spun forever in `while (!fakeThis.admitPendingStartupPrompts) await Promise.resolve()`, hanging the CI shard until the job timeout cancelled it with no output. Stub the method in createStartupRunHarness and bound both spin loops so a future regression fails the test instead of hanging the shard. * test(coding-agent): trim prompt stash coverage --------- Co-authored-by: Seth <seth@primeintellect.ai>
…ct-ai#1510) * feat(coding-agent): let subagents be spawned with an explicit reasoning level, fixes ENG-5301 * fix(coding-agent): neutral wording for the subagent thinking option in the rlm prompt * chore(coding-agent): drop a redundant doc comment * chore(coding-agent): drop the remaining name-restating doc comments in rlm-runtime * chore(coding-agent): simplify subagent thinking validation * fix(coding-agent): preserve early thinking validation * refactor(coding-agent): centralize thinking levels * refactor(coding-agent): keep thinking validation local * test(coding-agent): retain thinking option boundaries --------- Co-authored-by: Seth <seth@primeintellect.ai>
…t-ai#1520) * fix(coding-agent): normalize daemon socket paths at every entry point, fixes ENG-5303 * fix(coding-agent): address review findings on socket-path normalization - Defer --daemon-socket normalization until after --cwd applies so relative socket paths resolve against the requested working directory - Migrate legacy raw-spelling worker-descriptor namespaces to the canonical directory on supervisor construction so existing workers stay adoptable - Consolidate the remaining private normalizers (package-manager-cli, daemon-update-restart) onto the shared normalizeSocketPath - Wait for killed test supervisors to exit before removing their directories to fix ENOTEMPTY flakes in CI * fix(coding-agent): harden descriptor-namespace adoption and test cleanup - Adopt a raw-spelling descriptor namespace only after the socket-path lease and registry ownership are held, so a rejected startup can never move a live supervisor's directory - Skip unreadable JSON entries per file when identifying a namespace instead of aborting the whole scan - Drop migration-era vocabulary from the helper names and test titles - Disable the Node compile cache for spawned test supervisors and tolerate cleanup races so worker processes exiting late cannot fail the suite * refactor(coding-agent): drop the descriptor-namespace adoption machinery Normalization at the entry points prevents namespace forks going forward; descriptors written under an old raw-spelling key are healed per-field on load only when they share the directory. Cross-directory adoption kept accreting ordering hazards (live-daemon rename races, persisted-config staleness) disproportionate to its value, so a daemon that previously ran on a non-canonical spelling simply starts fresh namespaces; saved sessions are unaffected (they live in the session catalog, not the descriptor dir). * docs(coding-agent): align the changelog with the simplified scope * chore(coding-agent): trim excessive comments * fix(coding-agent): normalize the early-launch socket key and drop an incidental test maybeStartDaemonEarly memoized ensure attempts under the raw --daemon-socket spelling while main uses the normalized one, so equivalent spellings could run two concurrent spawn attempts for one daemon. The early kick now derives the same canonical spelling (resolving relative paths against --cwd). * chore(coding-agent): simplify socket normalization coverage * Address final review feedback --------- Co-authored-by: Seth <seth@primeintellect.ai>
* ci: require a linked Linear ticket or an explicit opt-out on pull requests * ci: drop the No-Ticket opt-out; every PR links a Linear ticket
…1540) * fix(coding-agent): bound persistent kernel snapshots * fix(coding-agent): simplify bounded kernel snapshots * fix(coding-agent): cap individual snapshot variables * fix(coding-agent): prune oversized state on compaction * fix(coding-agent): stop snapshots at aggregate limit * fix(coding-agent): preserve bounded snapshot packing --------- Co-authored-by: Seth <seth@primeintellect.ai>
* docs: add Trendshift badge * docs: separate the Trendshift badge --------- Co-authored-by: Seth <seth@primeintellect.ai>
Fixes ENG-5388 Co-authored-by: Seth <seth@primeintellect.ai>
* feat(acp): harden resident session lifecycle * test(daemon): align recovery fixtures with durable context * fix(acp): validate durable host settings * fix(daemon): clean up failed resident resources * fix(daemon): recover owned workers from fresh context * fix(acp): verify authoritative completion state * fix(daemon): refresh requested child roster * fix(acp): read child roster without reattaching * fix(daemon): preserve telemetry opt-out on recovery * fix(daemon): scope resident path conflicts * test(acp): remove runtime publish hook * fix(acp): drain admitted updates on close * fix(daemon): merge fresh supervisor defaults * fix(acp): await autonomous lifecycle settlement * fix(acp): preserve daemon input fences * fix(acp): serialize terminal lifecycle turns * fix(acp): reacquire daemon fences after reconnect * fix(acp): fail closed across abort cuts * fix(acp): resume queued input after pause * fix(acp): invalidate pending pumps on pause * fix(acp): preserve pause ownership through supervisor * fix(acp): fail closed on pause cleanup loss * test(daemon): initialize pause ownership harness * fix(acp): fence detach pause cleanup * fix(daemon): preserve detach-all cleanup * test(daemon): initialize worker pause registry * fix(daemon): reacquire pauses across release * fix(daemon): clear stable detach selectors * fix(daemon): retain detach fences on attach failure * refactor(daemon): keep descriptor validation private * docs(changelog): describe ACP lifecycle fencing * fix(acp): settle restart and failed close lifecycle * fix(acp): serialize cancel after failed close * fix(acp): preserve lifecycle reconciliation ordering * fix(acp): settle deleted child runtimes * test(acp): consolidate child deletion regressions * fix(acp): rearm settled child deletion * fix(acp): preserve recursive terminal settlement * test(acp): update terminal notice regression
…e next turn (PrimeIntellect-ai#800) * fix(coding-agent): queue ACP prompts behind in-flight work * fix(acp): keep queued-prompt semantics through the resident lifecycle merge Renumber owned prompt cancellation to schema revision 20 (17-19 landed on main), adapt compatibility gating to the requirements-array form, and return stopReason "cancelled" for a prompt parked behind the terminal lifecycle when a cancellation drops it before it starts. --------- Co-authored-by: Sebastian <sebastian@primeintellect.ai>
* feat(coding-agent): add generic MCP runtime * fix(coding-agent): harden MCP lifecycle and overrides * fix(runtime): close cancelled host request comms * fix(runtime): preserve MCP SDK result aliases * fix(runtime): preserve synchronous kernel shutdown * fix(runtime): allow MCP close retry after cancellation * feat(coding-agent): manage MCP servers from CLI and TUI * fix(coding-agent): preserve inherited stdio env names * fix(coding-agent): reserve built-in MCP identities * fix(coding-agent): allow cleanup of reserved MCP entries * test(coding-agent): update MCP command hint * fix: address MCP reload and cleanup findings * fix(coding-agent): advertise generic MCP connections * fix(coding-agent): keep MCP command results visible * fix(runtime): surface safe stdio startup diagnostics * fix(runtime): preserve startup errors through cleanup * fix(coding-agent): drop stored MCP credentials when a server is removed or replaced * fix(coding-agent): scope credential drops to generic servers and keep quoted empty argv tokens * docs(coding-agent): document the removed catalog-name override as a breaking change * fix: close kernel MCP servers during shutdown * docs: preserve changelog sections after main merge * fix: harden MCP shutdown boundaries * fix: bound graceful kernel shutdown --------- Co-authored-by: Sebastian <sebastian@primeintellect.ai>
…ct-ai#1955) * revert(coding-agent): restore subagent cleanup guidance Reverts PrimeIntellect-ai#1952 so agents delete direct subagents when they are no longer needed instead of retaining them until a user explicitly requests deletion. Adds prompt coverage for the intended cleanup policy. * test(coding-agent): keep prompt rollback free of copy assertions
…ies without changing list --all (PrimeIntellect-ai#1951) * fix(coding-agent): scope the roster seed to registered workers' families without changing list --all Reworks the closed PrimeIntellect-ai#1941: the boot seed now covers only registered workers' descendant families (catalog rows stay catalog-owned), but subagent rows of families with no registered worker are still served by list --all, read on demand from the spawn ledger with the same fields, statuses, and liveEdges ordering as the seeded rows had. The sessionDir ancestry walk falls back to ledger edges when the ancestor rows are not roster-resident. * fix(coding-agent): hydrate on-demand ledger list rows sequentially The dead-family list --all path fanned out one concurrent transcript read per unseeded ledger child (Promise.all), a shape the boot seed deliberately avoids; a large dead-family ledger could exhaust file descriptors. Hydrate inside the edge loop instead, one read at a time, matching seedRosterLedger.
…or a doomed kernel snapshot (PrimeIntellect-ai#1954) * feat(coding-agent): make the final trace flush detachable and the kernel snapshot optional on dispose AgentSessionRuntime.dispose() and AgentSession.disposeAsync() now take options: traceFlush "detach" fires the final trace upload without awaiting it (failures land in agent-traces.log), and kernelSnapshot:false skips the kernel's final snapshot at the provisioner shutdown boundary. Defaults keep today's blocking flush and snapshot on every caller. Session replacement (new/resume/fork teardown) detaches the flush: the outgoing session file and the process both outlive the swap. * fix(coding-agent): stop RLM subagent deletion from awaiting the trace upload and writing a doomed kernel snapshot Deleting a resident child blocked on the full session trace upload (15s timeout x3 retries, and a flat 60s sleep on HTTP 429 without retry-after) and wrote a final kernel snapshot that the artifact sweep removed milliseconds later. The daemon now detaches the flush on every close that the daemon and the session file outlive, and the delete paths dispose the kernel with snapshot:false. Daemon shutdown, update restarts, and worker archive-and-shutdown still await the flush; passivation still writes the snapshot. Measured on a resident child with a pending upload: 3s-network flush 3024ms -> 10ms; 429 without retry-after 60120ms -> 9ms. Linear: ENG-5837 * refactor(coding-agent): replace per-close trace-flush policy with an exit barrier Review round: disposal policy did not propagate through nested closes (hosted children), the closingSessions join (delete inheriting a passivation close's awaited flush; exit joins never upgrading an in-flight detached close), or startup-abort kernel teardown. Instead of threading policy through every close, disposal now never awaits the trace upload; the four exit owners (daemon shutdown, update restart, worker archive-and-shutdown, in-process connection dispose) drain all scheduled and in-flight uploads through one barrier (flushAllPendingAgentTraceUploads). This makes the nested/concurrent propagation bugs structurally impossible. kernelSnapshot:false stays threaded from the delete paths and now also covers a dispose that aborts a kernel startup in flight. * fix(coding-agent): run the trace-upload exit barrier even when teardown throws The in-process connection dispose and the daemon shutdown loop exit regardless of a thrown teardown (print/acp swallow the dispose error and exit; a rejected shutdown() is an unhandled rejection), so the barrier moves into a finally. The teardown error still propagates. The other two owners (update-restart commit, worker archive-and-shutdown) keep the daemon alive on a throw, so their detached uploads finish on their own.
* chore: prepare v0.9.0 release * chore: add missing list marker to a 0.9.0 changelog entry
…rimeIntellect-ai#1960) * fix(coding-agent): load the saved catalog when the agents view opens v0.9.0 regression: the Inactive section was empty on a fresh agents view until a search query was typed. The saved catalog was loaded only for search (a PrimeIntellect-ai#1900 optimization premised on the roster boot seed carrying the saved corpus as inactive rows); PrimeIntellect-ai#1951 scoped that seed to live families, so the view must load what it displays. The load stays progressive and once-per-view. * docs: tighten the catalog-load comment
* ci: prefer Research tickets for Prime Agent * ci: require RES tickets or explicit opt-out
….x (PrimeIntellect-ai#1993) * fix(ai): bump impersonated Claude Code version to 2.1.257 for Fable 5.x The Anthropic API now gates Fable 5.x models on Claude Code >= 2.1.251 and rejects the previously pinned 2.1.75 identity for OAuth requests. Fixes PrimeIntellect-ai#1962 * chore(ai): drop the identity-header test and rationale comment per review
…ct-ai#1992) * fix(installer): support npm 12 remote dependency policy * fix(installer): allow verified npm 12 postinstall * refactor(installer): simplify npm 12 coverage * refactor(installer): consolidate installer checks
PrimeIntellect-ai#2002) * fix: register ACP MCP tools as native callable tools via cpython proxy * fix: add MCP proxy tool names to allowlist so model can see them * fix: add missing details field to ACP MCP tool execute results * fix(coding-agent): harden ACP MCP proxy lifecycle Fixes PrimeIntellect-ai#2002 * fix(coding-agent): reject MCP without cpython Fixes PrimeIntellect-ai#2002 * refactor(coding-agent): narrow generic MCP accessor Fixes PrimeIntellect-ai#2002 * fix(coding-agent): cap ACP MCP server names so composed tool names fit provider limits --------- Co-authored-by: Sebastian <sebastian@primeintellect.ai>
…ursor outbox (PrimeIntellect-ai#1957) * refactor(coding-agent): reschedule agent-trace uploads through a disk-cursor outbox Upload intent and uploaded-content cursors persist per session file in agent-traces-outbox.json; a once-per-process startup catch-up uploads whatever a previous process never finished and prunes cursors of deleted files. Unchanged sessions are never re-uploaded. 429s return immediately and reschedule via the controller instead of sleeping in-request, and session disposal no longer knows trace uploads exist. Linear: ENG-5838 * fix(coding-agent): make the trace outbox per-entry, durable at persist, and process-exit safe One entry file per session (path-hashed) replaces the single-map file: concurrent writers cannot lose cursors and a bad read costs only its own entry. The pending marker is written synchronously at first persist. A failed cursor write after a successful PUT returns a retryable failure. 429 Retry-After is honored on the next scheduled cycle. All upload timers are unref-ed so telemetry never holds the process open. * fix(coding-agent): retry failed intent markers and cap Retry-After at the timer maximum A failed pending-marker write no longer marks the session as locally managed, so the next persist retries it. A Retry-After beyond Node's ~24.8-day setTimeout maximum is capped there instead of overflowing to an immediate retry loop.
…imeIntellect-ai#1985) A finished top-level agent stayed in the agents view Running section as "classifying" forever: the roster's activity axis holds an idle session at "working" until its summarizer verdict is current, but the summarizer only notified (and thereby scheduled a roster flush) when the verdict TEXT or task state changed. A settle that produced the same text at a new message count updated the verdict's currency without ever republishing the row, freezing rosterStatus at "running" with the "classifying" label. The change gate now treats an idle settle that refreshes basedOnMessageCount as a status change; working refreshes with unchanged text stay quiet. The dead agentStatusChanged export duplicated the old two-field predicate (with no production caller) and is deleted. Diagnosis credit: Vincent Bailly (VincentBailly#8), whose consumer-side workaround this producer fix supersedes. RES-1252
…rom the RLM spawn ledger (PrimeIntellect-ai#1987) * refactor(coding-agent): extract the append-only event-log substrate from the RLM spawn ledger One EventLog owns the shared crash-safety mechanics: single O_APPEND writes with optional fsync, bounded fail-closed reads, torn-final-line tolerance on replay, and repair-on-append (byte-offset truncate for an unparseable tail, newline completion for a parseable one). RlmSpawnLedger keeps spawn semantics only; its public API and test suite are unchanged. * fix(coding-agent): keep the union of ledger safety behaviors in the event-log substrate Reject unserializable events with a TypeError before any byte (including repair) is written; truncate EVERY unterminated tail instead of newline-completing a JSON-parseable one, which would hand a line a strict consumer parser rejects to every later replay as fail-closed interior poison; read through a bounded descriptor so a concurrent grow between size check and read cannot bypass maxBytes.
… residency with durable wake (PrimeIntellect-ai#1967) * fix: classify sessions with armed heartbeats as idle, not running An armed heartbeat between firings is residency, not work, so it no longer counts as a running signal anywhere: the roster classifier, the unified agents-view classifier, the ancestor-propagation overwrite, and the subagent count projections all drop the heartbeat clause. A firing heartbeat still lands in Running through its executing turn, and hasActiveHeartbeat stays on the wire for badges and labels. Idle rows with an armed heartbeat now label the next fire time (heartbeat - next 5m) when the aggregate knows it. Paused-only heartbeats, previously invisible, render a dimmed heartbeat badge without affecting the section. The delete confirmation warns when the targeted session or subagent still has an armed heartbeat; the daemon already cancels scheduled jobs in both delete paths. ENG-5847 * fix: passivate heartbeat sessions normally and wake them when jobs come due An armed heartbeat no longer pins a session resident forever. The hasRegisteredHeartbeat veto is deleted from the idle-eviction and child passivation policies (and from the empty-draft detach rule): heartbeat sessions follow the one existing idle rule (idleEvictionMinutes, default 90m) like every other session. The schedule survives passivation through a single wake owner split by residency: workers keep owning all firing and delivery for resident trees (their scheduler already revives passive children on due jobs), and the supervisor owns exactly one new concern - waking a session tree that is resident nowhere. It recomputes wake timers from durable truth (the saved-session catalog plus each session's scheduled-jobs artifact) at boot and on every heartbeat/residency change, and on fire relaunches the root worker through the same create path clients use; the fresh worker's scheduler then runs the due job with the existing catch-up semantics. Workers now also register passive descendants' scheduled-job artifacts when a root becomes resident, so a relaunched tree schedules its passivated children's heartbeats without hydrating them. Passivated sessions keep their heartbeats visible: the supervisor's heartbeats_list appends passive scheduled jobs, roster rows keep their registration marks across eviction, and the agents-view badge aggregate matches jobs by stable session id and file, not just the stale active id. ENG-5846 * fix: fence the scheduled-session wake to public sessions and ledger topology Review round on the residency half: - Client-owned schedules now die with the registration, exactly like their roster rows: stopping an ephemeral worker cancels the scheduled jobs of its root and ledger descendants, so a private session is never listed in heartbeats_list or relaunched by the wake. Public sessions are unaffected because heartbeat_set/cron_add promote owned sessions before their jobs land. - The wake enumeration now reads topology from the RLM spawn ledger's family (roots plus ledger descendants, fork headers stripped) instead of the saved-session catalog, which missed nested subagent transcripts entirely and misassigned passivated forks to their former parent through header lineage. - The wake schedule stays disarmed while an update restart is being prepared instead of spinning a zero-delay timer on overdue jobs; the failed-preparation transition re-arms it once, and a successful restart re-arms at the next boot. - One corrupt descendant artifact no longer strands the remaining descendants' registrations in a fresh worker. - Managing a passivated heartbeat now works: heartbeat_manage resolves the job through the same durable enumeration and mutates its scheduled-jobs store directly, without waking the session. ENG-5846 * chore: slim PR comments and merge overlapping heartbeat pins Comment pass over both halves: every added comment either deleted or cut to one load-bearing line. Test pass: the paused/active heartbeat eviction test duplicated the sweep pin's heartbeat eviction (the distinction has no code path anymore) and the next-fire label pin merged into the heartbeat aggregation test it shares fixtures with. * fix: key the ephemeral cancel by persisted ids and fence it against the wake The ephemeral-stop cancel derived descendant session ids from filenames, while scheduled-job artifacts key on the persisted session id: a child whose id differs from its file name kept armed jobs past its owner's departure. The cancel now walks the same ledger family() rows the wake enumeration uses, so both sides share one id resolution. The cancel also ran after the worker left the worker map, leaving a window (or, after a swallowed failure, forever) in which the tree looked non-resident with active jobs and could be woken publicly. It now runs before the map removal - every enumeration observed during the awaited cancel still sees the tree covered - and a failed cancel parks the root in pendingEphemeralCancels: enumeration retries the cancel first and keeps the tree excluded until it lands. ENG-5846 * fix: reach passive scheduled jobs from unscoped cron commands; drop stale cancel intents Unscoped cron_cancel probed only resident workers, so a passivated session's still-firing job could not be stopped without reopening the session; it now falls back to the passive enumeration and cancels through the per-job store API, broadcasting so the wake timer drops it. Unscoped cron_list merges the same passive jobs after the resident sweep (worker copies win). With these, no unscoped job surface in the protocol misses passive sessions. A parked ephemeral-cancel intent also outlived its premise: reopening the tree after a failed cancel would let the enumeration retry destroy the fresh worker's new schedules. The retry now drops the intent when a worker covers the tree again - the store is worker-owned from that moment and resident trees are excluded from enumeration anyway. RES-1250 * fix: read each passive session's scheduled-jobs artifact in isolation One malformed scheduled-jobs.json aborted collectPassiveScheduledJobs entirely, so every other passive session's jobs vanished from listings and their wakes never armed. Ref: RES-1250 * fix: keep the ephemeral stop tombstone as the durable cancel intent A failed ephemeral cancel was parked only in memory, so a supervisor restart lost the intent and the next boot recovered the private tree's jobs wake-eligible. The stop now deletes the persisted tombstone only after the cancel settles; boot finishes the cancel from the tombstone, with the same covered-tree exemption that protects a reopened session's new schedules. Ref: RES-1251 * fix: include passive terminal jobs in an inclusive unscoped cron_list Resident workers honor includeInactive but the passive merge only contributed active/paused jobs, so passivation silently shrank an inclusive listing. The wake path keeps considering active jobs only. Ref: RES-1250 * fix: revalidate ownership inside the ephemeral cancel's destructive walk promoteOwnedWorker can clear ownerClientId while the cancel awaits the ledger family read, so a successful promotion had its now-public schedules cancelled out from under it. The guard runs in the same synchronous turn as the store walk; promotion commits are synchronous single-mutation blocks, so a promote lands either wholly before the guard (cancel skipped) or wholly after the walk. Ref: RES-1251 * fix: treat a resident worker anywhere on the owning chain as covering its jobs Passivity was judged by the top ancestor alone, so a job owned by a mid-tree session with its own resident worker still counted passive and the wake launched a competing root worker over an active subtree. The walk now checks findWorkerBySessionFile at every hop; only a fully uncovered chain stays wake-eligible. Ref: RES-1250 * fix: guard every destructive cancel walk with the same ownership truth The round-5 stillWanted guard only ran when the family read succeeded: a promotion landing during a throwing read was still parked, and the enumeration retry cancelled the now-public schedules unguarded. The catch now settles instead of parking when ownership is gone, and the retry passes stillWanted from the parked worker's descriptor - the same object a late promotion mutates and persists, which the boot gate already reads. Ref: RES-1251 * refactor: derive ephemeral cancel intents from the persisted descriptors The in-memory park/retry map duplicated a truth that already lives on disk: the kept tombstoned client-owned descriptor. Enumeration now re-derives retry intents (and the still-pending tree exclusion) from descriptorDir alone, and the covering-worker exemption moves inside the destructive walk itself, checked per tree member - chain-complete where the old root-only retry check missed a replacement worker reopened on a child file - so stop-time, retry, and boot cancels share one guard. Promotion self-corrects in memory and on disk because promoteOwnedWorker replaces and persists the descriptor. Ref: RES-1251 * fix: keep wake-blind scheduled workers resident through the idle sweep The wake scan only enumerates the supervisor's sessions root, so a public worker whose root file lives in a custom session dir would passivate and never wake for its schedules. Eviction eligibility now matches wake enumerability: such a worker with a registered heartbeat or cron job stays resident, exactly the pre-PR behavior for the blind set. create.config.sessionDir is client-reachable (daemon CLI --session-dir). Ref: RES-1250 * fix: own the wake-blind exemption in the eviction eligibility predicates The gate lived at the sweep's candidate build only, so the fenced recompute after the mutation drain and the last-detach empty eviction could still passivate a custom-dir worker into the blind spot. The fact moves into the WorkerEvictionSnapshot (hasWakeBlindSchedule, refused by canEvictWorker) so both sweep sites read one policy, and the last-detach path - a different input shape built from empty-draft summaries, not eviction snapshots - consults the same isWakeBlindScheduledWorker helper in its own eligibility predicate. Call-site check deleted. Ref: RES-1250 * test: give the peer-transport eviction fixture a type-complete descriptor workerEvictionSnapshot now computes the required hasWakeBlindSchedule field, which walks descriptor.sessionFile ?? createCommand.sessionPath. createCommand is required on DaemonWorkerDescriptor and every real descriptor carries it; the fixture impersonated one through an unknown-cast without it. Fix the fixture, not the type contract. * feat(coding-agent): rank armed heartbeats first within the inactive section
…ntellect-ai#1885) * feat(coding-agent): ACP lineage-v1 provenance producer One opaque request ID on the wire (X-ACP-Lineage-Request-ID + Idempotency-Key, minted before the call and stable across retries of the same call), a durable append-only lineage.jsonl ledger per agent session beside its artifacts, context epochs driven by compaction outcomes, cross-process ancestry for spawned subagents, and deriveLineageManifest folding a session tree's ledgers into the verifiers lineage-v1 manifest. Derivation only; nothing publishes or reads the ledger yet. * fix(coding-agent): harden lineage retry identity, spawn ancestry, and ledger repair Review round 2 on the lineage-v1 producer: retry Idempotency-Key reuse now requires a byte-identical body (sha256 of model + system prompt + messages) instead of a shape proxy; spawn ancestry is snapshotted synchronously at the spawn entry point and survives resume via ledger replay; child terminal status is recorded once at actual release (reusable children stay running across follow-up runs, startup failures record failed); ledger construction never mutates the file (torn-tail repair deferred to first append) and only a malformed unterminated final line counts as torn; the completed-compaction ledger event now precedes the transcript commit. Adds host pass-through, real-summary-call wire, negative validator calibration, replayed-status, double-wrap, and failed/cancelled mapping coverage. * fix(coding-agent): eager lineage body hash and mutation-hardened tests Round 3 on the lineage-v1 producer. The retry body hash is now computed eagerly at request creation, before the wire call, so mutating the live message objects between the failed call and the retry park can no longer alias two different bodies under one Idempotency-Key (TOCTOU regression test included; the lazy-thunk indirection is gone). Test hardening from the mutation campaign: hash keyed on provider/model/systemPrompt each proven independently; undefined-vs-undefined parked hashes never match; readLineageLedger proven read-only; torn-tail repair proven single-shot; spawn-ancestry snapshot proven to precede preflight awaits; the completed compaction ledger event proven durable before (and exactly once despite a failing) transcript commit. The invariant port checks compactions before requests and the negative calibration table asserts the named error per row, adding duplicate-context, session-owner, and per-predicate isolation rows. * test(coding-agent): kill the last two lineage mutants readLineageLedger is proven to never open the ledger for writing (a missing path must throw ENOENT and stay missing; a zero-byte append-open would create it), and the calibration table gains a row where a fully valid child compaction is named by a root request, so only the request-side compaction owner check can reject it. * feat(coding-agent)!: convert the provenance producer to ACP semantic-edges-v1 The ecosystem replaced lineage-v1 (nano-rlm PrimeIntellect-ai#153, verifiers #2449): one renamed correlation header (X-ACP-Model-Request-ID + Idempotency-Key) and sparse commit-gated semantic edges instead of a full session manifest. The durable per-session JSONL ledger keeps its hardened mechanics (ledger-before-wire, torn-vs-corrupt tails, repair-on-first-append, read-path purity, replay idempotence, eager body-hash retry identity, synchronous spawn snapshots) and now records request outcomes: the streamFn wrapper commits or fails each request when its stream resolves, compaction summaries commit around the real wire call only, and a parent claims a child_returned with the child's last committed request at the success point. deriveSemanticEdges is a pure order-independent fold to continuation/subagent_call/subagent_return/compaction edges: edges materialize only when their target commits, failed requests return their inbound edges to the session's pending set (prime-agent has no prompt rollback, so they attach to the next commit), spawn edges defer to the child's first committed request, and a completed compaction suppresses the summary's own continuation. Deleted: context epochs, session status recording, depth in ledger events, the manifest deriver, and the lineage-v1 validator port with its calibration table. * test(coding-agent): kill the five surviving semantic-edge mutants Two isolated compaction fold gates (a committed summary followed by a later commit, and a failed compaction after a committed summary, each producing no compaction edge), distinct wrapper outcome cases including a rejected stream promise and an aborted final message (both must fail the request), and the wire header asserted as the literal string X-ACP-Model-Request-ID so a renamed production constant cannot pass. * feat(coding-agent): failed subagent runs also return their last commit Aligns with nano-rlm a1d7856: a failed child still returns an error outcome the parent consumes, so the failure settle point now records child_returned with the child's last committed request. Cancelled runs and zero-commit children stay silent; the returned-once guard is unchanged. Tests cover the failed-with-commits return edge, failed zero-commit silence, and cancelled-with-commits silence. * fix(coding-agent): harden semantic-edge retry identity and ledger fault paths Review-bot round: the retry body hash now covers tools and the request-shaping stream options (reasoning, thinking budgets, temperature, max tokens, service tier); parking is forfeited entirely when a before_provider_request hook is registered, since payload hooks rewrite the wire body after the hash point; ledger write failures in the stream outcome observer and in compaction error paths are contained with a one-line warning instead of crashing or masking the original error (the completed-compaction ID is marked consumed before its write so a failed write propagates untouched); and side questions now use the unwrapped inner stream function, keeping their calls out of the session ledger and the continuation chain. * fix(coding-agent): distinct request identity per split-turn summary call Split-turn compactions send two summary calls with different bodies; one shared Idempotency-Key gets rejected (or replayed) by key-honoring interceptors. compact() now runs every summary call through a host runner that mints its own request ID, headers, and finish/fail outcome, and the edge fold tracks all summary slices of a compaction, keying the compaction edge off whichever slice is the session's last commit. Earlier slices contribute ordinary continuation edges. * fix(coding-agent): degrade the semantic-edge recorder instead of throwing Review-bot round 2, one mechanism for four findings: the first failed ledger operation (read at construction or any append) permanently disables the recorder with a single warning; every write becomes a no-op and the streamFn wrapper and compaction runner stop emitting request IDs on the wire, preserving ledger-before-wire instead of weakening it. This removes the scattered try/catch layers (recordOutcomeSafely and both compaction-path guards are deleted) and means an unwritable ledger can no longer break compaction, rewrite a successful child run to error, or fail a compaction whose summary succeeded. Appends are now durable before in-memory commit state advances, so a failed write never leaves a claimable commit. Aborted compaction summaries record request_failed rather than committing partial text into the chain. Compaction summary slices claim no pending edges and return none on failure (documented divergence from nano-rlm): pending defers to the post-compaction turn, the request that actually consumes subagent results. * fix(coding-agent): flush pending edges to the last-committed summary slice A completed compaction as the session's final activity permanently lost deferred pending edges (subagent_return, reclaimed continuations): the slices claimed nothing and no post-compaction turn ever arrived. The fold now flushes still-pending edges to the compaction's last-committed slice at completion — the same request that sources the compaction edge — so pending always lands on a committed request whether or not the session continues, restoring nano-equivalent single-call semantics while staying deterministic for split turns. Failed or cancelled compactions still leave pending for the next turn. * fix(coding-agent): dedupe the terminal flush against generated continuations The terminal flush appended pending edges after the last summary slice had already emitted its regenerated continuation, so a reclaimed continuation from the same source derived twice — duplicate edges the consumer rejects. The flush now applies nano's source-only suppression: a pending edge from X removes the slice's generated continuation from X regardless of the pending edge's type, then pending flushes once. * fix(coding-agent): commit summary slices only when the compaction commits and gate spawn attribution on an active run A split-turn summary slice that succeeded on the wire no longer publishes request_finished at stream resolution: slices settle at the compaction outcome, so a failed or cancelled compaction leaves no committed summary request and the next turn's continuation edge stays on the pre-compaction request. Spawn attribution now requires an active agent run; an out-of-band spawn records no spawned_by_request_id instead of a stale one.
…t-traces outbox (PrimeIntellect-ai#1984) * feat(coding-agent): ACP lineage-v1 provenance producer One opaque request ID on the wire (X-ACP-Lineage-Request-ID + Idempotency-Key, minted before the call and stable across retries of the same call), a durable append-only lineage.jsonl ledger per agent session beside its artifacts, context epochs driven by compaction outcomes, cross-process ancestry for spawned subagents, and deriveLineageManifest folding a session tree's ledgers into the verifiers lineage-v1 manifest. Derivation only; nothing publishes or reads the ledger yet. * fix(coding-agent): harden lineage retry identity, spawn ancestry, and ledger repair Review round 2 on the lineage-v1 producer: retry Idempotency-Key reuse now requires a byte-identical body (sha256 of model + system prompt + messages) instead of a shape proxy; spawn ancestry is snapshotted synchronously at the spawn entry point and survives resume via ledger replay; child terminal status is recorded once at actual release (reusable children stay running across follow-up runs, startup failures record failed); ledger construction never mutates the file (torn-tail repair deferred to first append) and only a malformed unterminated final line counts as torn; the completed-compaction ledger event now precedes the transcript commit. Adds host pass-through, real-summary-call wire, negative validator calibration, replayed-status, double-wrap, and failed/cancelled mapping coverage. * fix(coding-agent): eager lineage body hash and mutation-hardened tests Round 3 on the lineage-v1 producer. The retry body hash is now computed eagerly at request creation, before the wire call, so mutating the live message objects between the failed call and the retry park can no longer alias two different bodies under one Idempotency-Key (TOCTOU regression test included; the lazy-thunk indirection is gone). Test hardening from the mutation campaign: hash keyed on provider/model/systemPrompt each proven independently; undefined-vs-undefined parked hashes never match; readLineageLedger proven read-only; torn-tail repair proven single-shot; spawn-ancestry snapshot proven to precede preflight awaits; the completed compaction ledger event proven durable before (and exactly once despite a failing) transcript commit. The invariant port checks compactions before requests and the negative calibration table asserts the named error per row, adding duplicate-context, session-owner, and per-predicate isolation rows. * test(coding-agent): kill the last two lineage mutants readLineageLedger is proven to never open the ledger for writing (a missing path must throw ENOENT and stay missing; a zero-byte append-open would create it), and the calibration table gains a row where a fully valid child compaction is named by a root request, so only the request-side compaction owner check can reject it. * feat(coding-agent)!: convert the provenance producer to ACP semantic-edges-v1 The ecosystem replaced lineage-v1 (nano-rlm PrimeIntellect-ai#153, verifiers #2449): one renamed correlation header (X-ACP-Model-Request-ID + Idempotency-Key) and sparse commit-gated semantic edges instead of a full session manifest. The durable per-session JSONL ledger keeps its hardened mechanics (ledger-before-wire, torn-vs-corrupt tails, repair-on-first-append, read-path purity, replay idempotence, eager body-hash retry identity, synchronous spawn snapshots) and now records request outcomes: the streamFn wrapper commits or fails each request when its stream resolves, compaction summaries commit around the real wire call only, and a parent claims a child_returned with the child's last committed request at the success point. deriveSemanticEdges is a pure order-independent fold to continuation/subagent_call/subagent_return/compaction edges: edges materialize only when their target commits, failed requests return their inbound edges to the session's pending set (prime-agent has no prompt rollback, so they attach to the next commit), spawn edges defer to the child's first committed request, and a completed compaction suppresses the summary's own continuation. Deleted: context epochs, session status recording, depth in ledger events, the manifest deriver, and the lineage-v1 validator port with its calibration table. * test(coding-agent): kill the five surviving semantic-edge mutants Two isolated compaction fold gates (a committed summary followed by a later commit, and a failed compaction after a committed summary, each producing no compaction edge), distinct wrapper outcome cases including a rejected stream promise and an aborted final message (both must fail the request), and the wire header asserted as the literal string X-ACP-Model-Request-ID so a renamed production constant cannot pass. * feat(coding-agent): failed subagent runs also return their last commit Aligns with nano-rlm a1d7856: a failed child still returns an error outcome the parent consumes, so the failure settle point now records child_returned with the child's last committed request. Cancelled runs and zero-commit children stay silent; the returned-once guard is unchanged. Tests cover the failed-with-commits return edge, failed zero-commit silence, and cancelled-with-commits silence. * fix(coding-agent): harden semantic-edge retry identity and ledger fault paths Review-bot round: the retry body hash now covers tools and the request-shaping stream options (reasoning, thinking budgets, temperature, max tokens, service tier); parking is forfeited entirely when a before_provider_request hook is registered, since payload hooks rewrite the wire body after the hash point; ledger write failures in the stream outcome observer and in compaction error paths are contained with a one-line warning instead of crashing or masking the original error (the completed-compaction ID is marked consumed before its write so a failed write propagates untouched); and side questions now use the unwrapped inner stream function, keeping their calls out of the session ledger and the continuation chain. * fix(coding-agent): distinct request identity per split-turn summary call Split-turn compactions send two summary calls with different bodies; one shared Idempotency-Key gets rejected (or replayed) by key-honoring interceptors. compact() now runs every summary call through a host runner that mints its own request ID, headers, and finish/fail outcome, and the edge fold tracks all summary slices of a compaction, keying the compaction edge off whichever slice is the session's last commit. Earlier slices contribute ordinary continuation edges. * fix(coding-agent): degrade the semantic-edge recorder instead of throwing Review-bot round 2, one mechanism for four findings: the first failed ledger operation (read at construction or any append) permanently disables the recorder with a single warning; every write becomes a no-op and the streamFn wrapper and compaction runner stop emitting request IDs on the wire, preserving ledger-before-wire instead of weakening it. This removes the scattered try/catch layers (recordOutcomeSafely and both compaction-path guards are deleted) and means an unwritable ledger can no longer break compaction, rewrite a successful child run to error, or fail a compaction whose summary succeeded. Appends are now durable before in-memory commit state advances, so a failed write never leaves a claimable commit. Aborted compaction summaries record request_failed rather than committing partial text into the chain. Compaction summary slices claim no pending edges and return none on failure (documented divergence from nano-rlm): pending defers to the post-compaction turn, the request that actually consumes subagent results. * fix(coding-agent): flush pending edges to the last-committed summary slice A completed compaction as the session's final activity permanently lost deferred pending edges (subagent_return, reclaimed continuations): the slices claimed nothing and no post-compaction turn ever arrived. The fold now flushes still-pending edges to the compaction's last-committed slice at completion — the same request that sources the compaction edge — so pending always lands on a committed request whether or not the session continues, restoring nano-equivalent single-call semantics while staying deterministic for split turns. Failed or cancelled compactions still leave pending for the next turn. * fix(coding-agent): dedupe the terminal flush against generated continuations The terminal flush appended pending edges after the last summary slice had already emitted its regenerated continuation, so a reclaimed continuation from the same source derived twice — duplicate edges the consumer rejects. The flush now applies nano's source-only suppression: a pending edge from X removes the slice's generated continuation from X regardless of the pending edge's type, then pending flushes once. * refactor(coding-agent): reschedule agent-trace uploads through a disk-cursor outbox Upload intent and uploaded-content cursors persist per session file in agent-traces-outbox.json; a once-per-process startup catch-up uploads whatever a previous process never finished and prunes cursors of deleted files. Unchanged sessions are never re-uploaded. 429s return immediately and reschedule via the controller instead of sleeping in-request, and session disposal no longer knows trace uploads exist. Linear: ENG-5838 * fix(coding-agent): make the trace outbox per-entry, durable at persist, and process-exit safe One entry file per session (path-hashed) replaces the single-map file: concurrent writers cannot lose cursors and a bad read costs only its own entry. The pending marker is written synchronously at first persist. A failed cursor write after a successful PUT returns a retryable failure. 429 Retry-After is honored on the next scheduled cycle. All upload timers are unref-ed so telemetry never holds the process open. * fix(coding-agent): retry failed intent markers and cap Retry-After at the timer maximum A failed pending-marker write no longer marks the session as locally managed, so the next persist retries it. A Retry-After beyond Node's ~24.8-day setTimeout maximum is capped there instead of overflowing to an immediate retry loop. * fix(coding-agent): commit summary slices only when the compaction commits and gate spawn attribution on an active run A split-turn summary slice that succeeded on the wire no longer publishes request_finished at stream resolution: slices settle at the compaction outcome, so a failed or cancelled compaction leaves no committed summary request and the next turn's continuation edge stays on the pre-compaction request. Spawn attribution now requires an active agent run; an out-of-band spawn records no spawned_by_request_id instead of a stale one. * feat(coding-agent): register the semantic-edge ledger with the agent-traces outbox The per-session semantic-edges.jsonl becomes a second outbox kind: durable kind-tagged intent at the first transcript persist, an append-only byte cursor (uploadedBytes) whose unchanged files are never re-counted, catch-up inclusion after a kill, and pruning when the ledger is deleted with its session. No delivery endpoint exists yet (verifiers#2449 consumes edges in-band over ACP metadata; the trace server has no semantic-edges route), so pending ledgers are counted, never sent, and the cursor stays untouched for the first real sender. Unknown outbox kinds from newer builds are left alone. The ledger path derivation is shared between the recorder and the outbox registration. * fix(coding-agent): re-register the ledger intent when the outbox ledger path changes A controller update that changes semanticEdgesLedgerPath resets the intent memo, so a reused SessionManager registers the new ledger at its next persist instead of silently dropping it from catch-up. Also bullet-prefix the changelog fragment. * fix(coding-agent): consent-gate outbox intent at persist time and drop the ledger intent latch Outbox entries (transcript and semantic-edges alike) are only created while trace sharing is enabled: an entry recorded during an opted-out era would turn a later enable into retroactive collection. The ledger intent memo is deleted rather than patched — marking re-runs every persist behind an existsSync short-circuit, so an entry pruned by a racing catch-up is re-registered at the next persist, and the path-change reset it needed disappears with it.
…rimeIntellect-ai#1986) * fix: count busy descendants in the agents-view subagents indicator The "N subagents running" indicator only tallied direct children whose row classified as running, so a busy grandchild under an idle intermediate child left every ancestor reading "N subagents" with a zero count. The tally is now a bottom-up pass over the built rows - the live per-descendant truth - replacing the direct-child increment. Indicator only, per policy: a session is Running when it works itself; idle ancestors of busy subtrees stay in Idle. Within Idle they now rank above plain idle rows, and a collapsed group's summary row renders its running count in the success color instead of dimmed so the busy subtree is discoverable without expanding. Diagnosis credit: Vincent Bailly (VincentBailly#9) traced the stale hasRunningRlmChildren snapshot and the row-walk mechanism; this adopts his walk for the indicator while rejecting the section promotion. RES-1253 * fix: tally busy descendants before heartbeat promotion inflates sections The descendant tally read row sections after propagateHeartbeatStateToAncestors had promoted idle ancestors to running, so a heartbeat-active grandchild counted its promoted parent too. The tally now runs before the promotion pass and counts intrinsically busy rows only; once that propagation pass is deleted (PrimeIntellect-ai#1967) the ordering is a no-op. RES-1253 * fix: make the descendant tally iterative so deep chains cannot overflow The recursive tally threw RangeError on a deep child chain just by opening the agents view. The nesting loop assigns every row at most one parent, so the tree is a forest: a reverse breadth-first pass over one work list computes the same bottom-up counts iteratively, with the pre-promotion ordering kept. RES-1253 * fix: classify running from the session's own work, not delegated children Second half of the RES-1253 policy, user-approved: Running means the session you enter is doing work now. Delegated child work no longer classifies a session as running - the busy-descendant badge, count, and idle ranking from the first half carry the delegation signal. hasRunningRlmChildren loses its section-classification role in both owners: classifySessionRosterStatus composes busy from the session's own activity/isSessionActive, and isActiveSessionBusy (the worker's activity axis) no longer holds a settled parent at "working" for its children. The "subagents running" status label dies with the section it had to agree with. The field itself stays on the wire, and isSessionSummaryBusy keeps it on purpose for its residency and shutdown-safety consumers (worker eviction snapshots, empty-draft eviction, busy client-owned session counts, daemon stop confirmation). RES-1253 * chore: note the running-means-the-session policy in the changelog fragment * fix: split display activity from live-work safety in the busy predicates isActiveSessionBusy served two meanings after the classification change: the display activity axis (session's own work only, correct) and worker recovery plus draft-discard (where a running RLM child is live work that dies with the worker). The recovery journal recorded busy:false for a settled parent with a running child, so a worker death skipped its interruption record and notice. The shared predicate is deleted and each meaning gets one named owner: activeActivityForSession reads the session's own isSessionActive directly, and hasLiveSessionWork (own turn or running RLM child) backs recordWorkerRecoveryState and isDiscardableDraft. No caller can grab the wrong meaning blind. RES-1253 * fix: gate destructive agent actions on live work, not display section Since Running means the session's own work, an idle parent of a running crew was branching into the delete path: killSubagent chose stop-vs- delete from the display section and stopAgentForDeletion treated activity==='working' as the only live work. Destructive actions are safety consumers of the display/safety split: one row-level hasLiveWork (own section running, busy descendants, or the wire running-children flag) now drives the stop-first branch, the deletion flow, and the confirmation verb. Also trimmed the tally comment to current behavior. RES-1253 * fix: stop live descendants when cancelling a settled child run Stopping a subagent row whose own run already settled reported 'Subagent already finished' while a nested descendant kept running: cancelRlmChildRun only cancelled the targeted run itself, and a fully released child (run removed, session retained) was never matched by id at all. Cancellation now descends where the tree lives: a settled target stops every running or queued run in its retained session's subtree (cancelRunningRlmDescendants, mirroring hasRunningRlmChildren's walk), and the returned flag stays truthful so the UI says stopped only when something stopped. RES-1253 * chore: trim comments and fold overlapping pins on the indicator branch Comment pass: multi-line narration cut to one-line invariant guards (destructive live-work gate, residency-vs-section busy split, worker- death live work) and code-readable notes deleted. Test pass: the direct- child indicator pin folded into the grandchild pin (the general case), which now also carries the idle-label and wire-flag assertions. * fix: cancel and descend at every node of the rlm cancel walk Cancelling a live child run aborts it, and abort cascades into the child's ACTIVE runs - but running work retained under a settled descendant of that child was the end of the line: neither the cascade nor the cancel walk visited it. cancelRlmChildRun now descends into the target's session after cancelling it, and cancelRunningRlmDescendants descends at every node instead of treating running runs as leaves, so cancellation is exhaustive over the subtree regardless of each node's run state. The returned flag stays truthful. RES-1253
…ubagent lineage (PrimeIntellect-ai#2021) resolveRuntimeSessionOptions now whitelists semanticParentSessionId and semanticSpawnedByRequestId, so sessions created through the production runtime factory (daemon workers, runtime hosts) register spawn lineage; the factory is extracted as createDefaultRuntimeFactory so the pin exercises the real path instead of a forward-everything test factory. A split-turn summary slice resolving after a sibling already failed the compaction now settles as request_failed instead of pushing into the drained slice list and staying in-flight forever.
) * fix: count busy descendants in the agents-view subagents indicator The "N subagents running" indicator only tallied direct children whose row classified as running, so a busy grandchild under an idle intermediate child left every ancestor reading "N subagents" with a zero count. The tally is now a bottom-up pass over the built rows - the live per-descendant truth - replacing the direct-child increment. Indicator only, per policy: a session is Running when it works itself; idle ancestors of busy subtrees stay in Idle. Within Idle they now rank above plain idle rows, and a collapsed group's summary row renders its running count in the success color instead of dimmed so the busy subtree is discoverable without expanding. Diagnosis credit: Vincent Bailly (VincentBailly#9) traced the stale hasRunningRlmChildren snapshot and the row-walk mechanism; this adopts his walk for the indicator while rejecting the section promotion. RES-1253 * fix: tally busy descendants before heartbeat promotion inflates sections The descendant tally read row sections after propagateHeartbeatStateToAncestors had promoted idle ancestors to running, so a heartbeat-active grandchild counted its promoted parent too. The tally now runs before the promotion pass and counts intrinsically busy rows only; once that propagation pass is deleted (PrimeIntellect-ai#1967) the ordering is a no-op. RES-1253 * fix: make the descendant tally iterative so deep chains cannot overflow The recursive tally threw RangeError on a deep child chain just by opening the agents view. The nesting loop assigns every row at most one parent, so the tree is a forest: a reverse breadth-first pass over one work list computes the same bottom-up counts iteratively, with the pre-promotion ordering kept. RES-1253 * fix: classify running from the session's own work, not delegated children Second half of the RES-1253 policy, user-approved: Running means the session you enter is doing work now. Delegated child work no longer classifies a session as running - the busy-descendant badge, count, and idle ranking from the first half carry the delegation signal. hasRunningRlmChildren loses its section-classification role in both owners: classifySessionRosterStatus composes busy from the session's own activity/isSessionActive, and isActiveSessionBusy (the worker's activity axis) no longer holds a settled parent at "working" for its children. The "subagents running" status label dies with the section it had to agree with. The field itself stays on the wire, and isSessionSummaryBusy keeps it on purpose for its residency and shutdown-safety consumers (worker eviction snapshots, empty-draft eviction, busy client-owned session counts, daemon stop confirmation). RES-1253 * chore: note the running-means-the-session policy in the changelog fragment * fix: split display activity from live-work safety in the busy predicates isActiveSessionBusy served two meanings after the classification change: the display activity axis (session's own work only, correct) and worker recovery plus draft-discard (where a running RLM child is live work that dies with the worker). The recovery journal recorded busy:false for a settled parent with a running child, so a worker death skipped its interruption record and notice. The shared predicate is deleted and each meaning gets one named owner: activeActivityForSession reads the session's own isSessionActive directly, and hasLiveSessionWork (own turn or running RLM child) backs recordWorkerRecoveryState and isDiscardableDraft. No caller can grab the wrong meaning blind. RES-1253 * feat: show token and cost totals on agents view rows Every agents view row with usage data now reads `<input>/<output> | $<own> ($<recursive>)` in the details cell, replacing the message-count detail. Own numbers are the session's whole-file spend (every branch, forks included, attributed child usage subtracted) - the money answer, deliberately broader than /usage's current-branch context answer, and identical for resident and on-disk rows so nothing shifts at passivation or revival. The recursive total rides the same reverse breadth-first traversal as the running-subagent tally, summing each descendant row's own cost exactly once. Producers: resident summaries compute the total from the already-loaded entries, memoized until entries change (0.27 ms measured cold at 10k entries); saved rows accumulate it inside the existing mtime/size-cached catalog scan, so old files get costs with no new IO and no new cache. Entries over the scan's max line length keep their existing skip, so a giant assistant message can undercount slightly. Schema revision 26 publishes the new optional summary and saved-row field. RES-1258 * feat: final row usage format with arrows and conditional recursive cost Details cell now reads `↑12k ↓1.2k · $0.42 ($1.10 w/ subagents)`; the parenthesized total renders only when descendants added spend visible at cent rounding, so leaf rows read `↑500 ↓50 · $0.68`. Arrow glyphs follow the existing TUI vocabulary (keybinding hints, token status) and the width pipeline already treats them as single cells. RES-1258 * fix: one own-spend truth across scan, loader, and filters Review round on the usage feature: - The catalog scan subtracted attributed child usage from disk values that (in append-only files) still carry the assistant's ORIGINAL model-response usage, double-subtracting and shifting numbers at passivation. Full-file rewrites (migrations, forks) can also persist the already-folded aggregates, so raw summation is wrong for those files instead. The scan now mirrors the loader exactly: fold each attribution's aggregate onto its target, then subtract the child usage - both disk representations cancel to the same own spend the resident computation reports. The equality pin now goes through a real v3 flushed-then-attributed fixture (the old fixture had no attribution, which is why it never caught this). - Recursive cost was computed over search-filtered rows, so a matching parent showed an incomplete "w/ subagents" total. The rollup now comes from the unfiltered record hierarchy (computeRecursiveCosts over the unified index) and filtering can no longer change the number. - Zero-spend sessions published 0/0/$0.00 live and nothing once passivated; sessionUsageSummaryFrom now returns undefined for zero totals, shared by both producers, so rows stay age-only until real spend exists. RES-1258 * fix: gate destructive agent actions on live work, not display section Since Running means the session's own work, an idle parent of a running crew was branching into the delete path: killSubagent chose stop-vs- delete from the display section and stopAgentForDeletion treated activity==='working' as the only live work. Destructive actions are safety consumers of the display/safety split: one row-level hasLiveWork (own section running, busy descendants, or the wire running-children flag) now drives the stop-first branch, the deletion flow, and the confirmation verb. Also trimmed the tally comment to current behavior. RES-1253 * fix: stop live descendants when cancelling a settled child run Stopping a subagent row whose own run already settled reported 'Subagent already finished' while a nested descendant kept running: cancelRlmChildRun only cancelled the targeted run itself, and a fully released child (run removed, session retained) was never matched by id at all. Cancellation now descends where the tree lives: a settled target stops every running or queued run in its retained session's subtree (cancelRunningRlmDescendants, mirroring hasRunningRlmChildren's walk), and the returned flag stays truthful so the UI says stopped only when something stopped. RES-1253 * feat: render the usage cell unconditionally, zeros included Every agents view row now renders the full details cell `↑<up> ↓<down> · $<own> ($<total> w/ subagents)` over four defaulted numbers - no presence branching, no thresholds, no conditional paren. A session that never spent reads `↑0 ↓0 · $0.00 ($0.00 w/ subagents)`, and a zero-usage parent with billed subagents shows its subtree spend instead of hiding it. RES-1258 * chore: trim comments and fold overlapping pins on the indicator branch Comment pass: multi-line narration cut to one-line invariant guards (destructive live-work gate, residency-vs-section busy split, worker- death live work) and code-readable notes deleted. Test pass: the direct- child indicator pin folded into the grandchild pin (the general case), which now also carries the idle-label and wire-flag assertions. * chore: cut the usage feature's comments and collapse its pins Comment pass (>80%): 24 added comment lines down to 4 one-line invariant guards (loader-fold cancellation in the scan, passivation-invariance on the resident getter, unfiltered rollup, the revision note). Test pass: the scan-accumulation and resident-equality pins merged onto one forked- and-attributed fixture; the descendant-rollup and filtered-total pins merged with a grandchild; the format pin asserts all four row shapes from one fixture; redundant zero-case and permutation tests deleted. * fix: cancel and descend at every node of the rlm cancel walk Cancelling a live child run aborts it, and abort cascades into the child's ACTIVE runs - but running work retained under a settled descendant of that child was the end of the line: neither the cascade nor the cancel walk visited it. cancelRlmChildRun now descends into the target's session after cancelling it, and cancelRunningRlmDescendants descends at every node instead of treating running runs as leaves, so cancellation is exhaustive over the subtree regardless of each node's run state. The returned flag stays truthful. RES-1253 * fix: passivated RLM descendants survive restarts as saved-catalog rows An RLM child's transcript lives in session-artifacts, which the saved- session scan never visits. While something resident remembered the child its row survived; after a supervisor restart an inactive parent's passivated descendants silently disappeared from the agents view, and their spend vanished from the parent's subtree total. Both list_saved_sessions composition points now append live spawn-ledger children the scan missed, through one shared walk: readSessionInfo per descendant, parentSessionPath/rlmDepth backfilled from the edge, deleted edges excluded. The view already renders saved-only descendants, merges resident duplicates by session identity, and rolls saved usage into recursive totals, so restored rows restore the money with no view change. Measured at 300 sessions with 36 passivated descendants: scan 33ms, merge +9ms (cold ledger, ~0.25ms per descendant). RES-1262 RES-1258 * feat: compaction and branch-summary calls bill the session they serve Summarization was invisible money: the compaction model call(s) - two on a split turn - and branch-summary calls returned usage that was dropped on the floor, so a session that compacted often under-reported what it actually cost. The summarizer responses' usage is now folded (both split-turn slices) and persisted on the compaction/branch_summary entry, and both own-spend producers add the same term: own spend = fold(assistant usage) + fold(summarization usage) - attributions, identical resident and scanned. Old entries without the field fold as zero; a failed compaction persists nothing, so failed-call billing is not recoverable - there is no committed entry to carry it. RES-1258 * fix: harden the passive-descendant catalog merge (round 2) Three review findings on the merge, all real: the transcript header's parentSession can point at a forked-away ancestor, so the ledger edge is now the authoritative topology (family() semantics) for parent and depth; ledgers are per sessions-dir family, so each catalog request now reads the ledger of the directory it lists instead of always the default one; and a broken ledger (unreadable, over-quota) no longer fails the whole list_saved_sessions response - the merge logs and returns the scanned catalog. RES-1262 * chore: consolidate the accumulated review-round pins The worker-daemon catalog pins from three rounds folded onto one fixture: restart rows, per-family ledger isolation, and broken-ledger degradation now share the two-family scaffold. Redundant assertions and narration comments cut; every behavior keeps exactly one pin. * test(coding-agent): align unified view regression with usage rows --------- Co-authored-by: Seth <seth@primeintellect.ai>
William Zhang (phytal)
deleted the
feat/upstream-0.9.1-durable-coordination
branch
September 4, 2026 02:42
Zero ReviewSummary
FindingsNo actionable findings. |
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.
Summary
Updates the Zero-owned Prime Agent fork to the current external-upstream 0.9.1 baseline and reapplies the durable shared task-coordination runtime on top.
This is a semantic rebase for
zeroset-inc/prime-agentonly. It replaces the fork's obsolete 0.7.12 lifecycle portions with the current upstream implementations while retaining the Zero-specific coordination layer.Validation
npm run checkgit diff --checkSupersedes the 0.7.12-targeted coordination PR #23.
No-Ticket: internal fork synchronization and runtime hardening follow-up.