Skip to content

feat(agent): add Jcode as a native persistent-server ACP runtime - #1

Open
coderberry wants to merge 1 commit into
mainfrom
feat/jcode-provider
Open

feat(agent): add Jcode as a native persistent-server ACP runtime#1
coderberry wants to merge 1 commit into
mainfrom
feat/jcode-provider

Conversation

@coderberry

Copy link
Copy Markdown

What does this PR do?

Adds Jcode (1jehuang/jcode) as a first-party agent runtime, implementing the "Jcode Native Runtime Adapter" prototype spec (gist). Unlike process-per-task providers, every Multica task runs as one session on a shared persistent jcode serve daemon: the per-task process (jcode acp) is a thin ACP v1 stdio shim, sessions survive daemon and shim restarts, and cancelling one task interrupts only its own session. The provider is gated behind MULTICA_EXPERIMENTAL_JCODE=1 — with the flag unset, jcode is never detected, registered, or started.

Why this approach: the spec forbids depending on jcode's private daemon protocol. The spike (recorded in docs/adr/ADR-JCODE-TRANSPORT.md) found jcode already ships the stable bridge the spec sketches — jcode acp, an upstream-owned ACP adapter backed by the persistent daemon — and Multica already has ten ACP providers on a shared transport (hermesClient). The backend therefore reuses that infrastructure and pins execution to a Multica-scoped server (JCODE_RUNTIME_DIR/JCODE_SOCKET, default ~/.multica/jcode) so tasks never collide with the user's interactive jcode.

Related Issue

No tracked issue — built from the prototype specification linked above. The ADR (docs/adr/ADR-JCODE-TRANSPORT.md) records the spec's required spike answers and the deliberate deviations.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Refactor / code improvement (no behavior change)
  • Documentation update
  • Tests (adding or improving test coverage)
  • CI / infrastructure

