Skip to content

Latest commit

 

History

History
216 lines (146 loc) · 13.5 KB

File metadata and controls

216 lines (146 loc) · 13.5 KB

MiMo — OCP enablement notes

Host (reference only): XiaomiMiMo/MiMo-Code User-facing package: @opencode-compat/ocp (umbrella + ocp setup) Bridge packages (internal): facade-plugin, facade-sdk, adapter, host-promise-v2 HostProfile id: mimo

OCP attaches as an external compatibility layer. MiMo is a read-only host reference.


1. Install-tree facade overrides (Layer A)

Where the host installs npm plugins into its cache (typically under the MiMo XDG/cache packages/ tree — see packages/opencode/src/npm/index.ts and packages/opencode/src/plugin/shared.ts), OpenCode plugins that import @opencode-ai/plugin / @opencode-ai/sdk should resolve OCP facades via install overrides:

{
  "@opencode-ai/plugin": "npm:@opencode-compat/facade-plugin@0.1.x",
  "@opencode-ai/sdk": "npm:@opencode-compat/facade-sdk@0.1.x"
}

(ocp setup --mode npm writes the concrete published train version.)

Preferred UX: follow the step-by-step guide in INSTALL.md (install @opencode-compat/ocp from npm → install consumer plugins → ocp setup --mode npm). Equivalent CLI names: compat setup / print-only compat overrides / opencode-compat overrides.

# summary — full steps in INSTALL.md
bun add -g @opencode-compat/ocp
mimo plugin -g cursor-opencode-provider
ocp setup --host mimo --mode npm

MiMo installs each npm plugin into an isolated child dir (~/.cache/mimocode/packages/<name>@<version>/). A root-level packages/package.json override alone is not enough — ocp setup --deep (default) patches those children and auto-reifies when they already have node_modules. Re-run ocp setup after installing or upgrading plugins.

Listing OCP itself in plugin is optional bootstrap only — it does not intercept other plugins’ @opencode-ai/plugin imports.

Absolute-path / file:// plugins (common in local dev: plugin: ["/…/dist/index.js"]) resolve @opencode-ai/* from the checkout’s own node_modules. ocp setup (default --absolute-plugins) symlinks those to the OCP facades. Required for unmodified catalog plugins that import @opencode-ai/sdk/v2/client during classic config — stock v2.model.list re-enters GET /api/model and deadlocks on MiMo; facade-sdk polyfills from models.dev instead.

From this checkout, --mode auto may use local file: facade paths; published installs use --mode npm. See INSTALL.md and npm-publish.md.

Do not override @opencode-ai/plugin straight to @mimo-ai/plugin — that skips OCP (v2 surface, doctor, shared host kit).


1.1 Option B — in-place provider entry shims (LanguageModel adoption)

MiMo’s SessionProcessor has no ensureToolCall: tool parts are created only on tool-input-start. A bare AI SDK tool-call (common from stock Cursor / OpenCode custom providers) therefore never materializes a tool part → finish tool-calls with zero tools → empty loop. Separately, MiMo’s bash schema requires a string description (z.string()); stock remaps that omit it fail validation. MiMo tool schemas also use names such as file_path, while some OpenCode providers emit filePath.

OCP policy (HostProfile mimo):

Capability Value OCP adoption
streamToolCallEnsure false Emit tool-input-start before bare tool-call
bashDescriptionRequired true Fill missing bash.description only (never swap host tool catalogs)

Argument spelling is not a MiMo-specific policy table. The shim reads the tools advertised on every LanguageModel call and maps only unique case/separator variants to their exact schema property names, recursively. That covers read, write, edit/StrReplace, future fork conventions, and MCP tools without hard-coding any of them; exact, unknown, and ambiguous keys are preserved.

Why in-place entry (not package.json exports): classic plugins such as cursor-opencode-provider set npm: MODULE_URL → a direct file://…/dist/index.js URL. Package exports shims never run. So after Layer A reify, ocp setup (default --provider-shim):

  1. Renames the install-tree entry to *.ocp-original.js (cache only — upstream tarball / plugin repo untouched)
  2. Writes the shim at the original entry path
  3. Drops sibling ocp-lm-runtime.js + ocp-shim-meta.json
ocp setup --host mimo
# skip Option B only: ocp setup --host mimo --no-provider-shim

Expect beside …/cursor-opencode-provider/dist/:

  • index.js — OCP shim (generated by ocp setup)
  • index.ocp-original.js — stock entry backup
  • ocp-lm-runtime.js — host-dynamic wrapProviderModule / stream adoption
  • ocp-shim-meta.json — setup-time host hint + export metadata (sibling of the shim)

The generated entry resolves the live host from an explicit override, binary, or host-owned environment marker (MIMOCODE / MIMOCODE_*). Provider workers may hide all of those, so it then uses the setup-time host recorded in ocp-shim-meta.json. The hint is only a fallback; a stronger live identity wins.

Re-run ocp setup after plugin install/upgrade/reifynpm install restores stock files and would wipe the shim. Do not fork the provider (cursor-kilocode-provider-style) or patch MiMo source for this gap.


2. Project dirs / .opencode (Layer B / T2)

MiMo today walks .mimocode in ConfigPaths.directories. OCP’s HostProfile for mimo records compatProjectDirs: [".opencode"] for matrix / doctor honesty.

Closing path gaps is the bridge’s job (docs, doctor, optional operator copy/symlink into .mimocode).


3. Promise v2 / host-promise-v2 (Layer E / T3)

3.1 Gap vs classic Hooks

Path What works on MiMo today
Classic Hooks (@opencode-ai/plugin) After Layer A (ocp setup + reify), MiMo loads the plugin and can surface models (e.g. cursor/* via mimo models) through the classic provider/auth hooks.
Promise v2 (@opencode-ai/plugin/v2/promise) Plugin define() + setup can register ctx.aisdk.sdk / ctx.aisdk.language listeners, but MiMo never calls those hooks during native provider-resolve. There is no @mimo-ai/plugin/v2/promise and no in-process aisdk emit in packages/opencode/src/provider/provider.ts.

So: classic prove-out ≠ Promise v2 prove-out. A v2/promise plugin that only sits in MiMo’s plugin list will not inject LanguageModelV3 into MiMo’s native model path by itself.

3.2 What OCP supplies (no host source edits)

OCP ships the missing host kit externally:

HostProfile for mimo sets capabilities.promiseV2 / aisdkProviderHooks to true because this OCP kit is the provider — not because MiMo exports a native v2 surface.

3.3 Operator sidecar — call resolveProvider yourself

Until MiMo gains an in-process seam, T3 live proof is an external sidecar (operator script / helper process) that:

  1. Loads the unchanged consumer plugin/v2 entry from the MiMo install tree
  2. wirePromiseV2({ env: { OPENCODE_COMPAT_HOST: "mimo" } })
  3. await host.register(plugin) — runs setup, attaches aisdk listeners
  4. await host.resolveProvider({ providerID, modelID, package }) — emits sdk then language, returns mutated { language, sdk, model }

Prerequisites

  1. Consumer plugin installed into MiMo (mimo plugin -g …)
  2. ocp setup --host mimo (deep overrides + reify) so @opencode-ai/plugin inside that tree is the OCP facade
  3. OPENCODE_COMPAT_HOST=mimo (or pass env into wirePromiseV2)

Copy-paste smoke (from this checkout; adjust plugin path if the cache layout differs):

# Layer A first (once per plugin install/upgrade)
bun packages/ocp/bin/ocp.ts setup --host mimo

# Sidecar: register + resolveProvider (does not edit MiMo)
OPENCODE_COMPAT_HOST=mimo bun -e '
import { wirePromiseV2 } from "./packages/adapter/src/index.ts"

const pluginPath =
  `${process.env.HOME}/.cache/mimocode/packages/cursor-opencode-provider@latest/node_modules/cursor-opencode-provider/dist/plugin-v2.js`
const plugin = (await import(pluginPath)).default
const host = wirePromiseV2({
  env: { ...process.env, OPENCODE_COMPAT_HOST: "mimo" },
})
await host.register(plugin)

const { language, sdk } = await host.resolveProvider({
  providerID: "cursor",
  modelID: "grok-4.5",
  // package string must match what the plugin’s sdk hook expects
  package: pluginPath,
})

if (!language?.specificationVersion || language.specificationVersion !== "v3") {
  throw new Error("resolveProvider did not return LanguageModelV3")
}
console.log({
  pluginIds: host.plugins(),          // e.g. ["cursor.provider"]
  provider: language.provider,        // "cursor"
  modelId: language.modelId,          // "grok-4.5"
  hasSdkFactory: typeof sdk?.languageModel === "function",
})
'

Success criteria: language.specificationVersion === "v3", language.provider / language.modelId match the request, and sdk.languageModel is present when the plugin’s sdk hook ran. That is the T3 bar for the OCP kit on MiMo.

What this does not do

  • It does not patch MiMo source or add a native v2/promise export.
  • It does not automatically feed language into mimo models / the running TUI. Native listing still uses classic Hooks (or built-in providers).
  • A future in-process seam would be: wherever MiMo builds a language model for providerID/modelID, call the same resolveProvider (or injectLanguageModel) and use the returned language. Until then, keep the sidecar as the operator bridge.

resolveProvider input tips

Field Role
providerID Matched by plugin language hooks (e.g. "cursor")
modelID Becomes event.model.id / event.model.api.id
package String passed to event.package — many plugins (incl. cursor-opencode-provider) gate the sdk hook on this containing their package name or install path
options / sdk / model Optional seeds; hooks mutate event.sdk / event.language in place

API reference: packages/host-promise-v2, contract docs/ocp/0.1.md §7.


4. Classic hook gaps (facade policy)

MiMo’s published classic surface lacks dispose and experimental.provider.small_model vs OCP core. Facade / doctor: accept + no-op + warn. Do not surface MiMo actor.* / session.* on the portable @opencode-ai/plugin path (ADR-7).


5. Telemetry (non-runtime)

OCP does not kill MiMo analytics. See docs/guides/mimocode-telemetry-disable.md (MIMOCODE_ENABLE_ANALYSIS=false).


6. Verification

OPENCODE_COMPAT_HOST=mimo opencode-compat doctor
opencode-compat matrix --host mimo
opencode-compat matrix --host mimo --compat-scan

Doctor should report streamToolCallEnsure: false and bashDescriptionRequired: true.

Live smoke (classic + Option B): after mimo plugin -g cursor-opencode-provider + ocp setup --host mimo:

  1. Confirm shim files under ~/.cache/mimocode/packages/cursor-opencode-provider@latest/node_modules/cursor-opencode-provider/dist/ (index.ocp-original.js, ocp-lm-runtime.js, shimmed index.js).
  2. mimo models should list cursor/* when Cursor auth/cache is available (classic Hooks; Layer A).
  3. A Cursor-backed mimo run that triggers tools should emit tool parts (Option B preamble), and read/write/edit arguments should validate against MiMo's advertised schemas without editing cursor-opencode-provider sources.

Live smoke (Promise v2): run the §3.3 sidecar. Expect language.specificationVersion === "v3" for cursor / grok-4.5. Do not expect mimo models to change from the sidecar alone — native listing is still classic Hooks until an in-process resolveProvider seam exists. Optional: wirePromiseV2().resolveProvider also adapts returned language / sdk via the same profile policy.