* fix(ok): give each desktop channel its own MCP entry
Beta wrote the same open-knowledge editor entry as Stable, and that
entry only ever launched Stable's app or npm @latest, so Beta users'
agents ran Stable's server. Each channel now registers its own key
(open-knowledge, open-knowledge-beta) with its own launcher chain and
marker, resolved from the running executable or OK_CHANNEL. Stable's
chains are byte-identical, so existing entries stay current.
* fix(ok): stop exporting the per-channel chain builders
Nothing imports buildUnixChain or buildWinChain outside editors.ts, so
knip flagged them as unused exports and failed the lint job.
* fix(ok): make every channel-keyed surface agree on the channel
Review of the first commit found places that still acted as Stable in
Beta. The sandboxed renderer has no process global, so the docked
terminal pre-approved open-knowledge while main checked
open-knowledge-beta; main now returns the name it checked and the launch
builders use only that, emitting no approval without it. The channel is
no longer resolved at module load.
The ACP known-server matcher is built from the product table, the Pi
bridge gets a per-channel file and header, Beta's launcher exports
OK_CHANNEL so its npx fallback runs as Beta, and the reconciler resolves
its key once. Adds Beta tests for each, pins Stable's chains to literal
strings, runs the chain grammar suite for both channels, and documents
the Beta entry and OK_CHANNEL.
* fix(ok): detect the running channel's Pi bridge on the server
Server-side skill-target detection read the static EDITOR_PROJECT_CONFIG_PATH
table, which names Stable's .pi/extensions/open-knowledge.ts. A Beta server in
a project wired only by ok-beta init therefore skipped .pi/skills on skill
installs and imports, and took Stable's bridge as its own.
The Pi bridge path now lives in core as piExtensionProjectPath(product), used
by the CLI's EDITOR_TARGETS.pi and by editorProjectConfigPath(id, product),
which detectProjectConfiguredTargets calls with the channel it resolves when
it runs. The static table's Pi row is derived from the Stable product instead
of a second literal. Reverting skill-projection.ts fails two of the three new
resolveSkillTargets tests.
* fix(ok): give each channel's Pi bridge its own tool namespace
Both channels' Pi bridges registered every OK tool under the ok_ prefix, so in
a project where ok init and ok-beta init had both run, Pi's extension load
order decided which channel's server answered ok_write and the rest.
The prefix is now piToolNamespace(product) plus an underscore: ok_ for Stable,
unchanged byte for byte, and ok-beta_ for Beta. The ACP known-server matcher
accepts every product's namespace, so Beta's gated Pi tools still always
prompt. Reverting the bridge prefix fails the channel-mcp-key Pi test;
reverting the matcher fails the tool-call-input and permissions tests.
* test(ok): pin the Beta chain channel line, Pi apply and probe, and injected name
Three channel fixes from 02ddce56d2 had no test that failed when
reverted. Beta's chains now assert that the OK_CHANNEL assignment is the
line right after the marker on Unix and Windows. A registry apply under Beta
writes open-knowledge-beta.ts next to an existing Stable bridge, leaves the
Stable bytes unchanged, and only then reads back satisfied. The hosted-agent
injection names open-knowledge-beta on both transports under Beta.
Reverts checked: dropping the Windows channel line, pointing the Pi apply or
probe at open-knowledge.ts, and restoring the literal injected name each fail
one of these tests.
* test(ok): require every product's MCP server name to start with open-knowledge
The ACP known-server matcher derives each product's suffix by slicing the
Stable name off the product's own name, so a product whose name did not start
with open-knowledge would compile and never match, and its gated tools would
lose the always-prompt guarantee. A core type test now fails typecheck when
any DESKTOP_PRODUCTS mcpServerName lacks that prefix; renaming Beta's server
to ok-beta fails it with TS2322.
* docs(ok): state how a CLI picks its channel and route Beta users to their entry
The Beta section said a CLI process learns its product from the app that
started it. The resolver reads OK_CHANNEL, then the executable name, and
otherwise answers Stable, so a hand-typed npx @inkeep/open-knowledge@beta init
writes Stable's entry; the page now says so. The migration paragraph names
Settings > Agent connections, the ok init file table, and a self-check instead
of "this change", and the section and the Pi page name Beta's ok-beta_ tool
prefix. The changeset links the section, and the Cursor, Codex, Claude Code,
Antigravity, OpenClaw and Pi pages carry the same Beta pointer as the other
four integration pages.
* docs(ok): name Beta's Pi tools in the Verify and troubleshooting steps
The Pi page's Verify step said Pi calls `ok_exec` and the troubleshooting
line said "If the `ok_` tools are missing", but OpenKnowledge Beta
registers its Pi tools as `ok-beta_*`. Both lines now name the Beta
names beside the Stable ones.
pi-docs.uncached.test.ts reads the page and requires the Verify section
to name `<namespace>_exec` and `<namespace>_` for every desktop product,
derived from piToolNamespace. Restoring pi.mdx from HEAD fails the Beta
case on `ok-beta_exec`; reverting only the troubleshooting line fails it
on `ok-beta_`.
* docs(ok): move the Claude Code Beta pointer into Verify
The pointer sat between the scheduling tier list and the Ingest meetings
line that follows it. It now sits after the Verify paragraph, where a
reader checks the MCP entry.
* refactor(ok): name the static project config path table as Stable-only
EDITOR_PROJECT_CONFIG_PATH's pi row is Stable's bridge path, and its
name sat one letter-case away from the channel-aware
editorProjectConfigPath(id, product). Renaming it
STABLE_EDITOR_PROJECT_CONFIG_PATH makes a caller that picks the table
over the accessor say so. No behaviour change: the two renderer display
sites still show Stable's path, which PR 2 of the series moves to values
delivered from main.
* Name the channel's own MCP entry in messages and pin Beta's launch chains
Refusal messages now name the entry the operation acted on, and the
renderer copy names no key since it cannot resolve the channel. The
migration note is scoped to the separately installed Beta app, since a
legacy-named Beta install resolves as Stable and keeps open-knowledge.
Beta's Unix and Windows chains are pinned byte-for-byte like Stable's.
* Show the channel's own MCP key in settings copy
Main passes the channel's MCP server name to every window, so the
settings dialog and the Cursor troubleshooting note name the key the
app actually wrote; the browser build, which has no bridge, says
OpenKnowledge. Fixes the import order biome flagged and pins both
refusal messages per channel.
* Declare the Cursor note's server placeholder in the guidance manifest
* Name a real server key in the Cursor note without a desktop bridge
The Cursor troubleshooting note fell back to the product label
"OpenKnowledge" when no desktop bridge delivered a server name, and no
entry in Cursor's MCP list carries that name. It now falls back to
Stable's key, which is what the note said before this branch, and it
uses a `server` value on the reference when one is supplied, so the
parameter the guidance manifest declares is no longer dropped.
A browser served by a CLI server running under OK_CHANNEL=beta still
gets Stable's key here; delivering the server's own name to the browser
belongs to the renderer-delivery step of the channel series.
* Pin the channel's MCP server name from main's argv to the copy
Main appended --ok-mcp-server-name inline in withWindowRuntimeArgs, which
vitest cannot import, and preload parsed its own copy of the flag name,
so a drifted name or a dropped append left mcpServerName null and the
settings copy fell back silently. The flag name is now one shared
constant, the append is an importable helper, and tests cover each
step: the helper's literal output for Beta and Stable, the real preload
exposing open-knowledge-beta from that argv, the replace-entry tooltip
naming the delivered key, and a source pin that withWindowRuntimeArgs
still calls the helper.
---------
GitOrigin-RevId: c14d4beb2572daa3c8b69cfa13b6dfa0a53415e0