Changes Made

  • server/pkg/agent/jcode.go — the backend: Multica-scoped server pinning (env injected last so nothing re-routes it), SUN_LEN-safe socket validation, graceful cancellation (session/cancel + bounded MULTICA_JCODE_INTERRUPT_TIMEOUT wait for stopReason:"cancelled" before group kill; orphaned sessions logged, shared server never killed), per-socket concurrency slots (MULTICA_JCODE_MAX_CONCURRENT, default 4), external-server mode (MULTICA_JCODE_AUTOSTART=0 → fail fast on dead socket), session/resume reattachment (capability-gated, no history replay), session/set_model (failure fails the task), reasoning effort via applyACPEffortOption, per-model usage attribution.
  • server/pkg/agent/{agent,launch,version,models,thinking}.go — SupportedTypes/New/launchHeaders, blocked-args policy, MinVersions 0.80.0, ACP model discovery pinned to the scoped server, acpCatalogThinkingProviders entry.
  • server/internal/daemon/agents_probe.go (+ config.go) — feature-flag-gated probe with MULTICA_JCODE_PATH/MULTICA_JCODE_MODEL.
  • server/internal/daemon/execenv/runtime_config.go — AGENTS.md context delivery (jcode loads it as session bootstrap input).
  • server/internal/metrics/labels.go, scripts/agent-cli-command-names.txt.
  • server/migrations/433_runtime_profile_add_jcode.{up,down}.sql — protocol_family whitelist widening (NOT VALID, matching migration 403's pattern).
  • packages/core/types/agent.ts, packages/core/agents/mcp-support.test.ts, packages/views/runtimes/components/provider-logo.tsx — provider union, MCP-unsupported flag (jcode validates and drops mcpServers; MCP lives in ~/.jcode/mcp.json), placeholder logo.
  • Tests: server/pkg/agent/jcode_test.go (fake ACP server: fresh session, resume-not-load with replay-leak guard, resume-unsupported rejection, graceful + orphaned cancellation, timeout classification, slot semantics, autostart preflight, socket validation, blocked args, env knobs), enrollment in the shared deliverable-boundary suite, server/internal/daemon/agents_probe_jcode_test.go (flag gate), server/pkg/agent/jcode_integration_test.go (real-binary smoke behind agentintegration).
  • Docs: docs/adr/ADR-JCODE-TRANSPORT.md, docs/JCODE_PROVIDER.md (operator guide), apps/docs/content/docs/environment-variables.{,ja.,ko.,zh.}mdx, creating-agents built-in skill + source map.

How to Test

  1. cd server && go test ./pkg/agent ./internal/daemon ./internal/daemon/execenv ./internal/metrics -count=1 — unit + fake-server suites, no model credentials needed.
  2. pnpm typecheck && pnpm test — frontend union, MCP flag, logo.
  3. With jcode ≥0.80 installed and authenticated: cd server && MULTICA_RUN_REAL_AGENT_SMOKE=1 MULTICA_JCODE_SMOKE_MODEL=<working-model> go test -tags=agentintegration ./pkg/agent -run 'TestJcodeReal' -count=1 -v — runs the spec's demonstration scenario (tool execution in the bound worktree, resume of the same session after shim death, two concurrent sessions with one cancelled). Uses the authenticated account and consumes quota.
  4. Manual: MULTICA_EXPERIMENTAL_JCODE=1 make dev, create an agent on the Jcode runtime, assign an issue, watch progress stream; cancel a second task mid-run and confirm the first continues.

All of 1–3 were run locally and pass (real smoke against jcode v0.80.2-dev: both tests green — session resume recalled prior-turn context through the persistent server; concurrent cancel interrupted only its own session, ~60 ms ack). Not run: step 4's full web-UI flow against a live workspace; risk is limited to registration/UI wiring, which follows the ZeroClaw pattern exactly and is covered by the probe/display/union tests.

Checklist

  • I have included a thinking path that traces from project context to this change — docs/adr/ADR-JCODE-TRANSPORT.md (spike findings → transport decision → deviations), plus the backend's header comment mapping each design constraint to its verified cause
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • If this change affects the UI, I have included before/after screenshots — UI delta is a placeholder provider logo + provider union entry only
  • I have updated relevant documentation to reflect my changes
  • If I added a new runtime / coding tool / UI tab, I synced the change to landing copy (apps/web/features/landing/i18n/) and relevant docs (apps/docs/content/docs/) — docs synced (env-vars ×4 languages + provider guide); landing copy deliberately not updated: the "supported coding tools" marketing list advertises out-of-the-box providers, and this one is experimental behind MULTICA_EXPERIMENTAL_JCODE. A landing/changelog entry belongs with the release that lifts the flag (same treatment as ZeroClaw/MiniMax Code, which entered landing copy via changelog entries at release time).
  • If this PR touches Chinese product copy, I checked it against apps/docs/content/docs/developers/conventions.zh.mdx (terminology, mixed-rule for task / issue / skill)
  • I have considered and documented any risks above
  • I will address all reviewer comments before requesting merge

AI Disclosure

AI tool used: Claude Code (Fable 5)

Prompt / approach: /goal Build out the following spec: <gist URL>. The session ran the spec's required Phase A spike first — driving the real installed jcode binary over ACP stdio with a Python probe and reading the upstream Rust source — and recorded every finding in the ADR before writing Go. Implementation deliberately mirrored the most recent provider addition (ZeroClaw, multica-ai#7351) as a touchpoint checklist, including its "verify against the real binary, don't assume the handshake" lesson: resume-of-unknown-session, MCP no-op, cancel semantics, and the runtime-dir lock were all discovered empirically, not assumed. Real-agent smoke tests were run under the repo's MULTICA_RUN_REAL_AGENT_SMOKE=1 convention.

Risks / notes for review

  • Deliberate spec deviation: the Multica daemon does not stop the jcode server on shutdown — the spec's own recovery model (reattach after daemon restart) requires the server to outlive the daemon. Documented in the ADR and operator guide, with the explicit jcode server stop --force incantation.
  • Silent fresh resume (upstream limitation): jcode accepts session/resume for an unknown id by creating an empty session under that id, so a refused resume is undetectable → jcode is in resumeRejectionUndetectable. Flagged as an upstream follow-up in the ADR.
  • Closest-review files: the cancellation window in jcode.go (promptCtx outliving runCtx by the interrupt window) and the slot table's fixed-size-per-socket semantics.

🤖 Generated with Claude Code

Implement the "Jcode Native Runtime Adapter" prototype spec: a first-party
jcode provider that executes every Multica task as one session on a shared,
Multica-scoped persistent `jcode serve` daemon, reached through the upstream
`jcode acp` stdio shim over ACP v1. Gated behind MULTICA_EXPERIMENTAL_JCODE.

Transport decision and the live-probe evidence are recorded in
docs/adr/ADR-JCODE-TRANSPORT.md (Phase A spike deliverable). Every protocol
claim was verified against a real jcode v0.80.2 binary and its source: the
runtime-dir single-instance lock, SUN_LEN socket limits, spawn-lock-protected
autostart, session/resume without history replay, the silent fresh-session
resume of an unknown id (hence resumeRejectionUndetectable), ignored
session-scoped mcpServers (MCP reported unsupported), set_model and the
reasoning_effort config option, per-turn usage, and ~60ms session/cancel
acknowledgment.

Backend (pkg/agent/jcode.go, reusing the shared hermesClient transport):

- Pins every task shim (and anything it autostarts) to a Multica-scoped
  server via JCODE_RUNTIME_DIR/JCODE_SOCKET (default ~/.multica/jcode),
  appended last so nothing re-routes task execution; socket paths are
  validated absolute and SUN_LEN-safe.
- Graceful cancellation: a running turn survives shim death by design, so
  runCtx cancellation sends session/cancel and waits a bounded interrupt
  window (MULTICA_JCODE_INTERRUPT_TIMEOUT, 10s) for stopReason=cancelled
  before the group kill; unacknowledged interrupts log the session id as
  potentially orphaned and never touch the shared server.
- Provider-level concurrency slots per server socket
  (MULTICA_JCODE_MAX_CONCURRENT, default 4), held for the whole execution,
  cancellation-aware while waiting.
- External-server mode: MULTICA_JCODE_AUTOSTART=0 probes the socket and
  fails fast instead of starting anything.
- session/resume for reattachment, capability-gated on initialize;
  set_model failures fail the task (never silently run the wrong model);
  ThinkingLevel flows through applyACPEffortOption (jcode's application is
  verified upstream, GH multica-ai#6720); usage attributed to the effective model.

Wiring follows the ZeroClaw playbook: SupportedTypes/New/launchHeaders,
blocked-args policy, MinVersions (0.80.0), ACP model discovery from
session/new (pinned to the scoped server), acpCatalogThinkingProviders,
feature-flag-gated daemon probe with MULTICA_JCODE_PATH/MODEL, AGENTS.md
context delivery, metrics label, migration 433 widening the protocol_family
whitelist, default-command-name registration, frontend provider union +
MCP-unsupported flag + placeholder logo, and env-vars docs (en/ja/ko/zh).

Tests mirror the ACP backend conventions (fresh session, resume-not-load
with replay-leak guard, resume-unsupported rejection, graceful and orphaned
cancellation, timeout classification, slot semantics, autostart preflight,
socket validation, blocked args, env knob parsing) plus enrollment in the
shared deliverable-boundary suite and a probe feature-flag gate test.
Real-binary smoke tests behind the agentintegration tag prove the spec's
demonstration scenario end-to-end: AGENTS.md + tool execution in the bound
worktree, resume of the same session through the persistent server after the
first shim exited (context recalled), and two concurrent sessions where
cancelling one leaves the other to complete.

docs/JCODE_PROVIDER.md covers install, the feature flag, managed vs
external server, configuration, concurrency, cancellation, recovery
limitations, troubleshooting, and uninstall.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant