Repository navigation
Closed as not planned
Closed as not planned
Copy link
Labels
wontfixThis will not be worked onThis will not be worked on
Description
Goal
Bring jcode to full three-mode alignment like pi / opencode / dsh already have:
- Mode 1 — proxy (URL prefix, exists today via launcher)
- Mode 2 — launcher (
bili jcode— exists) - Mode 3 — native plugin (
bili plugin install jcode— the gap): plugin loaded by the CLIENT itself, spawns/attaches its own proxy, rewrites model traffic, registers ACP tools natively, stamps plugin-mode headers, implements the runtime-info protocol.
Feasibility pre-assessment
UNKNOWN — research first: plugin/extension surface (config rewrite, request headers, native tool registration)? Follow the dsh playbook if yes.
Reference implementations (read these first)
src/agent/dsh-native.ts— cleanest full native (dynamic service inject, fetch patch, header gating, runtime-info stamps). PR feat: dsh native mode — full plugin viabili plugin install dsh+ upgraded launcher (#941) #942 / issue dsh: full plugin/native mode (mode 3) + upgrade launcher to plugin-mode compression (mode 2) #941.src/agent/opencode-native.ts(v1) +src/agent/opencode-v2.ts— config/catalog-based window+output maps, legacy-lane routing. PRs feat: OpenCode 1.x native support — V1 .server() hooks on the standard install path #919/feat: v1 legacy-lane routing — opencode-acp old sessions keep working, new sessions go to bili (#920) #921.src/agent/pi.ts/src/agent/pi-native.ts— header stamping discipline (toolsReady gate).- Installer:
src/plugin-install.ts(bili plugin install <client>); launcher wiring:src/launcher.ts. - Runtime-info protocol ([native] runtime-info protocol: plugins report real model config (model / contextWindow / maxOutput) to the proxy #955, PR feat: runtime-info protocol — plugins report real model config to the proxy (#955) #956): per-request headers
x-bili-plugin-context-window/x-bili-plugin-max-output/x-bili-plugin-model(gated onx-bili-plugin) + bootstrapPOST /__bili/plugin/runtime-info. README "Runtime-info protocol" table is the contract — follow it exactly.
Requirements if feasible
- No launcher, no env vars, no fixed port for the end user; opt-out env
BILI_NATIVE_JCODE=0 - Plugin-mode gating: stamp
x-bili-pluginONLY after native tools are registered (round 1 rides wire mode) - Parent-pid watchdog proxy teardown; attach to a healthy proxy when present
- Runtime-info stamps (model + contextWindow + maxOutput whenever the client exposes them)
npm run typecheck+npm test+npm run buildgreen; new tests for the plugin module (see tests/dsh-native.test.ts for the harness style)- README (en + zh) client table + install section updated
If NOT feasible
Post the evidence (what extension points exist, why wire interception / native tools are impossible) and close as wontfix — a documented no is a deliverable too.
For ework
This issue is directly implementable. Follow the dsh playbook; ask in this thread before inventing new protocol surface. PR must reference this issue (Fixes #N).
Metadata
Metadata
Assignees
Labels
wontfixThis will not be worked onThis will not be worked on
Activity
ranxianglei commentedon Sep 18, 2026
[bot] 🏷 On it. Kicking off the feasibility pre-assessment: (1) what extension/plugin surface
jcodeactually exposes (config rewrite, request headers, native tool registration), (2) how close it is to the dsh/opencode/pi playbooks. Will report findings here before writing any plugin code — if the surface is insufficient I'll post the evidence and close as wontfix per the issue's own contract.ranxianglei commentedon Sep 18, 2026
[bot] 🏷 Feasibility assessment in progress. First-order findings:
jcode(github.com/1jehuang/jcode) is a compiled Rust binary — unlike pi/opencode/dsh (all Node hosts with in-process extension APIs), there is no JS/TS seam to load a plugin into.~/.jcode/config.toml(provider profiles incl.base_url, per-provider staticheaderstable, per-modelcontext_window,extra_bodybody injection) and MCP client support (~/.jcode/mcp.json, plus Claude Code-compatible.mcp.json).That means the two load-bearing pieces of the dsh playbook — in-process traffic rewrite (fetch patch / registerProvider) and per-request dynamic header stamping (conversation id, toolsReady gate, runtime-info stamps) — have no obvious counterpart. Cloning the source now to verify there is no plugin/hook/event system hiding behind that, and to check whether MCP server processes receive session-scoped context (the one channel that could bridge to
POST /__bili/plugin/register). Will post the verdict with full evidence shortly.ranxianglei commentedon Sep 18, 2026
[bot] 🏷 Verdict: Mode 3 (native plugin) is NOT feasible for jcode. Per the issue spec, here is the evidence. I cloned
1jehuang/jcodemaster (Rust workspace, ~80 crates) and audited its entire extension surface at source level.Complete inventory of jcode's extension surface
[hooks]in~/.jcode/config.toml, docs/HOOKS.md):turn_end/session_start/session_end/pre_tool(sync gate) /post_tool. External commands receiving env payloads (JCODE_HOOK_SESSION_ID,JCODE_HOOK_PAYLOAD…). Observers/gates only — they can see what happened, but cannot touch request/response bytes.[terminal] spawn_hook, docs/SPAWN_HOOK.md): controls where headed sessions open (tmux/kitty pane). Terminal placement only.~/.jcode/mcp.json,.jcode/mcp.json, Claude-compat files): this is the one channel that could register native tools. But three source-level facts kill the native-plugin model:crates/jcode-base/src/mcp/pool.rs: "Manages a global pool of MCP server processes that are shared across all jcode sessions… Sessions get lightweightMcpHandleclones" — MCP servers are deliberately de-sessionized: one process pool serves every session (their issue fix: guard side requests against over-window payloads (#554) #557). No per-session lifecycle, so "parent-pid watchdog teardown when the client exits" has no mapping.crates/jcode-base/src/mcp/client.rs(connect_in_dir): child env = inherited daemon env + staticconfig.envfrom mcp.json. No session-scoped variables are injected (unlike Claude Code'sCLAUDE_CODE_SESSION_ID). The MCP shell can never learn which conversation is talking.headersfield says "retained so environment expansion is ready when those transports are").~/.jcode/config.toml):base_url, a staticheaderstable applied verbatim to every request to that provider (crates/jcode-config-types/src/lib.rs:464—BTreeMap<String,String>, no interpolation; applied atcrates/jcode-provider-anthropic-runtime/src/lib.rs:2011),extra_body(static JSON merged into every body), per-modelcontext_window. All of it constant-per-provider, read once at daemon start.HTTPS_PROXYhonored by reqwest — exactly howbili jcode's cert-MITM launcher already works;ANTHROPIC_BASE_URLetc.) — process-level, set at launch = launcher territory (Mode 2).Why each load-bearing piece of native mode fails
base_urlconfig rewrite (needs a proxy origin known before jcode's long-lived shared daemon starts — ephemeral ports can't work, fixed ports are excluded by the issue, and there's a startup race: the daemon reads config.toml before any concurrently-spawned "plugin" could bring up a proxy; additionally OAuth providers hard-code official endpoints — "Claude OAuth traffic always continues to use Anthropic's official endpoints" — so subscription users can't be rerouted at all);HTTPS_PROXYMITM (env vars → launcher, i.e. Mode 2). The only candidate "plugin" process (an MCP shell) is a child of the very process whose traffic must be rewritten — it can't patch its parent or set its parent's environment.x-bili-plugin+x-bili-plugin-conversation, gated on toolsReady): the only header surface is the static per-provider TOML table. A static conversation id would collapse all concurrent jcode sessions into one conversation — cross-session context corruption, and swarm mode (many parallel sessions by design) makes it catastrophic. No gating possible; round 1 could never ride wire mode. Worse, jcode sends no stable per-session identifier on model requests at all (verified: zero session-id headers in the provider runtimes; only a process-level static User-Agent), so even identity-binding (feat: launcher-first UX — native-plugin experience with zero persistent host config (plugin-in-launcher, 内外呼应) #162) has nothing to latch onto.POST /__bili/plugin/runtime-infohalf would be reachable (from the MCP shell), reporting data that correlates to zero routed requests. Mid-session/modelswitches are invisible to static config.compress/decompress/acp_status, their calls POST to conversations that no model request ever bound (nox-bili-plugin-conversationon the wire) → 404. And in proxy mode bili injects same-named wire tools itself and drops client-side ones. This is precisely why claude/codex/omp's MCP-shell companions are classified "not native mode" in the README.What would make it feasible (upstream jcode)
Either (a) a request-middleware hook that can mutate outgoing model-request headers/body per session, or (b) per-session context injected into model requests (a stable session id header) plus per-session MCP spawn context. Absent either, native mode cannot be built honestly.
Disposition
/bili/URL prefix andbili jcode(cert-MITM launcher,api.z.aiwhitelisted) — the launcher is the right fit for a binary host with no plugin seam.ranxianglei commentedon Sep 18, 2026
[bot] 🏷 Done — two housekeeping items on top of the verdict above:
bili jcodelauncher //bili/prefix, no native plugin) and a bullet in the native-mode notes explaining why (compiled Rust binary, no plugin seam, static per-provider headers, session-agnostic MCP pool). 11 insertions, docs-only; typecheck + 1881 tests + build all green locally.wont_fixstate reason, so it's closednot planned+ the repo's existingwontfixlabel). Reopen anytime if jcode upstream lands a request-middleware hook or per-session request context — that would change the verdict.Modes 1 & 2 are untouched and remain the supported paths for jcode.
docs: record jcode native-mode infeasibility (#962)
docs: record jcode native-mode infeasibility (#962)
Merge pull request #967 from ranxianglei/2026-09-19_jcode-native-wont…