GitHub owner/repo slugs are case-insensitive; `gh repo view` returns
the canonical casing while callers may pass any casing. The new guard
used strict equality, so a caller in the correct repo who typed
`owner/repo` while the canonical form was `Owner/Repo` was forced to
pass a redundant `branch`/`run` selector. Normalize both sides via
toLowerCase() before deciding the cwd is a different repository.
Regression test covers the casing-only match.
Refs #1949#1951
executeRunWatch passed undefined for the explicit `repo` to
resolveGitHubRepo, so a call like
`{op: "run_watch", repo: "owner/cxf", branch: "main"}` from a nested
or umbrella workspace silently fell through to `gh repo view` in cwd
and streamed `watching <sha> on <cwd-repo>` against the wrong
repository.
Route params.repo through resolveGitHubRepo so the explicit owner/repo
wins over both cwd inference and run-URL inference. When no `branch`
or `run` selector is given, refuse to derive the watched commit from
`git HEAD` unless the cwd actually points at the resolved repo —
otherwise raise a ToolError telling the caller to pass `branch` or
`run` instead of silently rebinding to an unrelated commit.
Also deduped resolveSearchRepoScope's best-effort cwd resolution into a
shared tryResolveCurrentRepo helper used by the new guard.
Fixes#1949
- Parsed zip metadata via central directory and lazy ranged reads.
- Inflated member contents only when a specific entry is read.
- Prevented large or corrupt zips from freezing directory reads.
- Added selectorLineRanges to extract ranges from raw/conflicts selectors.
- Routed internal URLs through URL-aware splitter in content search.
- Treated display-mode selectors as whole-resource searches instead of rejecting.
- Resolved @-mentions to exact existing paths only.
- Relied on the TUI @-selector to insert complete real paths.
- Dropped candidate scanning to avoid dragging in same-named files.
- Made `@`-mention resolution directory-aware so a mention ending in `/` or `\` matches only directory candidates.
- Stopped stripping the trailing separator and fuzzy-matching an arbitrary same-named file (e.g. npm scopes like `@scope/`).
- Left non-slash mentions unchanged.
- Skipped count/concurrency normalization when --bench is set.
- Errored when no OAuth accounts resolve for the provider.
- Updated flag docs to run one request per OAuth account.
- Added `getOAuthAccesses` to resolve each stored credential once.
- Sent one live request per account, reporting TTFT and TPS.
- Streamed per-account progress with interactive status lines.
If createAgentSession failed after installing a newly created AsyncJobManager but before AgentSession took ownership, the process-global singleton stayed installed. The new singleton guard then caused the next top-level session to skip constructing a scoped manager, disabling async bash/task support.
The startup-error cleanup now clears the singleton only when it still points at the newly created manager, disposes that manager, and then continues the existing registry/kernel cleanup. The regression test forces a startup failure after singleton installation and verifies the next top-level session can create and use its own async manager.
AgentSession now stores the same scoped AsyncJobManager reference that tools receive: owning top-level sessions use their constructed manager, subagents inherit the parent's manager, and secondary in-process top-level sessions get no manager when a singleton is already live.
getAsyncJobSnapshot and ACP delivery drains now use that scoped manager instead of AsyncJobManager.instance(), so secondary sessions cannot report or drain the primary session's background jobs. The regression test covers a secondary session created while the primary has a Main-owned running job.
- Appended newly sealed transcript blocks to native scrollback once.
- Deferred only the active live block during ED3-risk streaming.
- Hardened snapshot TTL parsing to handle whitespace-only env values.
- Added `NativeScrollbackLiveRegion` seam so components report the live suffix start.
- Stopped ED3-risk streaming from dropping sealed transcript rows above the live block.
- Appended newly sealed rows once while keeping the active tail deferred to checkpoint.
Per PR review on #1926: a secondary in-process top-level createAgentSession() that exposes bash/task/job tools would still call AsyncJobManager.instance() at execute time, register on the primary's manager, and have the primary's onJobComplete enqueue results into the primary's yieldQueue — corrupting the owning session's conversation.
ToolSession now carries an asyncJobManager reference scoped to its session: the constructed manager for top-level sessions, the inherited singleton for subagents (so their bash/task completions still flow into the spawning conversation as before), and undefined for secondary in-process top-level sessions that found a singleton already installed. bash, task, and job tools resolve the manager through ToolSession instead of the process-global singleton, so a secondary session whose tools attempt async work fails fast with the standard "Async job manager unavailable" error instead of contaminating the primary.
- Added AES-GCM cache for at-rest broker snapshots keyed on token, with URL as additional data.
- Added `onSnapshot` hook to RemoteAuthCredentialStore for persisting applied snapshots.
- Exposed cache read/write and TTL defaults through the coding-agent re-exports.
- Added `getAuthBrokerSnapshotCachePath` with `OMP_AUTH_BROKER_SNAPSHOT_CACHE` override.
Any in-process secondary createAgentSession() (e.g. the Agent Control Center's create flow in agent-dashboard.ts) was constructing its own AsyncJobManager, overwriting the process-global singleton, and then clearing it on its own dispose. The primary session still held its #ownedAsyncJobManager reference, but AsyncJobManager.instance() was undefined for the rest of the process — the task async path hard-failed with "Async execution is enabled but no async job manager is available" and only a full restart cleared it.
- sdk.ts: skip constructing/installing a second AsyncJobManager when a singleton is already live, so secondary top-level sessions share the owning session's manager instead of clobbering it.\n- agent-session.ts: scope #cancelOwnAsyncJobs so a secondary session inheriting the singleton with the default MAIN_AGENT_ID can no longer cancel the primary session's running bash/task jobs at dispose time. Subagents still reach the inherited singleton via their unique agent ids; the owning session still cancels its own jobs through #ownedAsyncJobManager.
Fixes#1923
- Added `omp dry-balance` command with model, count, concurrency, and JSON flags.
- Implemented random session-id sampling with bounded concurrency for OAuth access dry-run checks.
- Added success/failure summary generation with account and reason stats and optional JSON output.
- Set CLI exit status to 1 when any dry-balance attempt fails.
- Added the new dry-balance capability to the unreleased changelog notes.
When `async.enabled` is true but `AsyncJobManager.instance()` returns
`undefined` (orphaned-session state, host that never wired one up, etc.),
the `task` tool was returning a hard error and was unusable for the rest
of the session — even though the existing sync codepath (`#executeSync`,
which still parallelizes via `mapWithConcurrencyLimit`) was right there.
Fall back to `#executeSync` instead and emit a `logger.warn` so the
missing-manager state stays diagnosable. Background/job-poll semantics
are lost in this degraded mode, but the tool keeps working.
Fixes#1922
- Stopped leaking absolute home directory to the model in ttsr-interrupt and ttsr-tool-reminder blocks.
- Rendered rule paths as cwd-relative in-project, `~`-relative under home, else raw.
Reserved the first wrapped plain-text login URL rows above the manual-code prompt, while rendering the complete wrapped URL again below the prompt for terminals without OSC 8 support.
Fixes#1919
Hoisted an always-visible OSC8 'Browser login' row in the setup sign-in tab so wizard body clipping never hides both the clickable link and the focused manual-code prompt. The wrappable URL text still renders below for terminals without OSC 8 support.
Fixes#1919
Rendered manual-code prompts before wrapped OAuth status lines so wizard body clipping cannot hide the focused input behind a long login URL.
Fixes#1919
Kept explicit null package imports as exclusions so exact entries and active conditions do not fall through to wildcard or fallback targets.\n\nFixes #1889
Wrapped setup sign-in OAuth status lines instead of truncating them, and added a full OSC8 login link so narrow terminals still expose the complete URL.
Fixes#1919
The session-tree selector collapsed to "No entries found" whenever the
default filter rejected every entry. On a fresh session that is the
normal case: `sdk.ts` writes `model_change` + `thinking_level_change`
at startup so model + thinking state survive resumes, and the default
filter treats both as bookkeeping. `selector-controller.showTreeSelector`
guards on `tree.length === 0` so the selector still opens, and the user
sees an unexplained empty panel with `(0/0)` next to a "Recent sessions"
list that does contain data.
Split the empty-state branch into three shapes:
- `flatNodes.length === 0` → unchanged "No entries found".
- `searchQuery` non-empty → "No entries match search \"…\"" plus a
Backspace hint, with the real total in `(0/N)`.
- otherwise → "N entries hidden by the current filter [mode]" plus
"Press Alt+A to show all, Alt+D for default", with the real total in
`(0/N)`.
So the fresh-session case now explains why the panel is empty and how
to widen it instead of reading as "/tree is broken".
Fixes#1909
When an HTTP MCP server returns invalid_grant (or invalid_token / revoked /
plain 401 from the token endpoint) during OAuth refresh, MCPManager
previously logged "MCP OAuth refresh failed, using existing token" and
re-attached the stale access token as Authorization: Bearer on every
subsequent request. The next tool-load 401'd with invalid_token, future
sessions repeated the loop, and the only recovery was to hand-clear the
credential row in agent.db. Reported with Logfire as the trigger; any
remote HTTP MCP that rotates / revokes refresh tokens is affected.
#resolveAuthConfig now reuses pi-ai's isDefinitiveOAuthFailure classifier
(same one auth-broker and AuthStorage use for first-party providers): on
a definitive failure it calls AuthStorage.remove(credentialId), drops the
Bearer entirely, and the next request surfaces a clean auth error so the
user can /mcp reauth <server> (or /mcp unauth) to recover. Transient
failures (network/fetch failed/ECONNREFUSED) still fall back to the
existing token to ride out blips.
Verified with new mcp-manager-oauth-refresh.test.ts (invalid_grant, 401,
transient fallback, happy-path rotation). The full mcp-* test set
(45 tests across 5 files) still passes.
Fixes#1908
The github provider registered context-files (.github/copilot-instructions.md)
and instructions (.github/instructions/*.instructions.md), but no skills
capability — so .github/skills/<name>/SKILL.md, the layout GitHub documents
for Copilot Agent Skills, was silently never discovered. The skill://
URL resolved to 'Available: none' and nothing surfaced in the system prompt.
Register a skill capability on the github provider (priority 30, project-only)
pointing at .github/skills/ and reuse scanSkillsFromDir with
requireDescription: true to match the Agent Skills spec and the sibling
native/omp-plugins providers. Pin the wiring with a discovery test that
loads the skills capability scoped to the github provider against a temp
cwd containing a SKILL.md, and a negative case that drops a skill missing
a description.
Fixes#1906
Coalesced synchronous Hindsight routing setting hooks into one serialized
session rebuild so cwd reloads and multi-setting updates cannot have
multiple continuations capture the same old state and leak fresh
session listeners.
Also re-read the current state after awaiting the previous queue flush
before installing the replacement state, so an unexpected concurrent
owner cannot leave the actual current state undisposed.
Fixes#1902
Mid-session edits to hindsight.bankId / bankIdPrefix / scoping kept the
active HindsightSessionState pinned to the bank selected at session
start, so retain/recall/reflect calls landed in the stale bank. Settings
hooks now fire onHindsightScopeChanged; the backend rebuilds the
primary state against the recomputed scope, disposing the previous one
after flushing its queue so queued tool-initiated retains still land in
the bank they were enqueued for.
Also:
- Renamed ensureBankMission to ensureBankExists. The old version
skipped creation entirely when bankMission was blank, so the first
mental-model POST (auto-seed) could land against a never-PUT bank.
Bank creation is now idempotent and unconditional, and runs before
mental-model bootstrap.
- Fixed AgentSession.dispose to flush the retain queue BEFORE clearing
the session state pointer. Reversed, HindsightRetainQueue.#doFlush's
identity guard would see the cleared pointer and drop the spliced
batch with a 'session vanished' warning.
- Snapshotted hindsightScopeCallbacks before iterating because each
rebuild subscribes a fresh callback inside the same fire; iterating
the live Set would spin.
Fixes#1902
Handled omp://docs as an embedded documentation search root alongside omp://.
Added regression coverage for searching embedded docs through the docs alias.
Fixes#1898
Accepted omp://docs as the embedded documentation root and mapped docs-prefixed paths to the generated documentation index.
Added regression coverage for omp://docs and omp://docs/tools/read.md resolution.
Fixes#1898
- Added NativeScrollbackLiveRegion seam reporting where a component's transient suffix begins.
- Pinned the live block and chrome below it out of native history during foreground streams on ED3-risk terminals.
- Consumed pending forced scrollback wipes while pinned to avoid yanking a scrolled-up reader (#1682).
Logged structured session-title generation skip and failure outcomes with session/model context, and prevented credential lookup errors from escaping into the caller's swallowed promise path.
Added regression coverage for missing and failing title credentials.
Fixes#1892