The CI harness exports PI_TEST_RUNTIME=1, which the wrapper subprocess
inherited; isBunTestRuntime() then suppressed unref in the worker client
and deterministically kept the wrapper alive until the test timed out.
Override PI_TEST_RUNTIME=0 in the wrapper env and restore the 10s bound.
- Updated sdk-tool-activation expectations for 386385f18b: sessions
without a granted write tool keep extension/SDK tools top-level and
allocate no xd:// state instead of auto-granting write.
- Raised the unref'd-worker parent-exit repro to a 30s timeout; the 10s
ceiling SIGTERMed the wrapper (exit 143) on shared-core CI runners.
386385f18b made xd:// mounting require both granted transport halves and
stopped auto-granting write; a read-only session now surfaces deferred
MCP tools top-level. The test still encoded the old auto-grant contract.
- Parsed OpenRouter reasoning effort ladders and defaults during discovery.
- Preserved explicit thinking metadata from models.yml patches.
- Regenerated the catalog and covered both regression paths.
Fixes#7307
- Make over-context models selectable in the model picker by graying them instead of disabling them.
- Trigger automatic session compaction with the current model prior to switching when an over-context model is chosen.
normalizeGeneratedTitle only guarded emptiness and the none sentinel, so
when the tiny title model ignored the titling task and answered the first
user message, its full one-line reply became the session title verbatim.
Bound accepted titles to 80 chars / 12 words and return null past that,
deferring titling to the next user turn. Both the online and local-worker
paths funnel through this normalizer, so both are covered.
Fixes#7303
- Prevent xdev state allocation and tool mounting in sessions lacking a write tool.
- Expose discoverable tools top-level instead of auto-granting write transports.
- Change the default MCP JSON-RPC request ID format from snowflake strings to sequential integers.
- Update server configuration schema, connection equivalence checks, and tests to reflect the new integer default.
Routed direct custom-message conversion through the collab steering transform so side requests and compaction see the same enveloped user turn as primary requests.
Extended the regression test to exercise convertToLlm without transformContext.
Converted user-attributed collab prompt frames to prioritized user messages only on the model-facing path, preserving guest details in persisted transcript frames.
Added regression coverage for the provider role, steering envelope, and retained guest attribution.
Fixes#7288
The compiled registry enumerated each `exports` wildcard with a single-level
glob and explicitly skipped any key containing a slash, so a nested subpath
like `slash-commands/helpers/active-oauth-account` never entered the bundled
registry. Node matches `*` across `/`, so that import is legitimate: it
resolves from source, then falls through to `Bun.resolveSync` inside a
compiled binary and dies under bunfs. Reproducible on the published 17.2.1
binary with a real extension (`quota-hud.ts`).
Enumeration is now recursive, with every path segment held to the same
private/hidden rules as the leaf, so a `.private/` or `_internal/` directory
is no more exported than a private file. Directory index modules stay
excluded: `./x/*` must not serve `x/y` from `y/index.ts`, which Node would
not resolve either.
The move fence bumped #diskEpoch, which no-oped any disk task already queued at the prior epoch (e.g. a header-only ensureOnDisk materializing rewrite), losing explicitly materialized ACP/draft sessions. Gate the append hot path on #sessionFileRelocating instead of a fresh epoch, so prior disk work still drains.
Fixes#7270
A fenced append followed by a Ctrl+C flushSync in the post-rename, pre-repoint window rewrote the full body to the old path, recreating the orphan. Sync rewrites now defer while the session file is relocating.
Fixes#7270
Rebuilt advisor runtimes with the rediscovered context files so advisor turns stop evaluating against stale AGENTS.md instructions after /reload-plugins.
Fixes#7258
Threaded the session's disabledExtensions into context-file rediscovery so a concurrently-created session's global settings cannot toggle another session's context entries.
Fixes#7258
Rediscovered context files from the active session cwd whenever plugin prompt sources refresh, while preserving explicitly preloaded SDK context.
Covered edited and disabled context files in the current system prompt.
Fixes#7258
The TUI's CommandController special-cased backend.id === "off" for
/memory stats|diagnose, but the ACP/RPC slash-command handler in
builtin-registry.ts still fell back to the generic "not available for
the off backend" template — non-TUI users with memory.backend=off saw
the self-contradictory wording this PR was meant to remove.
Extract the shared fallback into memoryStatsUnavailableMessage()
(memory-backend/messages.ts) and use it from both CommandController
and the ACP builtin-registry handler, so the two surfaces can't drift
again.
Addresses review comment:
https://github.com/can1357/oh-my-pi/pull/7251#discussion_r3695383090
/memory stats and /memory diagnose fall back to a generic
'Memory <action> is not available for the <backend.id> backend.'
message whenever the active backend's stats/diagnose hook is
undefined. For every real backend (hindsight, mnemopi, local) this
reads fine, but the off backend isn't a backend a user picked among
several stats-capable options - it's the no-op state memory falls
back to by default - so the same template renders as 'Memory stats
is not available for the off backend.', which reads as an odd,
almost self-contradictory warning.
Special-case backend.id === "off" with wording that matches the
phrasing offBackend.status() already uses elsewhere ('Memory backend
is off.'), and add a unit test covering both the off-backend wording
and the unchanged generic fallback for a real backend (local) that
simply has no stats hook.
The regression test only asserts on a mocked showWarning call and
never renders Markdown, so it doesn't need a real theme instance;
drop the global dark-theme setup/teardown to avoid leaving the
process-wide theme singleton mutated for later suites in the same
Bun process.
A parked revive holds the same AgentRef while constructing a new session.
If the Hub tombstoned that ref before revive completed, the reviver could
still attach its session and set the terminal ref back to idle because
identity alone remained unchanged.
- Made aborted registry refs terminal: reject revival claims, late session
attachment, and status transitions out of aborted.
- Made lifecycle revival accept only an untouched detached parked ref or the
exact running session already claimed by createAgentSession; dispose and
reject every terminal/stale result.
- Added a delayed-revival regression test and claim-before-kill CAS checks.
Fixes#7250
Preserving aborted refs on dispose exposed a latent invariant break: the
executor's hard-abort path (finalizeSubagentLifecycle) set status `aborted`
and disposed the session without detaching it. With the ref now retained, it
kept a dangling pointer to the disposed session, and ensureLive returns any
non-null ref.session before its revivability check — so hub focus / transcript
chat could route into a dead session.
- finalizeSubagentLifecycle: detach the session before disposing on the
terminal hard-abort path, upholding the AgentRef invariant (session === null
when aborted).
- release(tombstone): detach before dispose too (capture the live session
first), same invariant.
- unregisterUnlessParked: preserve `aborted` refs only when already detached;
an aborted ref still holding a live session is a bug and is unregistered
rather than kept reachable.
- Regression test now asserts ensureLive rejects a tombstoned id as terminal.
Fixes#7250
A live-session hub kill did not stick: release(tombstone) awaited the
wrapped session dispose first, and createAgentSession's unregisterUnlessParked
removed any non-parked ref, so the subsequent detach/setStatus no-oped and the
ref was gone — leaving the reopen resurrection for idle/running agents.
- release(tombstone) now marks the ref `aborted` BEFORE disposing, so the
dispose guard preserves it; the session is detached afterward.
- unregisterUnlessParked now also spares terminal `aborted` refs (matching
the documented "hard-killed, terminal" retention and finalizeSubagentLifecycle).
- Regression test now uses a session stub that mirrors the real wrapped
dispose (unregister unless parked/aborted), so it fails if the tombstone is
set after dispose.
Fixes#7250