[{"name":"mcp","kind":"directory","size":0,"lastCommitHash":"b6ac5d014d99401b55820835a4966584271e9a3c","lastCommitWhen":"2026-09-16T16:20:18-07:00","lastCommitMessage":"mcp service catalog and oauth engine (#2330)\n\n* add a shared service catalog and generic mcp connections (#2256)\n\n* style(coding-agent): apply biome formatting to daemon-mode.ts\n\n* feat(coding-agent): add /plugins service catalog with verified MCP connections (ENG-6108)\n\n- Add /plugins and bare /mcp as the same searchable external-service picker with\n  honest states (Connect, Connected with verified tool counts, Reconnect,\n  Verifying, Requires setup, Disabled) and connect/disconnect actions\n- Add mcp.list_plugins, mcp.search_plugins, and mcp.list_connections host\n  requests with strict status filters, bounded limits, and honest nextCursor\n- Add local connection records (connectionId distinct from catalog serviceId);\n  verify every login with a real MCP handshake so token presence alone is\n  pending, never Connected; rejected credentials recover via Reconnect\n- Serve connected catalog services (Linear, Notion) through the generic mcp\n  runtime route: system-prompt inventory and mcp.config, no per-service wrappers\n- Drop the McpIntegration import from the kernel ready check ahead of the\n  Linear/Notion wrapper-package removal\n- mcp.connect is registered only behind an explicit UI-approved callback and\n  stays unregistered in daemon-hosted sessions (honest \"no handler\")\n\nTests: mcp-service-catalog, mcp-manager handlers, service-catalog-picker, and\nthe eng-6108 activation regression covering login -> verify -> session reload ->\nprompt inventory in the real services path without a restart.\n\n* fix(coding-agent): harden MCP verification, activation, and connection records (ENG-6108)\n\nReview follow-ups on the /plugins service catalog slice:\n\n- Verification now runs through the official @modelcontextprotocol/sdk\n  client (negotiated protocol version header, CRLF SSE support, explicit\n  session termination via HTTP DELETE) instead of a handwritten transport\n- Failures persist as fixed safe categories only; endpoint URLs,\n  server-controlled text, and tokens never reach records or messages\n- Connection records use the auth.json file-lock read-modify-write pattern\n  with unique temp files; concurrent UI/daemon writers no longer lose\n  records, and pending operations queue while a flush is in flight\n- Every verification result is bound to the exact grant it verified\n  (credential hash); stale probes after logout or token rotation are\n  discarded, never marking a newer or removed account Connected\n- An OAuth connection without a usable token is never probed anonymously\n- A login made while streaming or compacting queues automatic activation\n  at the next safe boundary (agent end / compaction end); the UI never\n  instructs the user to run /reload for MCP changes\n- /plugins and config-menu connect paths share the queue; failure categories\n  render as fixed user-readable wording\n- metadata-reviewed / legacyBuiltin rename absorbed from the catalog lane\n\nTests: probe (SDK fake server incl. CRLF/401/timeout/secret-leak), store\n(two-writer races, guard discard, mid-flight queueing), service catalog\n(stale verify after logout/rotation, non-connectable transports), manager,\nactivation queue (streaming/compaction/tool boundaries), regression suite.\n\n* fix(coding-agent): bind MCP credentials to endpoints, reconcile providers, honest connect (ENG-6108)\n\n- Endpoint binding everywhere a stored grant is consulted: catalog services and\n  user-declared servers require cred.endpoint === current URL before any token\n  fetch or probe (views, verify, dispatch inventory, demand-driven listing);\n  unbound legacy grants surface as Reconnect with the fixed credential-unbound\n  category and are recorded once on demand-driven verification\n- Provider ownership is one atomic reconciliation: legacy built-ins, eligible\n  catalog services, and user-declared OAuth servers are re-registered first and\n  only previously-owned ids unregister; the ModelRegistry reset hook calls the\n  full reconciliation, so catalog providers survive refresh and a removed user\n  override restores the catalog provider after a single manager.refresh()\n- Flush-time verification guards re-read fresh credential-source state\n  (authStorage.reload() under the backend lock), so probes that finish after\n  another client logged out or rotated the grant are discarded — no stale\n  Connected record survives across clients\n- mcp.connect (explicit-approver seam) verifies the real handshake before\n  reporting connected (tool count included) and reports fixed failure\n  categories on rejected handshakes\n- /plugins stdio user servers get a real management action: Enter disables the\n  server in global settings, refreshes providers, and reloads; already-disabled\n  and vanished entries report honest status\n- Catalog views keep the Reconnect action on error rows and add unverified/\n  unbound view tests; all oauth test fixtures now carry bound endpoints\n\nMinimal type-only addition in connection-probe.ts (lifecycle lane owns the file):\nMCP_PROBE_ERRORS.UNBOUND_CREDENTIAL.\n\nTests: manager 29, service-catalog 23, activation-queue 8 (incl. stdio\nmanagement), probe 7, store 6, picker 4, regression 2, slash-commands 18 —\n97/97; reviewer's mcp-service-safety suite verified 12/12 green against this\ntree (their file, their branch).\n\n* feat(ai): merged MCP service catalog with deterministic importer (ENG-6108)\n\n* feat(ai): metadata-reviewed catalog status, legacy builtins, local JSON sources, literal endpoint checks (ENG-6108)\n\n* fix(ai): harden local catalog loader and endpoint checks (ENG-6108)\n\n* feat(coding-agent): shared catalog resolver, local sources, per-account connections (ENG-6108)\n\n- resolveMcpServiceCatalog merges the built-in SERVICE_CATALOG with\n  settings-declared local sources (Settings.mcpCatalogSources): ~-expanded\n  before loadLocalServiceCatalog (which does no expansion), declared-but-missing\n  files and cross-source duplicate ids surface as visible diagnostics, first\n  source wins, total capped at 500 entries\n- The SAME resolution feeds the /plugins UI and the mcp host handlers; the UI\n  re-reads connection records from disk before rendering (no stale cache) and\n  shows resolver diagnostics on open\n- One-click Connect is metadata-reviewed-only (plus user-placed local files,\n  which still go through the login dialog's explicit approval); unreviewed\n  imported entries stay candidates with an honest setup hint\n- Per-account connections: \"Add another account\" allocates a distinct\n  connectionId (mcp:<serviceId>-N) with its own credential key, record, and\n  OAuth provider; the account picker lists accounts with per-account\n  Reconnect/Disconnect; alias ids join integrations, dispatch inventory,\n  mcp.config, and demand-driven verification through the same machinery; the\n  record's serviceId keeps the parent catalog id\n- test/mcp-service-catalog.test.ts settles the theme's cli-highlight preload\n  in beforeAll (deterministic fix for the pre-existing initTheme fire-and-forget\n  teardown race, evidence in ENG-6108 notes); ServiceCatalogPickerComponent\n  gains title/subtitle/hideSearch options for the account listing\n\nCatalog commits f8a4cf61a -> 50b79c3af -> be7445c3c integrated via authorized\nlocal cherry-picks; no catalog-source files modified.\n\nTests: manager 31 (alias integrations/provider lifecycle, demand-driven unbound\nrecord, reconciliation), service-catalog 29 (resolver, candidates, allocator),\nactivation-queue 9 (stdio management, add-account), probe 7, store 6, picker 4,\nregression 2, slash-commands 18 — 105/105; npm run check EXIT=0; 10/10\nconsecutive scoped runs with zero unhandled rejections, exit 0 each.\n\n* mcp: generic discovery surface, drop linear/notion wrappers (ENG-6108)\n\nAdd a bounded discovery surface to the kernel MCP module:\nlist_plugins, search_plugins, list_connections, search_tools and\ndescribe_tool, backed by host-owned catalog/connection handlers,\nwith cursor-safe tools/list pagination, explicit search scopes and\ndefensive secret scrubbing of inventory metadata only.\n\nReplace the bespoke Linear/Notion Python wrapper packages with one\ngeneric bundled mcp skill; remove the McpIntegration base class after\nauditing consumers, moving its shared helpers into rlm.mcp. Connecting\na service is a user action (/plugins or /mcp login), never an install\nor a per-service Python module.\n\n* mcp: fail-closed tool discovery, isolated schemas, redacted inventory errors (ENG-6108)\n\nFollow-up to 6514cbe6f per review:\n\n- discover() now publishes a tool map only when tools/list pagination\n  completes; a repeated or malformed continuation cursor, or exceeding\n  the page cap, raises McpDiscoveryError instead of a silent partial\n  inventory. Malformed cursors are rejected rather than read as done.\n- list_tools/describe_tool return deep copies, so caller mutations can\n  never reach the cached canonical schemas.\n- Per-connection search failures report a fixed exception type + hint\n  (McpCredentialsUnavailable and friends) instead of raw exception text;\n  host_request error text echoed by inventory calls is scrubbed of\n  credential-bearing URLs, query strings and auth headers and bounded.\n\n* mcp: never chain raw host errors in inventory wrappers (ENG-6108)\n\nFollow-up to ff738187e per review. _host_inventory raises a fixed\n\"MCP <request-type> request failed/timed out\" wrapper outside the\nexception handler, so arbitrary host/bridge error text (which can\nembed credential-bearing URLs, query strings, headers and HTTP\nbodies) is never echoed and cannot be reached through __cause__ or\n__context__ either; format_exception prints stay secret-free. The\nnow-unused redaction regex helper and bound constant are removed.\n\nRegression uses traceback.format_exception, not only str(exc), to\nassert the original exception is fully absent. Skill updated for the\nhost status vocabulary (pending, verified handshake) and PluginView\nfields (setupHint, unverified, verifiedAt/toolCount, connectionIds).\n\n* fix(coding-agent): bound MCP probe lifetime and connection-store races (ENG-6108)\n\nRoot review fixes on the lifecycle lane:\n\n- probeMcpEndpoint: the SDK passes its own controller's signal to every\n  fetch (including the cleanup DELETE and its body cancel), and the SDK\n  request timeouts only race the JSON-RPC promise, so an unresponsive\n  DELETE could hang the probe forever. One lifecycle AbortController now\n  bounds the probe: a fetch wrapper merges its signal into every\n  transport fetch, terminateSession/close are each raced against a\n  cleanup grace deadline whose expiry aborts the signal, an absolute\n  deadline timer plus a final abort end lingering body reads, and every\n  timer is cleared. Failure categories stay fixed and safe.\n- McpConnectionStore: the pre-lock exists-then-write-empty create could\n  rename an empty file over records another process just wrote; the file\n  is now created exclusively (wx, never truncating) before the canonical\n  realpath lock, with mode 0600. A failed atomic write no longer drops\n  queued mutations: the batch is unshifted back to the front of the\n  queue and retried by the next flush (ops are idempotent; verify\n  guards re-evaluate under the lock).\n- MCP_PROBE_ERRORS gains UNBOUND_CREDENTIAL (\"credential-unbound\") for\n  grants not bound to the endpoint, consumed by the service catalog.\n\nOffline regressions: never-returning cleanup DELETE, hanging response\nbody, exclusive first-create race with mode 0600, injected atomic-write\nfailure with in-order retry and stale-guard discard, plus the existing\nsuites. All fetch stubs; no live credentials or provider calls.\n\n* test(coding-agent): multi-PID first-create regression for MCP connection store (ENG-6108)\n\nRoot review follow-up: in-process instances cannot reproduce the\nfirst-create exists/write race because their pre-await work is\nserialized on one event loop. The store test file now also spawns four\nreal first-writer processes (temporary .mts worker imported through the\nproject tsx loader, no production entry point), aligned by ready\nmarkers + a file barrier, racing the first create and the locked flush\non the shared store path. Asserts distinct PIDs, all children exit\ncleanly, every record survives, and the file keeps mode 0600; children\nare terminated in a finally on every path.\n\nTest-only: no production files touched.\n\n* test(coding-agent): Windows-portable multi-PID store regression (ENG-6108)\n\nTest-only portability follow-up: the generated first-create worker\nimported the store module by resolved absolute OS path, which is not a\nvalid ESM specifier on Windows; it now imports by file URL, and the\nexact 0600 mode assertions are POSIX-only (Windows mode bits do not\nmap meaningfully). No production changes.\n\n* test(coding-agent): add service-safety adversarial regressions for MCP credentials and providers (ENG-6108)\n\nAdversarial regression suite for the ENG-6108 service-catalog safety\ncontract, red on the 4bd8abc12 baseline and green on the wave-2 fixes:\n\n- Credential binding before probe/token access: a stored credential bound\n  to a different endpoint, or unbound, is rejected before any probe call\n  and before authStorage.getApiKey - for user-declared servers and\n  catalog services, through direct verifyConnection and the demand-driven\n  mcp.list_plugins background verification; never a connected record.\n- Cross-client stale probes: two AuthStorage instances share one\n  auth.json; a probe finishing after a second client logs the grant out\n  or replaces it mid-flight must not persist a connected record or\n  verifiedAt for the current grant.\n- OAuth provider lifecycle: a full OAuth registry reset (through the\n  real createAgentSessionServices + modelRegistry.refresh seam)\n  preserves non-bundled catalog providers, and removing a user override\n  restores the catalog provider after one refresh.\n\nOffline-only: injected probes, denied fetch, per-test temp credential\nand connection stores, no live endpoints or real credentials.\n\n* fix(coding-agent): durable account reservations, per-account views, honest actions (ENG-6108)\n\nFinal-review wave 4:\n\n- Account allocation is a durable reservation under the connection-store file\n  lock: reserveConnectionId applies atomically on disk (exactly one winner in\n  a cross-process race; the loser allocates the next id), pending markers are\n  visible to every client, and removeReservation is ownership-validated (id +\n  still-pending + same createdAt) so a late callback never removes or resurrects\n  another client's account. Allocation consults store records, credentials,\n  configured user servers, and catalog ids; configured names are never taken.\n- Views aggregate every account of a service: connectionIds lists all accounts,\n  each account's status reflects its own bound credential, the service card\n  survives on a remaining account after the default disconnects, and the\n  inventory emits one row per account (no phantom primary row). Alias account\n  ids are searchable (search_plugins alias contract). Connections whose local\n  catalog source vanished keep a pinned descriptor at the recorded endpoint —\n  credentials are never retargeted.\n- Explicit state actions: pending accounts retry verification without a\n  relogin (the \"retry from /plugins\" path works), error accounts keep\n  Reconnect (catalog and user rows), each account gets an explicit Remove\n  row, and a verification result that could not be saved reports pending —\n  never a false Connected.\n- resolveMcpServiceCatalog isolates local-source failures per source: one\n  unreadable or invalid file becomes a bounded visible diagnostic while the\n  built-in catalog and every other source keep resolving.\n\nTests: two-client simultaneous reservation race with barrier, ownership\nvalidation, full add-second -> reload -> inventory -> disconnect-default ->\nsecond-still-manageable regression, per-account view aggregation, vanished-\nsource pinning, per-source failure isolation, state-based action tests, and\nallocation that skips configured ids.\n\nOwnership: bootstrap.ts untouched (delegated lane).\n\n* fix(coding-agent): require MCP discovery methods in the kernel ready check (ENG-6108)\n\nThe runtime ready check only asserted mcp.list_tools/mcp.call_tool; the\nfive new discovery methods were validated once by the bootstrap import\nscan, so a cached kernel venv with an old prime-agent-runtime could\npass readiness. The check now requires callables for list_plugins,\nsearch_plugins, list_connections, search_tools and describe_tool, and\nfails with an actionable message so the venv is rebuilt instead of\nsilently reused.\n\nRUNTIME_READY_CHECK is exported so the new regression executes the\nexact shipped check against a real project runtime: it accepts the\ncurrent runtime, and rejects a runtime simulated as old by deleting\neach discovery method in-process (not a text-grep test). No provider\ncalls, credentials, or real daemon involved.\n\n* fix(coding-agent): guarded account credential commits and computed account status (ENG-6108)\n\nFinal-review wave 5:\n\n- Reservations resolve only after the durable record write COMMITS: the\n  store defers reserve/remove/finalize callbacks past writeFileAtomicSync,\n  resolves them false on write failure, and drops the one-shot ops instead\n  of requeueing them — a failed write can never produce a ghost reservation\n  a login could start on (injected-write-failure regression: no marker, no\n  requeue surprise, retry works)\n- Ownership is an opaque random attempt nonce (not createdAt): cancel and\n  the NEW successful-commit guard both validate it under the same store\n  lock. Add-account logins stage the OAuth credential under\n  mcp:<id>--<attempt> and move it to the real account key only inside a\n  guarded finalizeAttempt commit — a late login success after remove or\n  replacement discards the staged credential and never touches the\n  account (regressions: late-success-after-remove and same-id-new-owner,\n  both assert the final credentials)\n- Per-account status is one computed helper (accountStateFor): credential\n  binding, expiry, and the record combine through httpConnectionStatus, and\n  the plugin aggregate, connection inventory, and account picker all use\n  it — previously-connected records with unbound, retargeted, or\n  expired-no-refresh credentials report Reconnect everywhere (6\n  regressions across default and alias accounts)\n- Catalog metadata aliases ride the view DTO and are searchable again\n  (regression: an alias absent from label/id/description matches)\n- Vanished-source pins are their own trust path (never localSource trust;\n  never one-click connectable)\n\nTests: 147/147 across 10 files; npm run check EXIT=0; 10/10 consecutive\nscoped runs with zero unhandled rejections, exit 0 each.\n\n* fix(coding-agent): exactly-once account transactions and locked disconnects (ENG-6108)\n\nFinal-review wave 6 (transaction issues):\n\n- Every one-shot store callback (reserve/removeReservation/finalizeAttempt/\n  removeAccount) settles EXACTLY once: deferred callbacks resolve with the\n  commit outcome (not `didCommit && resolve(true)`), losing and throwing\n  branches resolve false inline, one-shot ops are dropped on write failure,\n  and trailing ops in a failed batch can never hang — batched-two-reserves\n  write-failure regression included\n- Account removal runs credential deletion AND record removal under the SAME\n  store lock (new store.removeAccount; disconnect, remove-action, and\n  /mcp logout all use it): a concurrent finalize can never re-add an orphan\n  credential after a disconnect — interleaving regression on real stores\n  covers both orders (remove-then-finalize loses ownership; finalize-then-\n  remove clears record AND credential)\n- finalizeAttempt compensates: if the RECORD write fails after the credential\n  move, the compensate callback runs under the same lock and restores the\n  staged credential (the real key was empty before the attempt), so a failed\n  finalize is all-or-nothing; the flow then releases the reservation and\n  reports honestly — UI regression asserts the real key untouched, staged\n  restored+discarded, reservation released, and the honest message\n\nTests: 151/151 across 10 files; npm run check EXIT=0; 10/10 consecutive\nscoped runs with zero unhandled rejections, exit 0 each.\n\n* fix: honest MCP account recovery edges (ENG-6108)\n\n- removeAccount logs a credential-only integration out even when no\n  connection record exists; outcomes are typed (removed, credential-only,\n  missing, failed) and every UI route maps them honestly.\n- finalizeAttempt registers its rollback BEFORE any side effect: a\n  throwing or partial commit is compensated under the same lock, and a\n  FAILED rollback surfaces an explicit recovery-required state with the\n  credential retained instead of a silent false.\n- the guarded commit refuses to clobber a credential an ordinary login\n  wrote at the account key mid-flight, and the rollback only ever undoes\n  its own write; a bystander credential survives byte-for-byte.\n- one-shot store operations settle cleanly when the file lock itself\n  cannot be acquired; durable operations requeue (pre-splice).\n\n* fix: atomic MCP credential moves and honest partial logouts (ENG-6108)\n\n- staged logins move their credential to the account key with a new\n  disk-authoritative compare-and-set under the auth backend's own file\n  lock: moveStagedCredential (set-if-absent, returns the exact moved\n  credential), restoreCredentialIfAbsent, and removeIfCredentialMatches\n  (exact full-object own check, not access-token equality). A bystander\n  credential written by another process can no longer be clobbered by a\n  get/set race across AuthStorage instances.\n- the finalize compensation restores ours only into an empty staged slot\n  and deletes only its own exact credential from the account key.\n- removeAccount now reports \"logged-out\" when the credential logout\n  committed but the record save failed: the logout is preserved, the UI\n  wording says so and never claims the account is still connected; plain\n  \"failed\" wording is state-neutral.\n- two-real-storage-instance regressions: a bystander login written after\n  staging survives byte-for-byte; a generic /logout in another client\n  before/after the finalize races atomically (last-writer-wins under the\n  backend lock, record shows an honest unbound state); an injected\n  record-write failure after the logout reports the honest partial state.\n\n* fix: disk-verified MCP logouts and logout-cancelled login attempts (ENG-6108)\n\n- the three removeAccount authCleanup paths now use removeVerified\n  (extended to return whether a credential was actually removed from\n  disk): a failed auth-file write can no longer report Removed/\n  Disconnected while the credential persists; the honest \"failed\"\n  wording is state-neutral.\n- a real generic /logout of an MCP account now cancels that account's\n  pending login attempts: runLogout calls the new\n  ProviderAuthFlowsHost.onMcpCredentialRemoved hook, and the host\n  invalidates the pending reservation under the connection store's lock\n  (new invalidatePendingAttempts), so a stale in-flight OAuth attempt\n  resolves denied and can never re-activate the logged-out account.\n  Non-MCP logouts are unchanged.\n- regressions: auth-file write failure with a fresh disk reader (the\n  credential survives, nothing claims success), a route-exact\n  cross-client pending-finalize vs real /logout test driven through the\n  selector, store-level invalidation semantics, and stale\n  interactive-mode-status fixtures repaired to the current seams.\n\n* fix: one shared store-locked MCP logout op for the generic /logout route (ENG-6108)\n\n- the route delegates the ENTIRE MCP logout before touching auth: the new\n  exported logoutMcpAccount(providerId, store, authStorage) performs\n  verified credential deletion (removeVerified) AND pending-attempt\n  cancellation in one store-locked removeAccount with the new\n  preserveCompletedRecord semantics (pending attempts cancelled,\n  completed records kept for the honest unbound/Reconnect state).\n- removeAccount now runs the verified cleanup BEFORE mutating the\n  working records map: a failed auth-file write resolves \"failed\" with\n  the record intact in memory AND on disk (no half-applied deletion),\n  and a partially-saved logout still reports the honest \"logged-out\"\n  state.\n- the id resolution is exported as resolveMcpAccountLogoutTarget:\n  exact ids win (service ids may contain \"--\"), and staged keys map\n  back only through their actually recorded attempt nonce.\n- regressions: a barrier test fires the REAL generic route inside the\n  finalize's locked commit (verified deterministically red against the\n  old logout-first ordering); store-level half-applied-deletion and\n  preserve-semantics tests; route delegation/wording tests; handler\n  tests for double-hyphen ids, staged-key mapping, and preservation.\n\n* fix: fail-closed refusal for stale staged-key MCP logouts (ENG-6108)\n\n- a staged-key logout that races a completing finalize now refuses\n  fail-closed instead of orphaning the moved credential: the shared\n  handler resolves the account from CURRENT records and CURRENT on-disk\n  auth state under the store lock (never a pre-lock cache snapshot), and\n  when the staged credential already moved onto the account key it\n  returns \"refused\" — the record and the newer credential both survive,\n  and the route reports the completed login explicitly instead of\n  claiming Logged out.\n- new removeAccountForProvider store op (one critical section: resolve\n  under lock -> disk-verified refusal checks -> verified cleanup ->\n  cancel pending / preserve completed); resolveMcpAccountLogoutTarget\n  now returns a stale variant for nonce-matched non-pending records.\n- new disk-authoritative AuthStorage.hasVerified existence check used\n  for the current-state refusal decisions.\n- regressions: the staged-key race queued behind a finalize-first move\n  (refused, record + credential survive), resolver stale semantics, and\n  the route-level refused wording.\n\n* fix: staged-key MCP logouts cancel the attempt and preserve the account shell (ENG-6108)\n\n- removeAccountForProvider no longer gates record deletion on an auth\n  existence snapshot (not atomic with another client's ordinary login):\n  a staged-key logout removes only the staged credential, INVALIDATES the\n  attempt nonce on the pending record (so the in-flight login's denied\n  path can neither finalize nor removeReservation-delete it), and\n  PRESERVES the account record — deletion stays the explicit Remove\n  action's job; a bystander credential on the real key can never be\n  orphaned.\n- the \"refused\" outcome is now state-neutral (\"This login attempt is no\n  longer current; manage the account from /plugins\") — no Logged out or\n  Connected claim from token presence; failed verified auth deletion\n  still changes no record.\n- the interim AuthStorage.hasVerified helper is removed (no auth-state\n  snapshot decisions remain); regressions pin the both-exist contract\n  (staged removed, bystander byte-for-byte, record + nonce-invalidated\n  shell preserved, old-nonce finalize denied, removeReservation no-op)\n  and the finalize-first race with the same invariants.\n\n* feat: one guarded connect operation for every MCP OAuth login route (ENG-6108)\n\n- new one-shot store ops claimConnectionId/releaseClaim: a guarded login\n  claims an EXISTING account record (any status) under the store lock and\n  cancel releases it — the record, its status, and any credential survive\n  untouched; finalizeAttempt ownership is now the EXPLICIT nonce on any\n  record status.\n- every user-facing MCP OAuth login (initial connect, error-state\n  reconnect, add account, /mcp login, generic /login service options,\n  config menu) routes through the guarded flow: claim/reserve, staged\n  OAuth, guarded finalize. The raw dialog remains internal.\n- the finalize commit is a full-identity compare-and-swap on the auth\n  backend lock: set-if-absent for fresh accounts, replace-only-if-matching\n  for intentional replacements (reconnects and credential-only legacy\n  grants); it consumes the nonce and marks the record pending-verification\n  so an old Connected status never carries onto a replaced grant; the\n  compensate RESTORES the previous credential (never delete-only, never\n  clobbering a newer writer).\n- every preserving logout clears the ACTIVE claim nonce regardless of\n  record status, so a logout cancels an in-flight reconnect too; a nonce\n  match in the resolver is always a LIVE attempt (finalizes consume\n  nonces, so the stale variant is gone).\n- new AuthStorage primitives: getVerified (disk-authoritative identity\n  capture) and replaceStagedCredential / replaceCredentialIfMatches\n  (full-identity CAS move and restore).\n- regressions: error-state reconnect (CAS, pending-verification, nonce\n  consumed, old verification state cleared), cancelled reconnect\n  preservation, late-reconnect-after-logout discard, claim/release one-shot\n  settlement on failed batch writes, rollback-restores-previous via CAS,\n  and the generic /login + config-menu delegation to the guarded hook.\n\n* fix: preserve MCP login intent and cancelled account settings (ENG-6108)\n\n* test: cover MCP account lifecycle commit safety (ENG-6108)\n\n* fix: merge-main regressions and secret-free grant binding (ENG-6108)\n\n* fix: fail closed on unproven MCP catalog entries and expired grants (ENG-6108)\n\n* fix: keep built-ins in the catalog cap and harden grant-type regressions (ENG-6108)\n\n* fix: match MCP connectors to the inline configuration pickers (ENG-6108)\n\n* fix: make MCP picker actions match settings-backed targets (ENG-6108)\n\n* feat(ai): complete the generic MCP OAuth engine (ENG-6108)\n\nThe MCP OAuth engine only handled public-client dynamic registration and\nexact endpoint/protected-resource matches: it discarded all registration\nresponse fields beyond client_id, requested every AS-advertised scope,\nissued unbounded uncancellable requests, and rejected the official\nNotion/Slack metadata shape (pathful endpoint, origin-level PRM resource)\nat discovery, so those servers could not log in or refresh at all.\n\nThe engine now follows the SDK 1.30 auth standards via pure helper/schema\nimports from @modelcontextprotocol/sdk (never the auth() orchestrator,\nwhose redirect model does not fit the staged consent UI):\n\n- Client auth-method negotiation (client_secret_basic > post > none,\n  DCR hint honored, RFC 8414 defaults) applied to exchange and refresh,\n  with a compatibility gate: a secret-only server never receives a doomed\n  no-auth request; it fails early with typed setup guidance.\n- Full RFC 7591 registration responses: persisted DCR client identity,\n  issued secret and expiry (respected on refresh), and registration-mode\n  binding. Config-supplied secrets are never persisted or embedded.\n- CIMD/SEP-991: a configured Prime-controlled client-metadata URL becomes\n  the client id when the server advertises\n  client_id_metadata_document_supported; no URL is invented or deployed.\n- SEP-835 scope precedence: configured > protected-resource > omitted;\n  authorization-server-wide scope lists are never blindly joined.\n- Narrow audience policy: the PRM resource must equal the canonical\n  endpoint or its exact HTTPS origin (URL-component compared); refresh\n  pins both values and the audience mode, fail-closed on any change, and\n  legacy exact grants never refresh against origin audiences.\n- Every request is bounded (10/15s deadlines, 256KiB bodies) and login\n  honors the host abort signal, including callback-server cleanup.\n- Token errors are typed and sanitized: whitelisted RFC error codes only,\n  fixed guidance, never raw server-controlled descriptions.\n\nAll flows run against offline fake OAuth/MCP servers in\npackages/ai/test/mcp-oauth.test.ts (39 tests: official Notion/Linear\nfixtures, Slack post-only negotiation, CIMD, refresh binding and\nrotation, abort/cancellation); no live endpoints or credentials.\n\n* feat(ai): read-only catalog metadata audit with readiness evidence (ENG-6108)\n\nAdd a reproducible read-only public metadata audit of all 103 remote catalog endpoints (public unauthenticated GETs only, bounded, DNS-pinned per request through an undici dispatcher lookup) and merge the committed evidence into the catalog offline. New additive, optional interface: setup.readiness (informational, never a Connect gate), setup.requirement, setup.fields[].kind, auth.alternatives[] per-path alternatives, and the observational auth.metadata evidence block. Placeholder-only OAuth blockers are cleared with evidence (Airtable, Shopify stay Connect-attemptable); genuine documented requirements stay hard (API keys, bearer tokens, tenant config, registered clients, transport and local-runtime limits). CIMD alone is never oauth-ready, audience-mismatched PRM documents stay unknown with notes until the approved engine audience policy lands, and local sources cannot self-assert readiness, alternatives or evidence.\n\n* docs(ai): correct stale oauth-marker consumer comment (ENG-6108)\n\nThe host keys OAuth support off authStrategy, not entry.oauth?.kind; the comment now points consumers at auth.strategy. Comment-only change.\n\n* fix(coding-agent): guard mcp account ownership and capability policy (ENG-6108)\n\n* fix(ai): adopt engine component comparison for readiness re-derivation (ENG-6108)\n\nThe importer audience rule now uses the engine component comparison: a protected-resource document must match the exact canonical endpoint or the exact origin (root-slash normalized). notion plus the 9 DCR-capable origin-mismatched entries (airtable, amplitude, appwrite, lovable, miro, rootly, vercel, windsor-ai, zoominfo) re-derive to oauth-ready and their stale not-yet-landed notes are gone; logrocket stays unknown (its resource drops the ?toolsets=all query, failing closed under the component rule); slack and hubspot stay unflipped (no dynamic client registration). Counts re-derive to 72 oauth-ready / 53 user-setup / 5 prime-restricted / 10 unknown. Catalog oauth validation now rejects client secrets explicitly instead of silently dropping them, symmetric with the client-id rejection.\n\n* feat(coding-agent): wire oauth client identity through shared mcp provider factory (ENG-6108)\n\n* fix(ai): mirror engine PRM selection in the metadata audit (ENG-6108)\n\nBugbot PRRT_kwDOSXZbXs6hvVZP + PRRT_kwDOSXZbXs6hvVZS: auditEndpoint now mirrors oauth.ts exactly - a WWW-Authenticate resource_metadata pointer is followed alone and any failure fails closed with no well-known fall-through; absent a pointer the pathful well-known is tried then the origin-level root location (SDK parity, 4xx continues); a candidate is selectable only when its document passes the engine audience component comparison and carries authorization servers. protectedResourceUrl is used at the call site instead of an inline construction. The bounded read-only audit was re-run (public GETs only, DNS-pinned, all bounds unchanged) and readiness re-derived: the reviewer obligation flips stay intact; codspeed and resend flip to oauth-ready via the SDK-parity origin-level fallback (valid documents, DCR, coherent AS); confidence-docs/-flags stay unknown with fail-closed notes (served-but-invalid documents); logrocket stays unknown with the precise fail-closed reason; slack/hubspot stay unflipped (DCR-less); shopify metadata is now available but DCR-less. Counts: 72 oauth-ready / 53 user-setup / 5 prime-restricted / 10 unknown; metadata 97 available / 6 unavailable.\n\n* fix(ai): require the exact engine fallback condition for oauth-ready (ENG-6108)\n\nBugbot PRRT_kwDOSXZbXs6hva2k: evidenceSupportsStandardOauth granted oauth-ready via origin-AS evidence for ANY no-served-JSON state, but the engine reaches its origin-level AS fallback ONLY when no resource_metadata pointer was followed and every tried well-known candidate returned exactly a 4xx (5xx, fetch errors, 2xx/3xx non-JSON bodies and served-but-invalid documents all throw with no fallback). A precise engineFallsBackToOriginAs predicate is now derived from the recorded attempts (followed or rejected pointer, served document, and per-attempt httpStatus), so fail-closed discovery stays honestly unknown. The audit script selectionNote now fires only for true fail-closed states; the all-4xx state is the engine honest fallback and records no note. The bounded read-only audit was re-run to match the corrected note semantics (aggregates stable: 103 audited, AS 97/6, DCR 78, CIMD 25, PKCE 97). Zero readiness flips - pure hardening; 7 all-4xx entries lose stale generic notes; new regression assertions pin intercom (oauth-ready via the legitimate origin-AS fallback, no note) and adobe-for-creativity (unknown, unavailable AS, no note). Counts unchanged: 72 oauth-ready / 53 user-setup / 5 prime-restricted / 10 unknown.\n\n* fix(ai): classify figma as registered-client after live DCR 403 evidence (ENG-6108)\n\nDogfooding PR #2256 head 8e77f0cc1 surfaced what the read-only audit could\nnot observe: Figma's advertised dynamic-client-registration endpoint returned\nHTTP 403 to the engine's anonymous registration POST during a real\n/mcp login figma attempt (2026-09-13), while its AS metadata still advertises\nthe endpoint with confidential-only client auth. A public metadata-only GET\nof the register endpoint returns 404, and public reports corroborate the 403\nfor anonymous DCR requests; whether self-registered Figma apps can obtain\nmcp:connect is publicly unresolved.\n\n- Audit store: the live attempt is recorded as an authorizationServer\n  registrationAttempt block with explicit provenance (the audit script never\n  POSTs registration endpoints); the script now preserves these blocks\n  verbatim on re-runs, and the $comment sanctions exactly this supplement.\n- Importer: an advertised registration endpoint whose live anonymous\n  registration was rejected (401/403) is gated evidence —\n  auth.metadata.dynamicClientRegistration flips to an explicit false (gated,\n  distinguishable from omitted \"not advertised\"), the gated finding becomes\n  the default metadata note, and the entry can never be oauth-ready\n  (fail-closed, the same honest class as DCR-less Slack/HubSpot).\n- overrides.json: figma mirrors the Slack precedent — pre-registered client,\n  registered-client requirement, prime-restricted readiness, requires-setup\n  with the 403-documented reason and FIGMA_MCP_CLIENT_ID / FIGMA_MCP_CLIENT_SECRET\n  setup fields.\n- Regenerated catalog.json + catalog.data.generated.ts via the importer;\n  counts: ready 82 -> 81, requires-setup 58 -> 59, oauth-ready 72 -> 71,\n  prime-restricted 5 -> 6 (user-setup 53, unknown 10 unchanged; sum 140).\n\nOffline verification: catalog suite 25/25, coding-agent catalog eligibility\n12/12, tsgo --noEmit clean, and both catalog scripts typechecked under the\nrepo base config.\n\n* drop the revert changelog fragments\n\nthe reland makes the \"removed\" entries moot before any release cut them;\nthe release notes should only describe the additions.\n\n* feat(ai): cut the service catalog to zero-app self-serve only (ENG-6108)\n\n2026-09-14 product decision: Prime maintains ZERO provider OAuth apps, so\nthe catalog advertises only what works self-serve — dynamic client\nregistration (readiness \"oauth-ready\") or user-supplied tokens/keys\n(\"user-setup\"). Every entry classified prime-restricted (needs a\nprovider-registered client) or unknown (no verified self-serve path) is cut\nfrom the shipped catalog; audit/engine parity, the readiness vocabulary and\nthe fail-closed classification machinery are unchanged.\n\n- overrides.json: the 16 cut providers (figma, gmail, google-calendar,\n  google-drive, mongodb-atlas, slack, adobe-for-creativity,\n  confidence-docs, confidence-flags, hubspot, logrocket, mapbox-docs,\n  shopify, sumup, synthflow, synthflow-docs) are excluded via the documented\n  excludedServers list — one entry per upstream record (18 keys: figma and\n  slack are merged from both upstreams, so both sides are cut and no ghost\n  entry can re-enter), each with a per-entry reason citing the decision and\n  the specific audit evidence. The now-dead servers overrides for excluded\n  identities were removed; their honesty content lives on in the exclusion\n  reasons.\n- Importer: the cut is enforced structurally — buildCatalog refuses to emit\n  an entry whose readiness classifies prime-restricted or unknown and fails\n  with a curation prompt (exclude with a documented reason, or curate\n  readiness with evidence), so an unverified provider can never ship\n  silently again. The gated-DCR honesty machinery (advertisedRegistrationGated,\n  explicit-false dynamicClientRegistration semantics) is kept and now\n  exported for direct unit tests, since the live case (figma) is excluded\n  rather than classified; the predicate still guards future entries.\n- Audit store: untouched — the pinned source snapshots and metadata-audit\n  results for the excluded endpoints stay committed as evidence (including\n  figma's live 403 registrationAttempt). The audit script now also retires,\n  never drops, registration-attempt evidence for servers that leave the\n  audit target set via the new top-level retiredRegistrationAttempts array,\n  so a re-run can never silently delete the figma evidence.\n- Regenerated catalog.json + catalog.data.generated.ts via the importer;\n  counts: total 140 -> 124 (http 102 -> 86; stdio 32, templated 5, sse 1\n  unchanged), ready 81 -> 71, requires-setup 59 -> 53, oauth-ready 71,\n  user-setup 53, prime-restricted 6 -> 0, unknown 10 -> 0 (sum exact).\n- Tests: mcp-catalog.test.ts now asserts the cut (all 16 ids absent, both\n  upstream sides of merged providers excluded, all 18 keys documented in the\n  import report with zero-app reasons, source snapshots and audit evidence\n  kept incl. figma's 403 attempt), pins the gated-DCR machinery directly\n  with synthetic evidence, updates the count assertions (124/71/53/0/0) and\n  keeps the byte-for-byte regeneration pin; the figma assertions now assert\n  exclusion instead of prime-restricted-in-catalog.\n\nOffline verification: catalog suite 27/27, coding-agent catalog eligibility\n12/12 (plus all 13 coding-agent mcp test files: 281 passed, 1 skipped),\ntsgo --noEmit clean at the repo root, both catalog scripts typechecked\nclean under the repo base config, biome clean on the changed files.\n\n* test(coding-agent): adapt activation-queue route host to the inline auth panel host surface\n\n* test(coding-agent): drive logout route assertions through the inline auth panel surface\n\n* fix(ai): demote confidential-only AS providers to the user-setup OAuth path (ENG-6108)\n\nLive gap found in dogfooding: Hugging Face /mcp login failed at connect with\n\"no compatible client authentication method (advertised: client_secret_basic,\nclient_secret_post)\" because the catalog classified the entry oauth-ready\nwithout ever checking token_endpoint_auth_methods_supported; the engine checks\nit at runtime and fails closed.\n\n- Extract the engine's fail-closed client-auth decision from\n  negotiateAuthMethod into the exported decideClientAuthMethod and add the\n  catalog predicates tokenAuthMethodsSupportPublicClient /\n  tokenAuthMethodsSupportConfiguredClient in packages/ai/src/mcp/oauth.ts —\n  the importer runs the SAME decision, so classification and the connect-time\n  gate share one source of truth.\n- Wire the gate into importer readiness: coherent DCR evidence with\n  confidential-only advertised methods now demotes honestly to the user-setup\n  OAuth path (requires your own OAuth app reason, registered-client\n  requirement, client-id/client-secret setup fields) instead of oauth-ready —\n  the user's own registered app, zero-app compliant, never an exclusion.\n  Advertised methods that serve not even a configured secret-bearing client\n  (mTLS-only) keep failing closed as today; omitted lists stay oauth-ready\n  per the engine's public-client spec default.\n- Regenerate catalog.json, catalog.data.generated.ts and import-report.json:\n  14 entries demote (airwallex, airwallex-sandbox, atlan, gitlab,\n  hugging-face, legalzoom, lusha, miro, monday-com, planetscale, supabase,\n  vercel, windsor-ai, zoominfo) — readiness counts move 71->57 oauth-ready and\n  53->67 user-setup; total 124 and zero prime-restricted/unknown invariants\n  hold, each entry carrying its confidential-only evidence and note.\n- Document tokenAuthMethods as required engine-compatibility capture in the\n  audit script and catalog README so future audits keep the check.\n- Tests: pin the hugging-face demotion with its confidential-only evidence and\n  the full demoted set, add the synthetic classification matrix\n  (confidential-only demotes, none/omitted stay one-click, mTLS-only stays\n  unknown) and direct predicate tests; update readiness count pins. The\n  byte-for-byte regeneration pin and the coding-agent eligibility thresholds\n  pass unchanged.\n\n* test(coding-agent): pin the MCP auth routes to the inline auth panel surface\n\nthe guarded-credential race test kept an unobserved showOverlay stub on the\nfake route TUI; it now asserts the generic /logout service selector never\nmounts an overlay popup, so MCP auth can only render through showAuthPanel\nlike #2331 provider logins. adds the changelog fragment documenting that\nevery MCP auth surface (guarded staged OAuth dialog, /login and /logout\nservice selectors, mcp-connections tab, /mcp login) mounts inline under\nthe chat with stacked closers, focus restore, and rows-aware sizing.\n\n* feat(ai): cut the catalog to one-click DCR or user token/key only (ENG-6108)\n\n2026-09-15 product decision (\"remove everything that's not one-click auth\nor api key\"): the shipped catalog advertises only one-click dynamic client\nregistration (readiness \"oauth-ready\") and user token/key entries; the\nclassification machinery and audit/engine parity are unchanged.\n\n- overrides.json: the 49 non-token/key entries — the 14 registered-client\n  entries whose authorization servers accept only secret-bearing client auth\n  (airwallex, airwallex-sandbox, atlan, gitlab, huggingface-skills,\n  legalzoom, lusha, miro, monday-com, planetscale, supabase, vercel,\n  windsor-ai, zoominfo), the 32 local-runtime stdio adapters and the 3\n  url-only tenant templates (activecampaign, jfrog, pigment) — are excluded\n  via the documented excludedServers list: one entry per upstream record\n  (64 keys: monday-com/supabase/vercel are merged from both upstreams, so\n  both sides are cut; every stdio server of the multi-server adapter plugins\n  is excluded), each with a per-entry reason citing the decision and the\n  specific evidence. The now-dead servers overrides for excluded identities\n  were removed; their honesty content lives on in the exclusion reasons.\n- Importer: the cut is enforced structurally — buildCatalog refuses to emit\n  a user-setup entry whose requirement is not token/key-shaped\n  (bearer-token/api-key credentials, a tenant config with a genuine non-url\n  field, or a legacy-transport entry that still collects a token/key) and\n  fails with a curation prompt, so a registered-client demotion, a\n  local-runtime adapter or a url-only template can never silently re-derive.\n  The auth-methods honesty machinery (decideClientAuthMethod parity,\n  confidential-only demotion, tokenAuthMethods capture) is unchanged and\n  stays active for the survivors.\n- Audit store and source snapshots: untouched — evidence for the excluded\n  endpoints stays committed as history.\n- Regenerated catalog.json + catalog.data.generated.ts + import-report.json\n  via the importer; counts: total 124 -> 75 (http 86 -> 72, templated 5 -> 2,\n  sse 1 unchanged, stdio 32 -> 0), ready 57 unchanged, requires-setup 67 ->\n  18, oauth-ready 57, user-setup 18, prime-restricted 0, unknown 0 (sum\n  exact); api-key strategy 32 -> 16.\n- Tests: mcp-catalog.test.ts pins the cut (all 49 ids absent, both upstream\n  sides of merged providers excluded, all 64 keys documented in the import\n  report with 2026-09-15 reasons, source snapshots and audit evidence kept,\n  the shipped user-setup set is exactly the 18 token/key entries), keeps the\n  confidential-only machinery matrix (now pinned on the committed\n  huggingface-skills audit evidence plus the synthetic matrix), adds the\n  structural invariant regression (lifting the gitlab exclusion fails the\n  import with the curation prompt), updates the count assertions (75/57/18/\n  0/0) and keeps the byte-for-byte regeneration pin; coding-agent\n  mcp-service-catalog count comment + eligibility sweep thresholds updated\n  for the 75-entry catalog (api_key >= 10 still holds at 16; unknown 53).\n\nOffline verification: ai catalog suite 29/29, ai mcp-oauth 40/40, all 13\ncoding-agent mcp test files 276 passed 1 skipped (catalog eligibility 12/12,\nservice catalog 63/63), tsgo --noEmit clean at the repo root, biome clean on\nthe changed files.\n\n* fix(tui): separate the oauth url from the browser-step text with a blank line\n\n* fix(tui): keep the service-catalog picker at one description line with stable frames\n\n* feat(tui): persist MCP connect outcomes as refinement-style chat entries (ENG-6108)\n\nConnect outcomes (\"Connected Linear (12 tools verified)\" and its\nsaved-but-unverified variants) were transient status lines that vanished;\nthey now persist in the chat transcript like harness refinement notices.\n\n- Add the mcp_connection_outcome custom message with structured details\n  (label, login/retry source, connected/unverified/unsaved verification\n  state, tool count, account id, activation) and the exact legacy wording\n  as its content; keep it out of LLM context like refinement outcomes.\n- Render it with a purple refinementHeader \"◆ Connected <service> · N\n  tools verified\" header over a refinementSummary body via the\n  ExpandableEventMessage base; register live rendering and transcript\n  replay (malformed entries included) and gate both on display.\n- Route both emit sites (pending-account retry verification and the\n  guarded login completion) and their failure/partial variants through\n  completeMcpConnectionOutcome: reload, then append the durable entry via\n  the new AgentConnection.appendCustomMessage (in-process sendCustomMessage,\n  daemon legacy append_custom_message RPC). Mid-turn connects keep the\n  transient \"will activate automatically\" line and record the durable\n  entry at the next safe boundary; a failed reload marks the entry\n  \"not active in this session\". Pre-outcome login failures (eligibility,\n  claim, finalize, incomplete login) remain transient warnings.\n- Focused tests: component rendering/replay, formatter wording parity,\n  and both emit sites including queued-boundary, inactive, and\n  append-failure fallback paths.\n\n* fix(tui): flatten multi-line catalog copy before it becomes a picker line\n\n* fix(tui): repair /mcp picker chrome nits and search ranking (ENG-6108)\n\nFive chrome nits from Kevin's live testing plus the picker search:\n\n- The scroll counter aligns with the row indent (rows render their label\n  at column 2; the counter and the empty-state message now do too).\n- Enter on an accounts-picker row 1 (the account name) re-verifies the\n  account instead of disconnecting it; only the explicit Remove row\n  removes. connectServiceFromPicker's connected branch now re-verifies\n  exactly like the pending branch.\n- One blank line between the last row and the description line in both\n  catalog and accounts mode, budgeted in updateLayout so frames stay\n  height-stable.\n- Accounts search matches identity only (account label + connection\n  id): every account row shares the same description/setup hint, so\n  matching that text could never filter anything.\n- Every inline MenuPanel opens with exactly one separator rule between\n  the chat view and the picker: a titled panel draws the rule above its\n  title; a headerless panel keeps the bordered search's top border as\n  that rule, never both. Model/provider/oauth/extension/prime-team\n  pickers budget the rule through the shared inlineMenuPanelTopRuleRows\n  helper.\n- Search ranking rewritten: exact/prefix/word-start/substring matches on\n  label, service id, and aliases rank far above description/setup-hint\n  text; the identity-only subsequence fallback requires a consecutive\n  run of half the query (minimum two); descriptions match only as\n  word-start/substring. Searching vercel against a catalog without\n  Vercel now yields the No-matching-services empty state instead of\n  eight unrelated rows matched through setup-hint boilerplate.\n\n* fix(mcp): cut non-token catalog survivors and say what to paste\n\n2026-09-16 product decision, from live picker testing: the catalog ships\none-click DCR connectors and simple paste-an-API-key/token services only.\n\nCut 5 survivors with documented per-entry reasons in overrides.json:\n- cockroachdb (cockroach-labs): tenant config, only input is the\n  per-cluster `mcp-cluster-id` header, no key/token field\n- dynatrace, sourcegraph: tenant instance URL next to the token\n- paypal-sandbox: legacy SSE, the runtime cannot connect to it at all\n- render: api-key requirement with zero setup fields, nothing to paste\n\nThe importer now enforces the narrower rule structurally: a user-setup\nentry ships only when its requirement is bearer-token or api-key AND it\ncollects at least one bearer-token/api-key field. An entry the readiness\npass never classified also fails closed instead of slipping past every\nshipping guard.\n\nAll 13 surviving setup reasons are now picker copy for a human: one line,\nstarting with \"paste\", naming the entry's real field. \"requires provider\ncredentials supplied as headers\" and \"requires an auth token supplied via\nenvironment variable\" are gone; github no longer stutters the same clause\ntwice; zoom-meetings drops the upstream placeholder note that never\nchanged what the user does. A test pins the shape.\n\nCatalog: 75 -> 70 entries (57 oauth-ready, 13 user-setup), all plain\nstreamable-http. Evidence, source snapshots and audit store unchanged.\n\nENG-6108\n\n* fix(tui): drop the restated MCP connect body and add disconnect feedback\n\nKevin saw the durable connect entry print the same sentence twice: the\nheader and the body both said \"Connected Granola (6 tools verified)\".\nThe header is the outcome line, so the body now carries ONLY what the\nheader omits - added account, verification issue plus next step, deferred\nactivation - and a plain success renders as the header alone. The\npersisted `content` keeps the full legacy sentence, because the transient\nfallback and status lines still need a whole sentence.\n\nDisconnects had no durable feedback at all. A successful disconnect from\n/plugins, `/mcp logout <name>`, or the generic `/logout` route now leaves\nthe same kind of entry, in the muted theme key: not the connect purple (a\ndisconnect is no refinement) and not the error red (it succeeded) - the\ngrey /plugins already uses for \"Not connected\". Only outcomes that really\nremoved the credential (removed, credential-only, preserved) get an\nentry; \"logged-out\" (partial save failure), \"failed\", \"missing\", and the\nstate-neutral \"refused\" keep their honest warnings and record nothing, so\nno entry can claim a disconnect that did not finish.\n\nThe generic /logout route still runs ONE store op and ONE reload: the\nhost records the outcome under its existing delegation and the selector\nappends it after the reload it already did.\n\nENG-6108\n\n* fix(tui): record the picker disconnect entry on the remove row\n\n* fix(tui): rebuild the /mcp accounts menu as a choice list (ENG-6108)\n\nKevin's live-testing redesign: the accounts menu drops its no-op search box\nand account-name row, renders '<service> MCP' with the muted description\n(wrapped, capped at three lines) above the options, and lists Reconnect /\nDisconnect <id> / Add another account in the PR #2340 selection language\n(mirrored locally until #2340 lands). Reconnect keeps the existing re-verify\npath and never disconnects; Disconnect keeps the store-locked removal and\nits durable disconnected entry.\n\nCatalog picker nits: the Connect trailing status reads in the text colour\ninstead of the accent purple, and the empty state gains one blank row above\nthe shortcuts line, aligned with the row labels (pinned at width 120).\n\n* fix(tui): stop inline pickers doubling the separator rule\n\n* feat(mcp): connect paste-a-key catalog services through an inline masked paste panel (ENG-6108)\n\n* fix(mcp): collapse the paste flow to ONE credential per service, cut named-header pairs (ENG-6108)\n\nTwo Bugbot findings, one root cause: the generic MCP runtime sends exactly\none Authorization: Bearer per connection, but the paste flow treated every\nrequired credential field as a separate mandatory prompt, and GitHub's two\nsetup fields (GITHUB_PAT_TOKEN / GITHUB_PERSONAL_ACCESS_TOKEN) were presented\nas two required tokens when they are alternative names for one PAT.\n\n- Curate GitHub's fields as alternatives via a shared field-level\n  credentialSet (\"github-pat\") in mcp-catalog/overrides.json; the paste flow\n  now prompts ONCE, labels honestly (\"GitHub personal access token\") and\n  stores the value under the first alternative's id.\n- Cut datadog and cloudinary-mediaflows from the catalog (documented\n  excludedServers reasons citing the named-header evidence: DD_API_KEY +\n  DD_APPLICATION_KEY, cld-api-key + cld-secret - one bearer can never\n  authenticate them). Catalog: 68 entries = 57 one-click DCR + 11 paste.\n- The importer now throws when a shipped requires-setup entry collects more\n  than one distinct required credential, so an unmarked multi-credential\n  entry can never silently re-derive; tests pin the guard by lifting the\n  datadog and cloudinary exclusions.\n- Simplify the host flow to one prompt/one value (no multi-field loop, no\n  per-field checkmark rows); masking, Esc-cancels-nothing-stored, the\n  empty-value guard, verification and the durable Connected outcome are\n  kept; services with genuinely distinct credentials stay honest\n  setup_required and are not pasteable.\n\n* fix(mcp): drop the dead paste-credential branch and trim the importer comment\n\n* fix(tui): render paste outcomes honestly and polish the accounts menu\n\n* fix(tui): budget the accounts frame for the trailing blank row\n\n* fix(tui): keep the /mcp picker chain open after connect, disconnect, and paste actions\n\nSelecting a row no longer drops the user back to the prompt after the action: the picker chain re-enters the right surface, rebuilt from the live catalog and connection store. Disconnecting one account reopens that service accounts menu without the removed row; disconnecting the last account, or a cancel that stored nothing, reopens the catalog; reconnect/re-verify, add-account, and paste re-enter the accounts menu with refreshed state. Esc closes the whole chain, and actions blocked before they start (login in progress, guidance, target drift) end the chain with their status line instead of re-opening, so there is no re-entry loop.\n\n* fix(tui): drop the accounts description instead of overflowing tight viewports\n\n* fix(tui): one reconnect/disconnect row per service with an account sub-picker, and left-arrow back to the catalog\n\nA service's accounts menu is now exactly three rows — Reconnect, Disconnect, Add another account — instead of a reconnect/disconnect pair per connection id. With one account, Enter acts on it directly with the existing re-verify and remove semantics. With several accounts, Enter on Reconnect or Disconnect opens a second inline picker in the same choice-list shape that lists the account ids; picking one runs the action on that connection id only, and Esc or left there returns to the accounts menu without acting. The sub-picker never nests deeper, and after an action the existing re-entry table decides the next surface (the accounts menu while an account survives, the catalog otherwise).\n\nLeft arrow from the accounts page now navigates back to the /mcp catalog — the picker chain's parent surface — remounted fresh, using the same app.modal.back binding the dialogs share; Esc still closes the whole chain, and left in the catalog stays inert. The accounts frame budget is unchanged (the option count is the only thing that grows), the budget test now pins the three-row count, and the multi-account re-entry tests drive the sub-picker end to end.\n\n* fix(tui): colour mcp outcome entries by outcome, warning for non-success\n\n* fix(mcp): keep alias OAuth providers behind the parent service catalog classification (ENG-6108)\n\n* fix(mcp): probe the origin-level oauth-protected-resource document without the endpoint query\n\n* test: bring ENG-6108 suites into the main-branch testing policy (#2336)\n\nShrink the branch's test additions from a net 16,514 to 16,610? -> net 11,610\ntest lines (the same as the branch's source additions), clear all 46 new\ntest-policy pattern violations, and consolidate per the policy's \"one file\nper module, regressions live in the owning suite\" rules. No test survives\non a timer, poll, sleep, skip, or env gate; every wait resolves on a\nconcrete deferred signal (mount hooks, probe gates, store flushes).\n\nFolded per-issue regression files into the owning module suites:\n- suite/regressions/eng-6108-inline-picker.test.ts -> the picker-chain\n  describe in test/service-catalog-picker.test.ts (mount/stale/replacement\n  settling, action routing, re-entry, settings-actions freeze, paste flow,\n  blocked actions) with vi.waitFor replaced by ui.setFocus-mount and\n  action-start deferred signals; the pure row-shape/menu layout pins\n  already covered by the component tests were deleted.\n- suite/regressions/eng-6108-service-catalog-activation.test.ts -> the\n  connect-then-activate end-to-end test in test/agent-session-services\n  .test.ts; the stored-token-pending duplicate died (already pinned in\n  mcp-service-catalog).\n\nDissolved test/mcp-account-commit-safety.test.ts; its unique invariants\nmoved to the owning suites and the rest were duplicates:\n- store->auth gap interleave + finalize nonce ownership + no-resurrection\n  -> test/mcp-connection-store.test.ts\n- CAS-absence replaceStagedCredential -> test/auth-storage.test.ts\n- /mcp login on a PENDING account, vanished-source repair (record+grant\n  evidence; bare shell refuses), and the paste-flow seam (single-credential\n  static token, cancelled/empty value, login-in-progress and target-changed\n  refusals) -> test/mcp-activation-queue.test.ts.\n\nConsolidated near-identical seams (surviving test -> invariant it owns):\n- grant-state matrix pinned ONCE in mcp-catalog-eligibility (predicate +\n  prompt + dispatch + view parity); manager's copy deleted.\n- reserved-name ownership pinned at dispatch level in mcp-manager; the\n  view-level copy and mcp-service-safety's provider-lifecycle duplicate\n  deleted.\n- cap protection pinned in mcp-service-catalog (resolver's own module);\n  mcp-catalog-eligibility's copy deleted.\n- remove/logout-vs-finalize races pinned once at the REAL route seam in\n  mcp-activation-queue; the store-level interleave duplicate deleted.\n- exact rendered copy kept only where it is a durable protocol contract\n  (outcome notice wording); component render pins made structural.\n- oauth client-auth negotiation + PRM-mismatch refusals + python discover\n  cursor/table families -> it.each/subTest tables.\n\nViolation conversions (all 46 cleared):\n- wall-clock-timer/poll/sleep: setFocus-mount signals, action-start\n  deferreds, queueVerifyResult+flush chains, an asyncio.Event hang, and\n  fs.watch for the multi-process ready barrier.\n- environment-gated-path: the 11 false positives in mcp-oauth (mock route\n  conditions carrying token/credential substrings) renamed via aliases;\n  the rest made unconditional.\n- explicit-test-timeout: allowed with policy comments only where the\n  probe's own timeout bound IS the behavior under test (probe hang tests,\n  real multi-process spawn).\n- conditional-or-disabled-test: kernel-bootstrap made unconditional\n  (file-scoped venv resolution, no cwd dependence; merge of main's #2336\n  had dropped the branch's theme bootstrap in menu-panel too - restored).\n\nVerified: every modified suite passes standalone and under 6 shuffle\nseeds; npm run check (biome + check:test-policy + tsgo + installer +\nbrowser-smoke) is green; spot-reverts of the chain re-entry rule and the\nstore finalize-ownership guard both fail the consolidated tests.\n\n* fix(mcp): mirror the engine audience rule and first-issuer selection in the audit\n\n* fix(mcp): fail closed on a malformed advertised issuer and fix the notion URL substring check"},{"name":"providers","kind":"directory","size":0,"lastCommitHash":"79ad21b7e0b08ad3c0c3bdee2691e259ce86bfa7","lastCommitWhen":"2026-09-17T07:01:56+03:00","lastCommitMessage":"fix(ai,coding-agent): stop retrying a depleted account and refuse the spawn (R2, R1)\n\n429 quota exhaustion was classified as a transient hiccup, so swarms looped 70+\ntimes into a hard wall (01a08f07, 01a0a1ab produced 405 turn stop errors\nagainst exhausted accounts) and 8 children were stillborn at turn 0.\n\n- a 429 whose provider text names account depletion (quota, credit, billing)\n  is now the permanent kind `quota_exhausted`; a keyword-free 429 stays\n  `rate_limit` and keeps its backoff. The keyword test runs only inside the\n  rate-limit branch, so a 400 that merely says \"quota\" stays invalid_request\n- `isPermanentProviderFailureKind` treats it as permanent, so the retry loop\n  stops instead of hammering depletion\n- a draining response trips a rolling in-memory provider breaker\n  (`blockedUntilMs` from the provider's retry hint, else 60s), and\n  `ModelRegistry.assertRlmSpawnAdmission` refuses a child at turn 0 with a\n  typed `RlmSpawnAdmissionError` (reason `quota_exhausted` or\n  `auth_unconfigured`) before any child directory, session, or provider request\n- `rlm.models()` reports tripped providers under the reserved `exhausted` key,\n  keeping its `Record<string, string[]>` contract; the runtime shim degrades to\n  [] when the key is absent\n- in-stream provider error paths (anthropic SSE, responses error/response.failed)\n  now pass the provider's own text to the classifier; they previously passed\n  only the error type, which hid the audited Alibaba-style exhaustion message\n\nLocal state only: no new daemon command, event, or payload shape, and no\nnetwork request in the preflight. Tests: packages/coding-agent/test/suite/\nprovider-quota-circuit-breaker.test.ts (10) and subagent-admission-preflight.test.ts\n(6, including a turn-0 refusal through runRlmChild); packages/ai stream-failure\nand openai-codex-stream (78).\n"},{"name":"utils","kind":"directory","size":0,"lastCommitHash":"d0913ca17f0784abfa2ae053d365fe7f9e9a4051","lastCommitWhen":"2026-09-17T10:01:48+03:00","lastCommitMessage":"fix(coding-agent): honor provider quota deadlines and preflight lane health\n"},{"name":"api-registry.ts","kind":"file","size":2563,"lastCommitHash":"c725135a76f3a314d8eaa0af9776c76e4bae894e","lastCommitWhen":"2026-01-24T23:15:11+01:00","lastCommitMessage":"refactor(ai): register api providers\n"},{"name":"bedrock-provider.ts","kind":"file","size":205,"lastCommitHash":"e8b7168cc9972694232a9ed9c0a6a51655e44abf","lastCommitWhen":"2026-09-10T13:33:51+02:00","lastCommitMessage":"fix bedrock provider loading in packaged cli installs (#2134)\n\n* fix(ci): accept engineering and research Linear tickets\n\n* fix(ci): clarify the Research ticket project\n\nfixes #2124\n\n* fix(coding-agent): include the lazy Bedrock provider in CLI bundles\n\nFixes ENG-6006\n\n* fix(ci): clarify Prime Agent board routing\n\nfixes #2124\n\n* fix(coding-agent): preserve structured Bedrock logs\n\nfixes #2134\n\n* test(coding-agent): await Bedrock fixture daemon shutdown\n\nfixes #2134\n\n* fix(ci): require complete Linear ticket identifiers\n\n* test(coding-agent): guarantee Bedrock fixture cleanup\n\n* test(coding-agent): allow context before Bedrock prompts\n\n* test(coding-agent): allow context before literal prompts\n\n* fix(ci): isolate Ubuntu package sources"},{"name":"cache-pricing.ts","kind":"file","size":2099,"lastCommitHash":"58c4b7c5960906cd114d06dad873c5f19e97ce9e","lastCommitWhen":"2026-07-20T14:24:54-07:00","lastCommitMessage":"fix prime inference anthropic prompt caching (#478)\n\n* fix(ai): enable prime inference anthropic caching (ENG-4723)\n\n* fix(ai): price long cache writes correctly (fixes #478)\n\n* fix(ai): preserve provider cache pricing (fixes #478)"},{"name":"cli.ts","kind":"file","size":3881,"lastCommitHash":"3e5ad67e0f325d4888f82f9b82966218eb4407f5","lastCommitWhen":"2026-05-07T15:59:42+02:00","lastCommitMessage":"chore: migrate pi packages to earendil works scope\n"},{"name":"copilot-client-version.ts","kind":"file","size":519,"lastCommitHash":"a062ed221468697611fb0d9e44670c73ee94d5a1","lastCommitWhen":"2026-09-06T12:23:47+02:00","lastCommitMessage":"chore(ai): bump impersonated Claude Code and Copilot client versions (#2069)\n\n* chore(ai): bump impersonated Claude Code and Copilot client versions\n\nClaude Code 2.1.257 -> 2.1.261 (npm dist-tag latest). The mirrored tool-name\nsurface is unchanged: the ToolInputSchemas union in the packaged\nsdk-tools.d.ts is identical between the two versions (only a Skill prompt\nstring and one enum value differ), so this is a version-only bump like #1993;\nthe anthropic-beta strings stay untouched.\n\nGitHub Copilot Chat 0.35.0 -> 0.48.1 (marketplace stable, no pre-release\nflag, engine ^1.120.0) on VS Code 1.136.1 (current stable per\nupdate.code.visualstudio.com). The client identity previously lived in two\ndrift-prone copies (the OAuth flow and the generator's static headers baked\ninto every Copilot catalog row); both now import one owner,\nsrc/copilot-client-version.ts.\n\nCatalog regenerated: all 28 Copilot rows pick up the new headers; 5\nopenrouter price-jitter rows rode along (itemized in the PR body).\nByte-stable double regen, unchanged through the CI-mirror build.\n\nFixes RES-1311\n\n* fix(ai): commit peak tariffs for time-windowed OpenRouter pricing\n\nOpenRouter publishes clock-dependent pricing for some models via\npricing.overrides entries with utc_start/utc_end windows, and the top-level\nprice reflects whichever window is active when the API is queried. The\nevening regen in this PR therefore snapshotted Tencent Hy3's 16:00-24:00 UTC\noff-peak tariff (37.5% below the daytime rate) — and the same mechanism, not\na lasting price cut, produced the deepseek-v4-flash-vision-exp \"halving\"\ncommitted earlier (its listing alternates 0.22/0.44 across six daily\nwindows).\n\nThe OpenRouter cost parse now takes the per-field maximum across the\ntime-windowed tariffs and the top-level price, so committed costs never\nundercount and regens are hour-independent. Overrides without UTC bounds\n(context-tier and batch rates) are a different mechanism and stay untouched.\nRegen restores both affected rows to their peak rates; no other row changes.\n\nFixes RES-1311\n\n* chore(ai): regenerate the catalog after merging main\n\nPost-merge regen reflects both lines: the #2074 codex Astra row and this\nbranch's client headers + hour-independent peak tariffs. 36 rows differ from\nmain: the 28 Copilot rows (incl. the new gpt-6-astra) pick up the\n0.48.1/1.136.1 headers, deepseek-v4-flash-vision-exp returns to its peak\ntariff (main committed an off-peak snapshot), and 7 openrouter listing/price\nupdates rode along. hy3 stays at peak (main's #2074 regen ran inside the\npeak window; the tariff derivation keeps it there at any hour). Byte-stable\ndouble regen.\n\nFixes RES-1311"},{"name":"env-api-keys.ts","kind":"file","size":8032,"lastCommitHash":"032e3ee74574ca16efaf8745a1394b821256a5d0","lastCommitWhen":"2026-08-18T12:17:35+02:00","lastCommitMessage":"chore: remove redundant comments and unused internals (#1505)\n\n* chore: remove redundant comments\n\n* chore(tui): remove redundant export comments\n\n* chore(coding-agent): remove redundant example comments\n\n* chore(ai): remove redundant comments\n\n* chore(coding-agent): remove obsolete core comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(coding-agent): remove redundant rpc test comments\n\n* chore(coding-agent): remove redundant CLI comments\n\n* chore(agent): remove redundant comments\n\n* chore(tui): remove redundant source comments\n\n* chore(coding-agent): remove redundant mode comments\n\n* chore(coding-agent): remove redundant mode comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(ai): remove redundant source comments\n\n* chore(coding-agent): remove redundant session comments\n\n* chore(coding-agent): remove redundant core comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(tests): remove redundant test comments\n\n* chore: remove unused internal helpers\n\n* docs: restore public API contracts\n\n* chore(coding-agent): restore invariant documentation\n\n* chore(coding-agent): clarify agent family contracts\n\n* style(coding-agent): format restored contracts\n\n* fix: restore intentional catch explanations"},{"name":"index.ts","kind":"file","size":1996,"lastCommitHash":"da44bb76b40cc5a6087934b9631c767e573bdc7c","lastCommitWhen":"2026-09-17T07:08:37+03:00","lastCommitMessage":"refactor(ai,coding-agent): move the usage query into the provider layer behind a JSON contract\n\n/usage could only ever report one provider because the query lived inside a TUI file:\nthe endpoint, the JWT claim reading, the window parsing, and the rendering all sat in\ncoding-agent, and the renderer named Codex in strings. dawn wants that query from a\nmacOS app and elsewhere, so the query half moves to the package that already owns\nproviders and credentials (@earendil-works/pi-ai, in-repo) and becomes host-neutral.\n\n- packages/ai/src/utils/usage/: `UsageLimit`/`UsageReport` (now carrying\n  `providerName`), `UsageCredentialEntry`, and `collectUsage(entries, opts)` returning\n  `{ reports, skipped }`. Credentials are passed IN, so a host with its own token needs\n  no auth.json.\n- `OAuthProviderInterface.usage?()` is the extension point, implemented in\n  openai-codex.ts by moving the endpoint, JWT helpers, parse, and fetch there. Adding a\n  provider to /usage is now one file, with no registry edit; a provider without the hook\n  lands in `skipped` by name instead of being silently dropped.\n- coding-agent keeps rendering + the auth-store bridge. Headings come from\n  `providerName`/display names, and the empty state names providers that have no feed\n  rather than claiming Codex has none.\n- `prime-agent usage [--json]` is the language-neutral face: stdout is exactly one JSON\n  object, the documented contract, and a provider failure is data (`error`) rather than a\n  non-zero exit. That contract is the port target for a native host.\n- oauth dynamic node built-in imports move to the house `dynamicImport(\"node:\" + ...)`\n  pattern: re-exporting the usage module links getOAuthProvider into the browser bundle,\n  and check:browser-smoke fails without it.\n\nVerified: pi-ai usage tests 7/7 with injected fetch (no network), coding-agent usage\ntests 40/40, `npm run check` clean, and a live run against the stored codex accounts\n(two accounts reported, `skipped: [\"anthropic\", \"google-antigravity\"]`). The 13 failing\npi-ai test files are pre-existing: a baseline worktree at the previous head fails the\nsame 13 with 676 tests vs 683 here, the difference being the new 7. Negative control:\nthrowing in `parseUsagePayload` fails the adapter tests immediately.\n"},{"name":"log.ts","kind":"file","size":2649,"lastCommitHash":"189bb1466836a474ba98a25a0c3b43751f08fd43","lastCommitWhen":"2026-07-02T13:00:08-07:00","lastCommitMessage":"make provider failures diagnosable from logs (#313)\n\n* add structured logging and provider stream-failure classification\n\n* address review: reserved log keys, sink fallback, body request_id\n\n* address review: never auto-retry structured auth/invalid_request failures\n\n* address review: log serialization must survive circular refs and bigints\n\n* show users a concise classified error, keep the raw cause in the log"},{"name":"mcp.ts","kind":"file","size":32,"lastCommitHash":"2706aa2e015b821a0510ee1878ad4e35fd1fb8ba","lastCommitWhen":"2026-06-30T11:49:07-07:00","lastCommitMessage":"add mcp integrations for linear and notion (#280)\n\n* add MCP integrations for Linear and Notion via importable Python skills\n\n* address review: refresh mcp servers on reload, scope logout reload, list user servers\n\n* address review: re-register user mcp oauth on refresh, honor bearer env token, re-check expiry, preserve empty structured results\n\n* address review round 3: skill run-forwarding, sdk gating, oauth rejection, begin_login gating, agent-dir resolve, override registration\n\n* raise on MCP tool errors and document multi-session oauth registry limitation\n\n* use independent oauth state, honor mcpServers url override in kernel, normalize non-text tool results\n\n* resolve api-key indirection in kernel, surface oauth cancel cleanly, unregister removed mcp servers on refresh\n\n* reload auth.json on session reload and distinguish refresh failure from missing creds\n\n* surface mcp refresh failures to the kernel instead of a false success\n\n* let an in-flight browser callback win over manual oauth cancellation\n\n* drop built-in mcp provider when a catalog server is overridden without oauth\n\n* do not treat catalog-override servers as authed via official creds, surface manual oauth validation errors, add reload hint to services login\n\n* never inherit official creds for a catalog-name override and propagate custom http headers to the kernel"},{"name":"models-dev.ts","kind":"file","size":30648,"lastCommitHash":"db7ff1c1e8f8f3f3db17285b028f8e4c872e3638","lastCommitWhen":"2026-09-17T07:01:44+03:00","lastCommitMessage":"fix(ai): update deepseek flash model to deepseek/deepseek-flash\n"},{"name":"models.generated.ts","kind":"file","size":661842,"lastCommitHash":"db7ff1c1e8f8f3f3db17285b028f8e4c872e3638","lastCommitWhen":"2026-09-17T07:01:44+03:00","lastCommitMessage":"fix(ai): update deepseek flash model to deepseek/deepseek-flash\n"},{"name":"models.ts","kind":"file","size":4715,"lastCommitHash":"03c3ca484fbaa7f610a185df65fd73b05599651a","lastCommitWhen":"2026-09-17T05:52:15+03:00","lastCommitMessage":"feat(coding-agent,runtime): rebase minimal runtime extensions on upstream 0.9.3\n"},{"name":"oauth.ts","kind":"file","size":40,"lastCommitHash":"e0754fdbb35908798729deea7b63ea9dfe345614","lastCommitWhen":"2026-03-04T20:21:25+01:00","lastCommitMessage":"fix(ai,coding-agent): make pi-ai browser-safe and move OAuth runtime exports\n\n- add browser smoke bundling check to root check + pre-commit\n\n- lazy-load Bedrock provider registration to avoid browser graph traversal\n\n- remove top-level OAuth runtime exports from @mariozechner/pi-ai\n\n- add @mariozechner/pi-ai/oauth subpath export and update coding-agent imports\n\n- move proxy dispatcher init to coding-agent CLI (Node-only)\n\n- document Bedrock/OAuth browser limitations\n\ncloses #1814\n"},{"name":"openrouter-reasoning.ts","kind":"file","size":2578,"lastCommitHash":"032e3ee74574ca16efaf8745a1394b821256a5d0","lastCommitWhen":"2026-08-18T12:17:35+02:00","lastCommitMessage":"chore: remove redundant comments and unused internals (#1505)\n\n* chore: remove redundant comments\n\n* chore(tui): remove redundant export comments\n\n* chore(coding-agent): remove redundant example comments\n\n* chore(ai): remove redundant comments\n\n* chore(coding-agent): remove obsolete core comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(coding-agent): remove redundant rpc test comments\n\n* chore(coding-agent): remove redundant CLI comments\n\n* chore(agent): remove redundant comments\n\n* chore(tui): remove redundant source comments\n\n* chore(coding-agent): remove redundant mode comments\n\n* chore(coding-agent): remove redundant mode comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(ai): remove redundant source comments\n\n* chore(coding-agent): remove redundant session comments\n\n* chore(coding-agent): remove redundant core comments\n\n* chore(coding-agent): remove redundant test comments\n\n* chore(tests): remove redundant test comments\n\n* chore: remove unused internal helpers\n\n* docs: restore public API contracts\n\n* chore(coding-agent): restore invariant documentation\n\n* chore(coding-agent): clarify agent family contracts\n\n* style(coding-agent): format restored contracts\n\n* fix: restore intentional catch explanations"},{"name":"prime-inference-model-catalog.ts","kind":"file","size":3533,"lastCommitHash":"b9cf467edf8bbbd8e607991cf770407d62daa8ec","lastCommitWhen":"2026-09-07T02:15:35-04:00","lastCommitMessage":"Improve session navigation, model discovery, and background commands (#2068)\n\n* feat(coding-agent): show the model label on every agents-view row\n\nResident summaries carry model info for all session kinds; only the\nrenderer restricted the label to subagent rows. Drop the special case so\nany row whose summary has a model renders provider/id[:thinkingLevel] in\nthe suffix region, still suppressed while pending delete/kill.\n\n* fix(coding-agent): open visible sessions without waiting for selection anchors\n\nConsolidates #2079 at d5b49266b5829a4ea8f46ad76b525e0e560ace03.\n\n* feat(tui): open local file links\n\nConsolidates #2073 at b6e83f0f2db71c705d48ef14b499a513c1506f4d.\n\n* feat(coding-agent): highlight file references and command options\n\nConsolidates #1145 at 285ccf457c2f94508b5764d0ab1cc1b4b79a6d61.\n\n* feat: refresh Prime Inference models from the live catalog\n\nConsolidates #1928 at 24d3413c304cefbb063e86a7e41c85207916d90e.\n\n* feat(coding-agent): let root agents create sibling sessions\n\nConsolidates #1996 at 0c9c883c951097183664cf82453856cca21c8ac3.\n\n* feat(coding-agent): wake agents when asynchronous bash finishes\n\nConsolidates #2053 at 1bb4d43a0a64366217f8fec38d574e3917fa550d.\n\n* feat(coding-agent): simplify the agents view while retaining model details\n\nConsolidates #2087 at dc67428af10b1b2fbe63d996e52ede6116bb4d62.\nRetains #2068 model visibility and #2079 immediate opening and query ownership.\n\n* fix(coding-agent): preserve background work and sibling-session credentials\n\nKeep managed shell commands active until completion delivery is accepted.\nForward only effective same-provider transient auth when creating root sessions.\nIntegration fixes for #2053 and #1996.\n\n* fix(coding-agent): address session lifecycle and rendering regressions\n\nPreserve sibling PATH and reject root creation after disposal.\nTreat background completions as new runs without duplicate as_completed wakes.\nKeep table delimiters visible and sanitize live catalog display metadata.\nScope the stale-auth regression fixture to its cached Prime team.\n\n* fix(coding-agent): forward the selected provider environment credentials\n\nPreserve alias priority and runtime credential identity when creating sibling sessions.\nPin that a create timeout never dispatches the task prompt.\n\n* fix(runtime): recheck creating-cell waits when shell commands settle\n\nPreserve late as_completed consumers without suppressing detached completion notices.\nDocument the cached-wrapper result limitation.\n\n* fix(runtime): preserve background bash completion lifecycle\n\nWait for process-group cleanup before releasing residency. Defer paused admission within the original request and report terminal rejection.\nPreserve notices after cancelled non-owning waits and clean up interrupted task construction.\n\n* fix(runtime): use a portable signal for construction cleanup\n\nKeep immediate POSIX cleanup and use the existing Windows job cleanup path without referencing SIGKILL.\n\n* fix(coding-agent): steer shell messages and stop losing supervisor launches\n\nDeliver labeled shell completion messages at the next safe turn boundary.\nStop only an owned replacement child that loses to a current authenticated supervisor.\n\n---------\n\nCo-authored-by: Seth <seth@primeintellect.ai>"},{"name":"session-resources.ts","kind":"file","size":671,"lastCommitHash":"23420012abd670e7ff74dd47c8a81103ca97c0a9","lastCommitWhen":"2026-05-04T00:45:56+02:00","lastCommitMessage":"fix(coding-agent): decouple codex session cleanup\n"},{"name":"stream.ts","kind":"file","size":1486,"lastCommitHash":"e0754fdbb35908798729deea7b63ea9dfe345614","lastCommitWhen":"2026-03-04T20:21:25+01:00","lastCommitMessage":"fix(ai,coding-agent): make pi-ai browser-safe and move OAuth runtime exports\n\n- add browser smoke bundling check to root check + pre-commit\n\n- lazy-load Bedrock provider registration to avoid browser graph traversal\n\n- remove top-level OAuth runtime exports from @mariozechner/pi-ai\n\n- add @mariozechner/pi-ai/oauth subpath export and update coding-agent imports\n\n- move proxy dispatcher init to coding-agent CLI (Node-only)\n\n- document Bedrock/OAuth browser limitations\n\ncloses #1814\n"},{"name":"types.ts","kind":"file","size":18831,"lastCommitHash":"f6e10088494d4d8d2bee79210a3c59188eb64f09","lastCommitWhen":"2026-09-17T05:54:07+03:00","lastCommitMessage":"feat(agent,coding-agent): record tool call durations and show them in the tool header\n"}]