@cotal-ai/pi 0.48.1 → 0.48.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -2
- package/dist/standalone.js +2 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -16540,7 +16540,7 @@ import { isConcreteChannel as isConcreteChannel3, channelInAllow as channelInAll
|
|
|
16540
16540
|
|
|
16541
16541
|
// ../connector-core/dist/docs-bundle.generated.js
|
|
16542
16542
|
var DOCS_BUNDLE = {
|
|
16543
|
-
"version": "0.48.
|
|
16543
|
+
"version": "0.48.2",
|
|
16544
16544
|
"generatedFrom": "docs/*.md + SPEC.md + spec/cotal-lang.md + spec/cotal.schema.json",
|
|
16545
16545
|
"pages": [
|
|
16546
16546
|
{
|
|
@@ -16646,7 +16646,7 @@ var DOCS_BUNDLE = {
|
|
|
16646
16646
|
"title": "Connect Jcode (beta)",
|
|
16647
16647
|
"kind": "Guide (informative)",
|
|
16648
16648
|
"summary": "Jcode joins a Cotal mesh as a lateral peer.",
|
|
16649
|
-
"body": "# Connect Jcode (beta)\n\n> **Guide** (informative) \xB7 **For:** operators \xB7 **Prereqs:** [Quickstart](getting-started.md)\n\n[Jcode](https://github.com/1jehuang/jcode) joins a Cotal mesh as a lateral peer. The connector\ncreates one private Jcode Harness API instance per seat, one Jcode session inside it, and exposes\nthe normal `cotal_*` tool surface through Jcode's documented stdio MCP configuration.\n\n**Beta** means the supported path is deliberately narrow: a fresh private session, prompt\ninjection, presence, managed start/stop, requested reasoning effort, and an attached TUI work.\nFeatures that do not preserve that private session's mesh surface fail loud: `--resume`,\nexact-session continuation, `--share-tools`, `--events`, and connector `--opt` values are not\nsupported.\n\n## Install\n\nThe connector is seeded with the Cotal CLI. Jcode's released Harness API bridge is a Unix-socket\nsurface, so Windows is not supported. Managed seats run on Linux, macOS, and the BSDs.\nInstall Jcode 0.78.1 or later from its GitHub release and make the binary available as `jcode` on `PATH`:\n\n```bash\njcode version --json\ncotal spawn --agent jcode\n```\n\nIf an older Cotal installation is missing the connector, run `cotal ext seed --repair` (or\n`cotal ext add @cotal-ai/connector-jcode`). This connector intentionally uses the released\nbinary's `api-bridge` command; it does not require a Rust checkout.\n\n## Spawn it\n\n```bash\ncotal spawn --agent jcode\ncotal spawn reviewer --agent jcode -d\ncotal spawn --agent jcode --model gpt-5.6-sol --prompt \"Review the current change.\"\nCOTAL_DEFAULT_AGENT=jcode cotal spawn\n```\n\nA detached seat is managed normally: `cotal ps`, `cotal attach`, and `cotal stop` control the\nsame process the connector starts. In a terminal, Jcode opens on the managed session. With piped\noutput it stays headless; set `COTAL_JCODE_TUI=1` or `COTAL_JCODE_TUI=0` in the environment of the\nprocess building the launch to override that choice. For a detached spawn, that is the manager's\nenvironment.\n\n## How it binds\n\nJcode's stable integration surface is the **Harness API**: protocol-v1 NDJSON over a Unix socket.\nThe connector launches a **private instance** with `@1jehuang/jcode-sdk`'s `launchInstance()` and\nattaches only to that instance's own socket:\n\n- `launchInstance()` starts a private `JCODE_HOME`, runtime directory, daemon, and `api-bridge`;\n the connector holds the process handle first-hand and closes that instance with the Cotal seat.\n This gives each managed Cotal peer one owned session and prevents it from seeing or changing\n the operator's live Jcode sessions.\n- Attaching to an **operator-run** `jcode api-bridge` shares the operator's live session\n inventory. That is appropriate for a dashboard or editor integration, but not a managed Cotal\n seat: stop, prompt injection, and session selection could act on the operator's work. The\n connector never attaches to an operator bridge.\n- A managed seat **never updates its own binary**. Jcode's background updater restarts the\n process tree when it lands a release; that restart drops the seat's TUI, which is the only\n connection the Jcode server counts as a client, and nothing re-attaches, so the server's idle\n reaper takes the seat down five minutes later in the middle of a turn. The seat's version is\n whatever is on `PATH` when you spawn it, and it stays that version for the seat's life. Update\n deliberately, between seats, not under a running agent.\n\nOn a graceful stop **and** on a startup failure, the connector proves the private daemon tree is\nactually gone rather than trusting the SDK's registry-keyed stop (which is a silent no-op when the\n`servers.json` socket path does not match verbatim): it reads the PIDs the private home itself\nrecords, sends a bounded SIGTERM, escalates survivors to an exact-PID SIGKILL, and reports a\nfailed stop instead of a clean one if any recorded process survives. It never signals by name, so\nteardown can only ever reach the seat's own tree.\n\nA seat that dies without that teardown, from a manager restart or a kill past the grace window,\nleaves its Jcode server running. The server has a process group of its own and carries no\n`COTAL_NAME`, so a name-keyed reap does not reach it, and it holds the seat's runtime directory\nuntil its own five-minute idle timer expires. Each launch records its identity nonce and its host\nprocess in the private home, and the seat's next launch stops the tree that record names. The\nrecorded host is the gate: while it is still alive the seat is still serving, so nothing is\nsignalled and the second launch meets Jcode's own runtime-directory lock instead.\n\nThe private Jcode home lives under `<manager-workspace>/.cotal/jcode/`. It is unique per\nspace/name and is owner-only. Jcode's own credential inheritance is used for the private instance,\nso provider logins work without copying its transcript/config tree into the seat. The spawned\nJcode process does not inherit `COTAL_*` values or the Cotal launch-material pointer.\n\nBecause the home is keyed by space and name, a seat respawned under the same space, name, and\nmanager workspace lands in the same home, and the connector automatically continues the\nnon-archived Jcode session there that was recorded for the seat's working directory and holds the\nlargest transcript, since that is the session carrying the memory a restart would otherwise throw\naway. A seat spawned under a fresh name keys a different home and starts with an\nempty transcript, so keep the same name when you want a replacement seat to continue where the\nprevious one stopped. This automatic continuation is a relaunch of the seat's own private session;\nit is separate from `--resume`, which names an outside session and stays unsupported. The short\nsocket alias the connector derives from that home is reclaimed at every launch, so a name a stopped\nseat used stays launchable.\n\nConnector diagnostics are written both to the spawning terminal and to an owner-only\n`<private-home>/logs/connector-<timestamp>-<pid>.log`, so a failed launch remains inspectable after\nthe manager's launch error scrolls away. Public startup failures stay scrubbed to allow-listed\ncodes rather than arbitrary Harness API messages.\n\nCredential mirroring is mandatory for managed Jcode seats: each launch atomically refreshes the\nallowlisted Jcode, provider-config, and external-login destinations, and removes a destination when\nits source login was removed. Cleanup addresses only that explicit inventory; transcripts, MCP\nconfiguration, logs, and other private-home state are untouched. Copy, mkdir, and removal walk the\nparents with `O_NOFOLLOW`, then publish, create, or unlink the leaf through the pinned parent rather\nthan through a path the kernel re-walks. Replacing a walked directory with a symlink cannot write,\ncreate, or delete a namesake outside the private home.\n\nTwo mechanisms provide that pin. On Linux the leaf is named `/dev/fd/<fd>/<name>`, the openat and\nunlinkat equivalent Node does not expose, after `/dev/fd/<fd>/.` is proven to traverse. macOS mounts\n`/dev/fd` but has no subpath namespace under a descriptor, so there the connector pins the parent as\nthe process working directory instead: a single-component name resolves from that directory's inode\nand no ancestor is walked again. Entering by path is verified rather than trusted, because `chdir`\ntakes a path: the entered directory's inode must equal the inode of the descriptor opened a moment\nearlier, and a mismatch is refused by name. The working directory is restored on every exit,\nincluding the refusing ones. If neither pin is available, the connector throws a named error and\nmirrors nothing. Once a pin holds, `ENOENT` on a child means the mirror path is absent.\n\nThere is no credential-free opt-out today because the private instance must reproduce the operator's\ncurrent provider-login state rather than silently start with stale or partial authorization.\n\nIf a provider failure closes the private Harness API connection during a mesh-driven turn, the\nconnector leaves that turn's inbox batch unacknowledged and opens one bounded recovery window for a\nprivate replacement connection to the same session. A transient launch or attach failure retries\ninside that window, so a loaded host gets the same result as a fast one without creating an\nunbounded connector relaunch loop. The seat reports `waiting` while it reconnects, then redrives\nthat unacknowledged batch only after the session attaches. Each failed replacement must be proven\nstopped before another launch. A permanent Harness refusal, including an invalid request, missing\nsession, protocol mismatch, missing binary, or socket permission denial, ends the seat immediately;\nanother launch cannot change it. An unprovable teardown, the recovery window expiring, or a second\ndisconnect after a successful replacement also ends the seat. An unrecognized Harness SDK error\ncode remains transient by default and retries inside the same bounded window; new permanent codes\nmust be added to the explicit classifier and its exact-count regression.\n\nJcode currently supports **stdio** MCP servers. The connector writes only its own `cotal` entry to\nthe private `JCODE_HOME/mcp.json`; it starts a stdio MCP bridge for that entry and relays its calls\nto the host's one `MeshAgent`. The Jcode/MCP child receives a per-launch relay capability, but not\nthe Cotal broker credential or its launch-material pointer. Jcode also overlays project\n`.jcode/mcp.json`, `.mcp.json`, and `.claude/mcp.json`; a managed launch **refuses** a workspace\ncontaining any of those files, because one could replace the `cotal` bridge or add tools that were\nnot explicitly shared. Operator MCP configuration is isolated in the private home and project MCP\nconfiguration is not supported yet.\n\nBefore the seat joins the mesh, the host runs a mandatory Jcode turn that calls\n`cotal_orientation`. Jcode loads MCP tools asynchronously; its first turn can use the pre-MCP tool\nsnapshot immediately before Jcode rebuilds that snapshot. The host repeats the identical proof once\nin that case. A second absence fails the launch, so a bridge that never comes up remains a loud\nfailure rather than an agent that is present but mute. The persona is already in that transcript as\na no-reply message; the spawn `--prompt` is not submitted until after join. While the proof is in\nflight the connector log names `pre-join readiness` and the bound. That in-flight line only means\nstartup reached the gate; it is not a hang, a missing prompt, or a provider refusal by itself.\nAfter the turn, a second line names the outcome and is what separates those cases:\n`orientation proved; joining with no spawn --prompt` or `joining, then submitting the spawn\n--prompt` when the proof passed; `provider refusal` when the provider rejected the turn; `timeout`\nwhen the bound fired. A genuine hang that never returns and never hits the bound has no outcome\nline. A one-line log with no route line is not that signal. The proof itself is bounded to the\nsame three minutes the connector declares to the manager (`readinessTimeoutMs` is the exported\n`JCODE_READINESS_TIMEOUT_MS`). Tests may shorten the host bound through\n`COTAL_JCODE_READINESS_TIMEOUT_MS`; that override is not an operator setting and does not change\nthe window the connector declares to the manager. If the turn overruns that bound the host\nexits `readiness_timeout` and never joins, rather than working invisibly. That teardown does not\nwait for the in-flight turn: it kills the private Jcode tree and discards whatever that turn had\ngenerated. Nothing from it is recoverable; inspect the seat connector log for the timeout\noutcome, then spawn again. The manager's wait can still report `uncertain` when join itself is\nslow after a passing proof; that is not a cleanup verdict, and it is not the same as a host\n`readiness_timeout`. Use `cotal attach <name>` or `cotal ps` to inspect an `uncertain` launch. The\nhost then waits for the mesh connection and presence bind to complete before it adds a no-reply\nnotice that the bootstrap orientation predates the join and that a new orientation is live\ncontext. During a broker outage, it stays waiting and sends no connected notice.\n\nFor a foreground launch, the TUI opens as soon as the session is ready, before the readiness turn,\nso it streams boot activity instead of leaving the terminal blank. Presence still begins only after\nthe readiness proof passes. An inbound peer message then wakes a Harness API turn. A directed message\nthat arrives while the Harness session is busy (a Cotal-owned `run()`, a TUI-owned turn, or an\nadvisory idle pulse between tool rounds of a still-open Cotal-owned run) enters Jcode's session-owned\nsoft-interrupt queue. Ambient channel traffic stays buffered for the next turn. The host marks\npresence working while the session is busy, publishes `activity` naming automatic queue depth and age\nwhile anything remains uncommitted, and acknowledges every initial or soft-interrupted inbox id only\nafter that containing turn succeeds. A failed Cotal-owned turn or private Harness replacement leaves\nthose ids unacknowledged for mesh redelivery. `cotal_inbox` pulls only buffered quiet\nambient from that host-owned queue; its shared optional `peek` argument is supported, so `peek: true`\nshows those messages without clearing them.\n\n## Model limits\n\n`--model` is passed to Jcode's session-level Harness API model selector. Jcode validates the model\nagainst the active provider, then the connector reads runtime identity back and refuses startup if\nit is not the requested model; a seat is never allowed to join under a model label it did not\nreceive.\n\nModel startup refusals are named without exposing provider output: `model_prefix_rejected` means a\n`provider/model` value was supplied where the Harness API requires a bare id, `model_refused` means\nJcode rejected that bare id, and `model_mismatch` means Jcode accepted the request but reported a\ndifferent effective model. `private_state` names a different step: the seat's private home, its\ncredential mirror, or its short socket alias could not be prepared.\n\n`cotal models --agent jcode` reads the declared catalog from the operator Jcode home's\n`config.toml`: each provider with `model_catalog = true`, its `[[providers.<name>.models]]` ids,\nand any declared `reasoning_efforts`. This is the same config Jcode copies into a private managed\ninstance. The command fails loud when the file is unreadable, malformed, or enables a catalog\nwithout model entries.\n\nThe listed effort tiers are declarations, not provider-verified capabilities. `cotal models` prints\nthat caveat inline as `variants (declared, not provider-verified)` beside each configured tier list,\nso it cannot be missed by reading only the model rows. Providers can reject a tier the file names,\nso launch remains the authority: Jcode applies the requested value and a provider rejection ends the\nlaunch. `--refresh` does not turn this local declaration into a live probe.\n\nThe Harness API can set a requested effort but cannot read an effective effort back. Its runtime\nidentity reports provider, model, and routes only; no reply or event carries the applied tier. Cotal\ntherefore records the accepted request and does not relabel it as an observed effect.\n\n`--variant` is the session's **reasoning effort**, applied after the model and before the seat's\nfirst turn, so a seat never serves a turn at an effort nobody chose. A persona's `variant:` is the\ndefault and `--variant` overrides it, the same way `model:` and `--model` work:\n\n```bash\ncotal spawn --agent jcode --model gpt-5.6-sol --variant high\n```\n\nWhich tiers exist depends on the provider **and** model. The connector does not carry a copy of\nthose ladders: it passes the requested tier to Jcode, which validates it against the active model's\nladder. A rejected tier, or a model with no reasoning-effort surface, ends the launch rather than\nquietly starting the seat at another effort. The external observer/UI receives only the requested\ntier, effective model, fixed `invalid_request` provider code, and an accepted-tier ladder when it\ncan be safely parsed; arbitrary provider rejection text stays private. Omit `--variant` to keep\nJcode's configured default.\n\nIf the mandatory readiness turn receives a provider `invalid_request` refusal for a model id or\nreasoning-effort value, the launch diagnostic names only the provider error code and rejected\nvalue. Other provider response text remains scrubbed, so an external observer/UI can correct\nconnector-visible input without exposing private harness output.\n\nThe following fail loud before a new session is provisioned where the manager can preflight them,\nor at connector launch as a backstop:\n\n- **Resume /continuation:** a Cotal seat owns a new private Jcode instance. Reusing a session from\n an operator or another seat would violate that ownership boundary.\n- **Tool sharing:** Jcode resolves its MCP configuration from several global and project sources.\n The connector owns a private configuration containing only `cotal`, rather than claim a chosen\n subset can be safely merged.\n- **Events:** Jcode's Harness API does not provide the durable structured rollout surface required\n by Cotal's event plane.\n- **Launch options:** the connector does not map arbitrary flags/config into the Harness API.\n- **Containers:** the current deploy image does not bundle Jcode, so there is no containerized Jcode connector today.\n\n## Security limits\n\nThe private home protects against accidental sharing and stale session selection; it is not an\nOS-user isolation boundary. A hostile process running as the same user can still read that user's\nfiles or inspect another same-user process. Use OS/container isolation where peers must be mutually\nhostile.\n\nThe model can receive remote peer messages and Jcode is an autonomous coding harness. Treat its\nprovider credentials, filesystem access, and network capability as the privileges of the OS user\nrunning the seat. Cotal's spawn capability governs who may create a seat; it is not a sandbox for\nwhat a model can be persuaded to do after creation.\n"
|
|
16649
|
+
"body": "# Connect Jcode (beta)\n\n> **Guide** (informative) \xB7 **For:** operators \xB7 **Prereqs:** [Quickstart](getting-started.md)\n\n[Jcode](https://github.com/1jehuang/jcode) joins a Cotal mesh as a lateral peer. The connector\ncreates one private Jcode Harness API instance per seat, one Jcode session inside it, and exposes\nthe normal `cotal_*` tool surface through Jcode's documented stdio MCP configuration.\n\n**Beta** means the supported path is deliberately narrow: a fresh private session, prompt\ninjection, presence, managed start/stop, requested reasoning effort, and an attached TUI work.\nFeatures that do not preserve that private session's mesh surface fail loud: `--resume`,\nexact-session continuation, `--share-tools`, `--events`, and connector `--opt` values are not\nsupported.\n\n## Install\n\nThe connector is seeded with the Cotal CLI. Jcode's released Harness API bridge is a Unix-socket\nsurface, so Windows is not supported. Managed seats run on Linux, macOS, and the BSDs.\nInstall Jcode 0.78.1 or later from its GitHub release and make the binary available as `jcode` on `PATH`:\n\n```bash\njcode version --json\ncotal spawn --agent jcode\n```\n\nIf an older Cotal installation is missing the connector, run `cotal ext seed --repair` (or\n`cotal ext add @cotal-ai/connector-jcode`). This connector intentionally uses the released\nbinary's `api-bridge` command; it does not require a Rust checkout.\n\n## Spawn it\n\n```bash\ncotal spawn --agent jcode\ncotal spawn reviewer --agent jcode -d\ncotal spawn --agent jcode --model gpt-5.6-sol --prompt \"Review the current change.\"\nCOTAL_DEFAULT_AGENT=jcode cotal spawn\n```\n\nA detached seat is managed normally: `cotal ps`, `cotal attach`, and `cotal stop` control the\nsame process the connector starts. In a terminal, Jcode opens on the managed session. With piped\noutput it stays headless; set `COTAL_JCODE_TUI=1` or `COTAL_JCODE_TUI=0` in the environment of the\nprocess building the launch to override that choice. For a detached spawn, that is the manager's\nenvironment.\n\n## How it binds\n\nJcode's stable integration surface is the **Harness API**: protocol-v1 NDJSON over a Unix socket.\nThe connector launches a **private instance** with `@1jehuang/jcode-sdk`'s `launchInstance()` and\nattaches only to that instance's own socket:\n\n- `launchInstance()` starts a private `JCODE_HOME`, runtime directory, daemon, and `api-bridge`;\n the connector holds the process handle first-hand and closes that instance with the Cotal seat.\n This gives each managed Cotal peer one owned session and prevents it from seeing or changing\n the operator's live Jcode sessions.\n- Attaching to an **operator-run** `jcode api-bridge` shares the operator's live session\n inventory. That is appropriate for a dashboard or editor integration, but not a managed Cotal\n seat: stop, prompt injection, and session selection could act on the operator's work. The\n connector never attaches to an operator bridge.\n- A managed seat **never updates its own binary**. Jcode's background updater restarts the\n process tree when it lands a release; that restart drops the seat's TUI, which is the only\n connection the Jcode server counts as a client, and nothing re-attaches, so the server's idle\n reaper takes the seat down five minutes later in the middle of a turn. The seat's version is\n whatever is on `PATH` when you spawn it, and it stays that version for the seat's life. Update\n deliberately, between seats, not under a running agent.\n\nOn a graceful stop **and** on a startup failure, the connector proves the private daemon tree is\nactually gone rather than trusting the SDK's registry-keyed stop (which is a silent no-op when the\n`servers.json` socket path does not match verbatim): it reads the PIDs the private home itself\nrecords, sends a bounded SIGTERM, escalates survivors to an exact-PID SIGKILL, and reports a\nfailed stop instead of a clean one if any recorded process survives. It never signals by name, so\nteardown can only ever reach the seat's own tree.\n\nA seat that dies without that teardown, from a manager restart or a kill past the grace window,\nleaves its Jcode server running. The server has a process group of its own and carries no\n`COTAL_NAME`, so a name-keyed reap does not reach it, and it holds the seat's runtime directory\nuntil its own five-minute idle timer expires. Each launch records its identity nonce and its host\nprocess in the private home, and the seat's next launch stops the tree that record names. The\nrecorded host is the gate: while it is still alive the seat is still serving, so nothing is\nsignalled and the second launch meets Jcode's own runtime-directory lock instead.\n\nThe private Jcode home lives under `<manager-workspace>/.cotal/jcode/`. It is unique per\nspace/name and is owner-only. Jcode's own credential inheritance is used for the private instance,\nso provider logins work without copying its transcript/config tree into the seat. The spawned\nJcode process does not inherit `COTAL_*` values or the Cotal launch-material pointer.\n\nBecause the home is keyed by space and name, a seat respawned under the same space, name, and\nmanager workspace lands in the same home, and the connector automatically continues the\nnon-archived Jcode session there that was recorded for the seat's working directory and holds the\nlargest transcript, since that is the session carrying the memory a restart would otherwise throw\naway. A seat spawned under a fresh name keys a different home and starts with an\nempty transcript, so keep the same name when you want a replacement seat to continue where the\nprevious one stopped. This automatic continuation is a relaunch of the seat's own private session;\nit is separate from `--resume`, which names an outside session and stays unsupported. The short\nsocket alias the connector derives from that home is reclaimed at every launch, so a name a stopped\nseat used stays launchable.\n\nConnector diagnostics are written both to the spawning terminal and to an owner-only\n`<private-home>/logs/connector-<timestamp>-<pid>.log`, so a failed launch remains inspectable after\nthe manager's launch error scrolls away. Public startup failures stay scrubbed to allow-listed\ncodes rather than arbitrary Harness API messages.\n\nCredential mirroring is mandatory for managed Jcode seats: each launch atomically refreshes the\nallowlisted Jcode, provider-config, and external-login destinations, and removes a destination when\nits source login was removed. Cleanup addresses only that explicit inventory; transcripts, MCP\nconfiguration, logs, and other private-home state are untouched. Copy, mkdir, and removal walk the\nparents with `O_NOFOLLOW`, then publish, create, or unlink the leaf through the pinned parent rather\nthan through a path the kernel re-walks. Replacing a walked directory with a symlink cannot write,\ncreate, or delete a namesake outside the private home.\n\nTwo mechanisms provide that pin. On Linux the leaf is named `/dev/fd/<fd>/<name>`, the openat and\nunlinkat equivalent Node does not expose, after `/dev/fd/<fd>/.` is proven to traverse. macOS mounts\n`/dev/fd` but has no subpath namespace under a descriptor, so there the connector pins the parent as\nthe process working directory instead: a single-component name resolves from that directory's inode\nand no ancestor is walked again. Entering by path is verified rather than trusted, because `chdir`\ntakes a path: the entered directory's inode must equal the inode of the descriptor opened a moment\nearlier, and a mismatch is refused by name. The working directory is restored on every exit,\nincluding the refusing ones. If neither pin is available, the connector throws a named error and\nmirrors nothing. Once a pin holds, `ENOENT` on a child means the mirror path is absent.\n\nThere is no credential-free opt-out today because the private instance must reproduce the operator's\ncurrent provider-login state rather than silently start with stale or partial authorization.\n\nIf a provider failure closes the private Harness API connection during a mesh-driven turn, the\nconnector leaves that turn's inbox batch unacknowledged and opens one bounded recovery window for a\nprivate replacement connection to the same session. A transient launch or attach failure retries\ninside that window, so a loaded host gets the same result as a fast one without creating an\nunbounded connector relaunch loop. The seat reports `waiting` while it reconnects, then redrives\nthat unacknowledged batch only after the session attaches. Each failed replacement must be proven\nstopped before another launch. A permanent Harness refusal, including an invalid request, missing\nsession, protocol mismatch, missing binary, or socket permission denial, ends the seat immediately;\nanother launch cannot change it. An unprovable teardown, the recovery window expiring, or a second\ndisconnect after a successful replacement also ends the seat. An unrecognized Harness SDK error\ncode remains transient by default and retries inside the same bounded window; new permanent codes\nmust be added to the explicit classifier and its exact-count regression.\n\nJcode currently supports **stdio** MCP servers. The connector writes only its own `cotal` entry to\nthe private `JCODE_HOME/mcp.json`; it starts a stdio MCP bridge for that entry and relays its calls\nto the host's one `MeshAgent`. The Jcode/MCP child receives a per-launch relay capability, but not\nthe Cotal broker credential or its launch-material pointer. Jcode also overlays project\n`.jcode/mcp.json`, `.mcp.json`, and `.claude/mcp.json`; a managed launch **refuses** a workspace\ncontaining any of those files, because one could replace the `cotal` bridge or add tools that were\nnot explicitly shared. Operator MCP configuration is isolated in the private home and project MCP\nconfiguration is not supported yet.\n\nBefore the seat joins the mesh, the host runs a mandatory Jcode turn that calls\n`cotal_orientation`. Jcode loads MCP tools asynchronously; its first turn can use the pre-MCP tool\nsnapshot immediately before Jcode rebuilds that snapshot. The host repeats the identical proof once\nin that case. A second absence fails the launch, so a bridge that never comes up remains a loud\nfailure rather than an agent that is present but mute. The persona is already in that transcript as\na no-reply message; the spawn `--prompt` is not submitted until after join. While the proof is in\nflight the connector log names `pre-join readiness` and the bound. That in-flight line only means\nstartup reached the gate; it is not a hang, a missing prompt, or a provider refusal by itself.\nAfter the turn, a second line names the outcome and is what separates those cases:\n`orientation proved; joining with no spawn --prompt` or `joining, then submitting the spawn\n--prompt` when the proof passed; `provider refusal` when the provider rejected the turn; `timeout`\nwhen the bound fired. A genuine hang that never returns and never hits the bound has no outcome\nline. A one-line log with no route line is not that signal. The proof itself is bounded to the\nsame three minutes the connector declares to the manager (`readinessTimeoutMs` is the exported\n`JCODE_READINESS_TIMEOUT_MS`). Tests may shorten the host bound through\n`COTAL_JCODE_READINESS_TIMEOUT_MS`; that override is not an operator setting and does not change\nthe window the connector declares to the manager. If the turn overruns that bound the host\nexits `readiness_timeout` and never joins, rather than working invisibly. That teardown does not\nwait for the in-flight turn: it kills the private Jcode tree and discards whatever that turn had\ngenerated. Nothing from it is recoverable; inspect the seat connector log for the timeout\noutcome, then spawn again. The manager's wait can still report `uncertain` when join itself is\nslow after a passing proof; that is not a cleanup verdict, and it is not the same as a host\n`readiness_timeout`. Use `cotal attach <name>` or `cotal ps` to inspect an `uncertain` launch. The\nhost then waits for the mesh connection and presence bind to complete before it adds a no-reply\nnotice that the bootstrap orientation predates the join and that a new orientation is live\ncontext. During a broker outage, it stays waiting and sends no connected notice.\n\nA refused post-join notice is logged without ending the joined session. The startup prompt stays\npending while the native session is busy or its bridge reconnects. Once the host invokes the request,\nit consumes that prompt and does not retry it after an ambiguous error or close. This prevents a\nsecond submission; it cannot prove whether the first request executed.\n\nThe startup prompt excludes the automatic inbox. Messages buffered before it run in the following\nturn, including ordinary channel traffic held in `dnd`. Quiet-channel traffic remains available only\nthrough an explicit inbox pull. The connector log names a startup prompt waiting on in-flight\nsteering and a turn deferred because native state changed during that wait.\n\n\nFor a foreground launch, the TUI opens as soon as the session is ready, before the readiness turn,\nso it streams boot activity instead of leaving the terminal blank. Presence still begins only after\nthe readiness proof passes. An inbound peer message then wakes a Harness API turn. A directed message\nthat arrives while the Harness session is busy (a Cotal-owned `run()`, a TUI-owned turn, or an\nadvisory idle pulse between tool rounds of a still-open Cotal-owned run) enters Jcode's session-owned\nsoft-interrupt queue. Ambient channel traffic stays buffered for the next turn. The host marks\npresence working while the session is busy, publishes `activity` naming automatic queue depth and age\nwhile anything remains uncommitted, and acknowledges every initial or soft-interrupted inbox id only\nafter that containing turn succeeds. A failed Cotal-owned turn or private Harness replacement leaves\nthose ids unacknowledged for mesh redelivery. `cotal_inbox` pulls only buffered quiet\nambient from that host-owned queue; its shared optional `peek` argument is supported, so `peek: true`\nshows those messages without clearing them.\n\n## Model limits\n\n`--model` is passed to Jcode's session-level Harness API model selector. Jcode validates the model\nagainst the active provider, then the connector reads runtime identity back and refuses startup if\nit is not the requested model; a seat is never allowed to join under a model label it did not\nreceive.\n\nModel startup refusals are named without exposing provider output: `model_prefix_rejected` means a\n`provider/model` value was supplied where the Harness API requires a bare id, `model_refused` means\nJcode rejected that bare id, and `model_mismatch` means Jcode accepted the request but reported a\ndifferent effective model. `private_state` names a different step: the seat's private home, its\ncredential mirror, or its short socket alias could not be prepared.\n\n`cotal models --agent jcode` reads the declared catalog from the operator Jcode home's\n`config.toml`: each provider with `model_catalog = true`, its `[[providers.<name>.models]]` ids,\nand any declared `reasoning_efforts`. This is the same config Jcode copies into a private managed\ninstance. The command fails loud when the file is unreadable, malformed, or enables a catalog\nwithout model entries.\n\nThe listed effort tiers are declarations, not provider-verified capabilities. `cotal models` prints\nthat caveat inline as `variants (declared, not provider-verified)` beside each configured tier list,\nso it cannot be missed by reading only the model rows. Providers can reject a tier the file names,\nso launch remains the authority: Jcode applies the requested value and a provider rejection ends the\nlaunch. `--refresh` does not turn this local declaration into a live probe.\n\nThe Harness API can set a requested effort but cannot read an effective effort back. Its runtime\nidentity reports provider, model, and routes only; no reply or event carries the applied tier. Cotal\ntherefore records the accepted request and does not relabel it as an observed effect.\n\n`--variant` is the session's **reasoning effort**, applied after the model and before the seat's\nfirst turn, so a seat never serves a turn at an effort nobody chose. A persona's `variant:` is the\ndefault and `--variant` overrides it, the same way `model:` and `--model` work:\n\n```bash\ncotal spawn --agent jcode --model gpt-5.6-sol --variant high\n```\n\nWhich tiers exist depends on the provider **and** model. The connector does not carry a copy of\nthose ladders: it passes the requested tier to Jcode, which validates it against the active model's\nladder. A rejected tier, or a model with no reasoning-effort surface, ends the launch rather than\nquietly starting the seat at another effort. The external observer/UI receives only the requested\ntier, effective model, fixed `invalid_request` provider code, and an accepted-tier ladder when it\ncan be safely parsed; arbitrary provider rejection text stays private. Omit `--variant` to keep\nJcode's configured default.\n\nIf the mandatory readiness turn receives a provider `invalid_request` refusal for a model id or\nreasoning-effort value, the launch diagnostic names only the provider error code and rejected\nvalue. Other provider response text remains scrubbed, so an external observer/UI can correct\nconnector-visible input without exposing private harness output.\n\nThe following fail loud before a new session is provisioned where the manager can preflight them,\nor at connector launch as a backstop:\n\n- **Resume /continuation:** a Cotal seat owns a new private Jcode instance. Reusing a session from\n an operator or another seat would violate that ownership boundary.\n- **Tool sharing:** Jcode resolves its MCP configuration from several global and project sources.\n The connector owns a private configuration containing only `cotal`, rather than claim a chosen\n subset can be safely merged.\n- **Events:** Jcode's Harness API does not provide the durable structured rollout surface required\n by Cotal's event plane.\n- **Launch options:** the connector does not map arbitrary flags/config into the Harness API.\n- **Containers:** the current deploy image does not bundle Jcode, so there is no containerized Jcode connector today.\n\n## Security limits\n\nThe private home protects against accidental sharing and stale session selection; it is not an\nOS-user isolation boundary. A hostile process running as the same user can still read that user's\nfiles or inspect another same-user process. Use OS/container isolation where peers must be mutually\nhostile.\n\nThe model can receive remote peer messages and Jcode is an autonomous coding harness. Treat its\nprovider credentials, filesystem access, and network capability as the privileges of the OS user\nrunning the seat. Cotal's spawn capability governs who may create a seat; it is not a sandbox for\nwhat a model can be persuaded to do after creation.\n"
|
|
16650
16650
|
},
|
|
16651
16651
|
{
|
|
16652
16652
|
"slug": "connect-opencode",
|
package/dist/standalone.js
CHANGED
|
@@ -56981,7 +56981,7 @@ config(en_default());
|
|
|
56981
56981
|
|
|
56982
56982
|
// ../connector-core/dist/docs-bundle.generated.js
|
|
56983
56983
|
var DOCS_BUNDLE = {
|
|
56984
|
-
"version": "0.48.
|
|
56984
|
+
"version": "0.48.2",
|
|
56985
56985
|
"generatedFrom": "docs/*.md + SPEC.md + spec/cotal-lang.md + spec/cotal.schema.json",
|
|
56986
56986
|
"pages": [
|
|
56987
56987
|
{
|
|
@@ -57087,7 +57087,7 @@ var DOCS_BUNDLE = {
|
|
|
57087
57087
|
"title": "Connect Jcode (beta)",
|
|
57088
57088
|
"kind": "Guide (informative)",
|
|
57089
57089
|
"summary": "Jcode joins a Cotal mesh as a lateral peer.",
|
|
57090
|
-
"body": "# Connect Jcode (beta)\n\n> **Guide** (informative) \xB7 **For:** operators \xB7 **Prereqs:** [Quickstart](getting-started.md)\n\n[Jcode](https://github.com/1jehuang/jcode) joins a Cotal mesh as a lateral peer. The connector\ncreates one private Jcode Harness API instance per seat, one Jcode session inside it, and exposes\nthe normal `cotal_*` tool surface through Jcode's documented stdio MCP configuration.\n\n**Beta** means the supported path is deliberately narrow: a fresh private session, prompt\ninjection, presence, managed start/stop, requested reasoning effort, and an attached TUI work.\nFeatures that do not preserve that private session's mesh surface fail loud: `--resume`,\nexact-session continuation, `--share-tools`, `--events`, and connector `--opt` values are not\nsupported.\n\n## Install\n\nThe connector is seeded with the Cotal CLI. Jcode's released Harness API bridge is a Unix-socket\nsurface, so Windows is not supported. Managed seats run on Linux, macOS, and the BSDs.\nInstall Jcode 0.78.1 or later from its GitHub release and make the binary available as `jcode` on `PATH`:\n\n```bash\njcode version --json\ncotal spawn --agent jcode\n```\n\nIf an older Cotal installation is missing the connector, run `cotal ext seed --repair` (or\n`cotal ext add @cotal-ai/connector-jcode`). This connector intentionally uses the released\nbinary's `api-bridge` command; it does not require a Rust checkout.\n\n## Spawn it\n\n```bash\ncotal spawn --agent jcode\ncotal spawn reviewer --agent jcode -d\ncotal spawn --agent jcode --model gpt-5.6-sol --prompt \"Review the current change.\"\nCOTAL_DEFAULT_AGENT=jcode cotal spawn\n```\n\nA detached seat is managed normally: `cotal ps`, `cotal attach`, and `cotal stop` control the\nsame process the connector starts. In a terminal, Jcode opens on the managed session. With piped\noutput it stays headless; set `COTAL_JCODE_TUI=1` or `COTAL_JCODE_TUI=0` in the environment of the\nprocess building the launch to override that choice. For a detached spawn, that is the manager's\nenvironment.\n\n## How it binds\n\nJcode's stable integration surface is the **Harness API**: protocol-v1 NDJSON over a Unix socket.\nThe connector launches a **private instance** with `@1jehuang/jcode-sdk`'s `launchInstance()` and\nattaches only to that instance's own socket:\n\n- `launchInstance()` starts a private `JCODE_HOME`, runtime directory, daemon, and `api-bridge`;\n the connector holds the process handle first-hand and closes that instance with the Cotal seat.\n This gives each managed Cotal peer one owned session and prevents it from seeing or changing\n the operator's live Jcode sessions.\n- Attaching to an **operator-run** `jcode api-bridge` shares the operator's live session\n inventory. That is appropriate for a dashboard or editor integration, but not a managed Cotal\n seat: stop, prompt injection, and session selection could act on the operator's work. The\n connector never attaches to an operator bridge.\n- A managed seat **never updates its own binary**. Jcode's background updater restarts the\n process tree when it lands a release; that restart drops the seat's TUI, which is the only\n connection the Jcode server counts as a client, and nothing re-attaches, so the server's idle\n reaper takes the seat down five minutes later in the middle of a turn. The seat's version is\n whatever is on `PATH` when you spawn it, and it stays that version for the seat's life. Update\n deliberately, between seats, not under a running agent.\n\nOn a graceful stop **and** on a startup failure, the connector proves the private daemon tree is\nactually gone rather than trusting the SDK's registry-keyed stop (which is a silent no-op when the\n`servers.json` socket path does not match verbatim): it reads the PIDs the private home itself\nrecords, sends a bounded SIGTERM, escalates survivors to an exact-PID SIGKILL, and reports a\nfailed stop instead of a clean one if any recorded process survives. It never signals by name, so\nteardown can only ever reach the seat's own tree.\n\nA seat that dies without that teardown, from a manager restart or a kill past the grace window,\nleaves its Jcode server running. The server has a process group of its own and carries no\n`COTAL_NAME`, so a name-keyed reap does not reach it, and it holds the seat's runtime directory\nuntil its own five-minute idle timer expires. Each launch records its identity nonce and its host\nprocess in the private home, and the seat's next launch stops the tree that record names. The\nrecorded host is the gate: while it is still alive the seat is still serving, so nothing is\nsignalled and the second launch meets Jcode's own runtime-directory lock instead.\n\nThe private Jcode home lives under `<manager-workspace>/.cotal/jcode/`. It is unique per\nspace/name and is owner-only. Jcode's own credential inheritance is used for the private instance,\nso provider logins work without copying its transcript/config tree into the seat. The spawned\nJcode process does not inherit `COTAL_*` values or the Cotal launch-material pointer.\n\nBecause the home is keyed by space and name, a seat respawned under the same space, name, and\nmanager workspace lands in the same home, and the connector automatically continues the\nnon-archived Jcode session there that was recorded for the seat's working directory and holds the\nlargest transcript, since that is the session carrying the memory a restart would otherwise throw\naway. A seat spawned under a fresh name keys a different home and starts with an\nempty transcript, so keep the same name when you want a replacement seat to continue where the\nprevious one stopped. This automatic continuation is a relaunch of the seat's own private session;\nit is separate from `--resume`, which names an outside session and stays unsupported. The short\nsocket alias the connector derives from that home is reclaimed at every launch, so a name a stopped\nseat used stays launchable.\n\nConnector diagnostics are written both to the spawning terminal and to an owner-only\n`<private-home>/logs/connector-<timestamp>-<pid>.log`, so a failed launch remains inspectable after\nthe manager's launch error scrolls away. Public startup failures stay scrubbed to allow-listed\ncodes rather than arbitrary Harness API messages.\n\nCredential mirroring is mandatory for managed Jcode seats: each launch atomically refreshes the\nallowlisted Jcode, provider-config, and external-login destinations, and removes a destination when\nits source login was removed. Cleanup addresses only that explicit inventory; transcripts, MCP\nconfiguration, logs, and other private-home state are untouched. Copy, mkdir, and removal walk the\nparents with `O_NOFOLLOW`, then publish, create, or unlink the leaf through the pinned parent rather\nthan through a path the kernel re-walks. Replacing a walked directory with a symlink cannot write,\ncreate, or delete a namesake outside the private home.\n\nTwo mechanisms provide that pin. On Linux the leaf is named `/dev/fd/<fd>/<name>`, the openat and\nunlinkat equivalent Node does not expose, after `/dev/fd/<fd>/.` is proven to traverse. macOS mounts\n`/dev/fd` but has no subpath namespace under a descriptor, so there the connector pins the parent as\nthe process working directory instead: a single-component name resolves from that directory's inode\nand no ancestor is walked again. Entering by path is verified rather than trusted, because `chdir`\ntakes a path: the entered directory's inode must equal the inode of the descriptor opened a moment\nearlier, and a mismatch is refused by name. The working directory is restored on every exit,\nincluding the refusing ones. If neither pin is available, the connector throws a named error and\nmirrors nothing. Once a pin holds, `ENOENT` on a child means the mirror path is absent.\n\nThere is no credential-free opt-out today because the private instance must reproduce the operator's\ncurrent provider-login state rather than silently start with stale or partial authorization.\n\nIf a provider failure closes the private Harness API connection during a mesh-driven turn, the\nconnector leaves that turn's inbox batch unacknowledged and opens one bounded recovery window for a\nprivate replacement connection to the same session. A transient launch or attach failure retries\ninside that window, so a loaded host gets the same result as a fast one without creating an\nunbounded connector relaunch loop. The seat reports `waiting` while it reconnects, then redrives\nthat unacknowledged batch only after the session attaches. Each failed replacement must be proven\nstopped before another launch. A permanent Harness refusal, including an invalid request, missing\nsession, protocol mismatch, missing binary, or socket permission denial, ends the seat immediately;\nanother launch cannot change it. An unprovable teardown, the recovery window expiring, or a second\ndisconnect after a successful replacement also ends the seat. An unrecognized Harness SDK error\ncode remains transient by default and retries inside the same bounded window; new permanent codes\nmust be added to the explicit classifier and its exact-count regression.\n\nJcode currently supports **stdio** MCP servers. The connector writes only its own `cotal` entry to\nthe private `JCODE_HOME/mcp.json`; it starts a stdio MCP bridge for that entry and relays its calls\nto the host's one `MeshAgent`. The Jcode/MCP child receives a per-launch relay capability, but not\nthe Cotal broker credential or its launch-material pointer. Jcode also overlays project\n`.jcode/mcp.json`, `.mcp.json`, and `.claude/mcp.json`; a managed launch **refuses** a workspace\ncontaining any of those files, because one could replace the `cotal` bridge or add tools that were\nnot explicitly shared. Operator MCP configuration is isolated in the private home and project MCP\nconfiguration is not supported yet.\n\nBefore the seat joins the mesh, the host runs a mandatory Jcode turn that calls\n`cotal_orientation`. Jcode loads MCP tools asynchronously; its first turn can use the pre-MCP tool\nsnapshot immediately before Jcode rebuilds that snapshot. The host repeats the identical proof once\nin that case. A second absence fails the launch, so a bridge that never comes up remains a loud\nfailure rather than an agent that is present but mute. The persona is already in that transcript as\na no-reply message; the spawn `--prompt` is not submitted until after join. While the proof is in\nflight the connector log names `pre-join readiness` and the bound. That in-flight line only means\nstartup reached the gate; it is not a hang, a missing prompt, or a provider refusal by itself.\nAfter the turn, a second line names the outcome and is what separates those cases:\n`orientation proved; joining with no spawn --prompt` or `joining, then submitting the spawn\n--prompt` when the proof passed; `provider refusal` when the provider rejected the turn; `timeout`\nwhen the bound fired. A genuine hang that never returns and never hits the bound has no outcome\nline. A one-line log with no route line is not that signal. The proof itself is bounded to the\nsame three minutes the connector declares to the manager (`readinessTimeoutMs` is the exported\n`JCODE_READINESS_TIMEOUT_MS`). Tests may shorten the host bound through\n`COTAL_JCODE_READINESS_TIMEOUT_MS`; that override is not an operator setting and does not change\nthe window the connector declares to the manager. If the turn overruns that bound the host\nexits `readiness_timeout` and never joins, rather than working invisibly. That teardown does not\nwait for the in-flight turn: it kills the private Jcode tree and discards whatever that turn had\ngenerated. Nothing from it is recoverable; inspect the seat connector log for the timeout\noutcome, then spawn again. The manager's wait can still report `uncertain` when join itself is\nslow after a passing proof; that is not a cleanup verdict, and it is not the same as a host\n`readiness_timeout`. Use `cotal attach <name>` or `cotal ps` to inspect an `uncertain` launch. The\nhost then waits for the mesh connection and presence bind to complete before it adds a no-reply\nnotice that the bootstrap orientation predates the join and that a new orientation is live\ncontext. During a broker outage, it stays waiting and sends no connected notice.\n\nFor a foreground launch, the TUI opens as soon as the session is ready, before the readiness turn,\nso it streams boot activity instead of leaving the terminal blank. Presence still begins only after\nthe readiness proof passes. An inbound peer message then wakes a Harness API turn. A directed message\nthat arrives while the Harness session is busy (a Cotal-owned `run()`, a TUI-owned turn, or an\nadvisory idle pulse between tool rounds of a still-open Cotal-owned run) enters Jcode's session-owned\nsoft-interrupt queue. Ambient channel traffic stays buffered for the next turn. The host marks\npresence working while the session is busy, publishes `activity` naming automatic queue depth and age\nwhile anything remains uncommitted, and acknowledges every initial or soft-interrupted inbox id only\nafter that containing turn succeeds. A failed Cotal-owned turn or private Harness replacement leaves\nthose ids unacknowledged for mesh redelivery. `cotal_inbox` pulls only buffered quiet\nambient from that host-owned queue; its shared optional `peek` argument is supported, so `peek: true`\nshows those messages without clearing them.\n\n## Model limits\n\n`--model` is passed to Jcode's session-level Harness API model selector. Jcode validates the model\nagainst the active provider, then the connector reads runtime identity back and refuses startup if\nit is not the requested model; a seat is never allowed to join under a model label it did not\nreceive.\n\nModel startup refusals are named without exposing provider output: `model_prefix_rejected` means a\n`provider/model` value was supplied where the Harness API requires a bare id, `model_refused` means\nJcode rejected that bare id, and `model_mismatch` means Jcode accepted the request but reported a\ndifferent effective model. `private_state` names a different step: the seat's private home, its\ncredential mirror, or its short socket alias could not be prepared.\n\n`cotal models --agent jcode` reads the declared catalog from the operator Jcode home's\n`config.toml`: each provider with `model_catalog = true`, its `[[providers.<name>.models]]` ids,\nand any declared `reasoning_efforts`. This is the same config Jcode copies into a private managed\ninstance. The command fails loud when the file is unreadable, malformed, or enables a catalog\nwithout model entries.\n\nThe listed effort tiers are declarations, not provider-verified capabilities. `cotal models` prints\nthat caveat inline as `variants (declared, not provider-verified)` beside each configured tier list,\nso it cannot be missed by reading only the model rows. Providers can reject a tier the file names,\nso launch remains the authority: Jcode applies the requested value and a provider rejection ends the\nlaunch. `--refresh` does not turn this local declaration into a live probe.\n\nThe Harness API can set a requested effort but cannot read an effective effort back. Its runtime\nidentity reports provider, model, and routes only; no reply or event carries the applied tier. Cotal\ntherefore records the accepted request and does not relabel it as an observed effect.\n\n`--variant` is the session's **reasoning effort**, applied after the model and before the seat's\nfirst turn, so a seat never serves a turn at an effort nobody chose. A persona's `variant:` is the\ndefault and `--variant` overrides it, the same way `model:` and `--model` work:\n\n```bash\ncotal spawn --agent jcode --model gpt-5.6-sol --variant high\n```\n\nWhich tiers exist depends on the provider **and** model. The connector does not carry a copy of\nthose ladders: it passes the requested tier to Jcode, which validates it against the active model's\nladder. A rejected tier, or a model with no reasoning-effort surface, ends the launch rather than\nquietly starting the seat at another effort. The external observer/UI receives only the requested\ntier, effective model, fixed `invalid_request` provider code, and an accepted-tier ladder when it\ncan be safely parsed; arbitrary provider rejection text stays private. Omit `--variant` to keep\nJcode's configured default.\n\nIf the mandatory readiness turn receives a provider `invalid_request` refusal for a model id or\nreasoning-effort value, the launch diagnostic names only the provider error code and rejected\nvalue. Other provider response text remains scrubbed, so an external observer/UI can correct\nconnector-visible input without exposing private harness output.\n\nThe following fail loud before a new session is provisioned where the manager can preflight them,\nor at connector launch as a backstop:\n\n- **Resume /continuation:** a Cotal seat owns a new private Jcode instance. Reusing a session from\n an operator or another seat would violate that ownership boundary.\n- **Tool sharing:** Jcode resolves its MCP configuration from several global and project sources.\n The connector owns a private configuration containing only `cotal`, rather than claim a chosen\n subset can be safely merged.\n- **Events:** Jcode's Harness API does not provide the durable structured rollout surface required\n by Cotal's event plane.\n- **Launch options:** the connector does not map arbitrary flags/config into the Harness API.\n- **Containers:** the current deploy image does not bundle Jcode, so there is no containerized Jcode connector today.\n\n## Security limits\n\nThe private home protects against accidental sharing and stale session selection; it is not an\nOS-user isolation boundary. A hostile process running as the same user can still read that user's\nfiles or inspect another same-user process. Use OS/container isolation where peers must be mutually\nhostile.\n\nThe model can receive remote peer messages and Jcode is an autonomous coding harness. Treat its\nprovider credentials, filesystem access, and network capability as the privileges of the OS user\nrunning the seat. Cotal's spawn capability governs who may create a seat; it is not a sandbox for\nwhat a model can be persuaded to do after creation.\n"
|
|
57090
|
+
"body": "# Connect Jcode (beta)\n\n> **Guide** (informative) \xB7 **For:** operators \xB7 **Prereqs:** [Quickstart](getting-started.md)\n\n[Jcode](https://github.com/1jehuang/jcode) joins a Cotal mesh as a lateral peer. The connector\ncreates one private Jcode Harness API instance per seat, one Jcode session inside it, and exposes\nthe normal `cotal_*` tool surface through Jcode's documented stdio MCP configuration.\n\n**Beta** means the supported path is deliberately narrow: a fresh private session, prompt\ninjection, presence, managed start/stop, requested reasoning effort, and an attached TUI work.\nFeatures that do not preserve that private session's mesh surface fail loud: `--resume`,\nexact-session continuation, `--share-tools`, `--events`, and connector `--opt` values are not\nsupported.\n\n## Install\n\nThe connector is seeded with the Cotal CLI. Jcode's released Harness API bridge is a Unix-socket\nsurface, so Windows is not supported. Managed seats run on Linux, macOS, and the BSDs.\nInstall Jcode 0.78.1 or later from its GitHub release and make the binary available as `jcode` on `PATH`:\n\n```bash\njcode version --json\ncotal spawn --agent jcode\n```\n\nIf an older Cotal installation is missing the connector, run `cotal ext seed --repair` (or\n`cotal ext add @cotal-ai/connector-jcode`). This connector intentionally uses the released\nbinary's `api-bridge` command; it does not require a Rust checkout.\n\n## Spawn it\n\n```bash\ncotal spawn --agent jcode\ncotal spawn reviewer --agent jcode -d\ncotal spawn --agent jcode --model gpt-5.6-sol --prompt \"Review the current change.\"\nCOTAL_DEFAULT_AGENT=jcode cotal spawn\n```\n\nA detached seat is managed normally: `cotal ps`, `cotal attach`, and `cotal stop` control the\nsame process the connector starts. In a terminal, Jcode opens on the managed session. With piped\noutput it stays headless; set `COTAL_JCODE_TUI=1` or `COTAL_JCODE_TUI=0` in the environment of the\nprocess building the launch to override that choice. For a detached spawn, that is the manager's\nenvironment.\n\n## How it binds\n\nJcode's stable integration surface is the **Harness API**: protocol-v1 NDJSON over a Unix socket.\nThe connector launches a **private instance** with `@1jehuang/jcode-sdk`'s `launchInstance()` and\nattaches only to that instance's own socket:\n\n- `launchInstance()` starts a private `JCODE_HOME`, runtime directory, daemon, and `api-bridge`;\n the connector holds the process handle first-hand and closes that instance with the Cotal seat.\n This gives each managed Cotal peer one owned session and prevents it from seeing or changing\n the operator's live Jcode sessions.\n- Attaching to an **operator-run** `jcode api-bridge` shares the operator's live session\n inventory. That is appropriate for a dashboard or editor integration, but not a managed Cotal\n seat: stop, prompt injection, and session selection could act on the operator's work. The\n connector never attaches to an operator bridge.\n- A managed seat **never updates its own binary**. Jcode's background updater restarts the\n process tree when it lands a release; that restart drops the seat's TUI, which is the only\n connection the Jcode server counts as a client, and nothing re-attaches, so the server's idle\n reaper takes the seat down five minutes later in the middle of a turn. The seat's version is\n whatever is on `PATH` when you spawn it, and it stays that version for the seat's life. Update\n deliberately, between seats, not under a running agent.\n\nOn a graceful stop **and** on a startup failure, the connector proves the private daemon tree is\nactually gone rather than trusting the SDK's registry-keyed stop (which is a silent no-op when the\n`servers.json` socket path does not match verbatim): it reads the PIDs the private home itself\nrecords, sends a bounded SIGTERM, escalates survivors to an exact-PID SIGKILL, and reports a\nfailed stop instead of a clean one if any recorded process survives. It never signals by name, so\nteardown can only ever reach the seat's own tree.\n\nA seat that dies without that teardown, from a manager restart or a kill past the grace window,\nleaves its Jcode server running. The server has a process group of its own and carries no\n`COTAL_NAME`, so a name-keyed reap does not reach it, and it holds the seat's runtime directory\nuntil its own five-minute idle timer expires. Each launch records its identity nonce and its host\nprocess in the private home, and the seat's next launch stops the tree that record names. The\nrecorded host is the gate: while it is still alive the seat is still serving, so nothing is\nsignalled and the second launch meets Jcode's own runtime-directory lock instead.\n\nThe private Jcode home lives under `<manager-workspace>/.cotal/jcode/`. It is unique per\nspace/name and is owner-only. Jcode's own credential inheritance is used for the private instance,\nso provider logins work without copying its transcript/config tree into the seat. The spawned\nJcode process does not inherit `COTAL_*` values or the Cotal launch-material pointer.\n\nBecause the home is keyed by space and name, a seat respawned under the same space, name, and\nmanager workspace lands in the same home, and the connector automatically continues the\nnon-archived Jcode session there that was recorded for the seat's working directory and holds the\nlargest transcript, since that is the session carrying the memory a restart would otherwise throw\naway. A seat spawned under a fresh name keys a different home and starts with an\nempty transcript, so keep the same name when you want a replacement seat to continue where the\nprevious one stopped. This automatic continuation is a relaunch of the seat's own private session;\nit is separate from `--resume`, which names an outside session and stays unsupported. The short\nsocket alias the connector derives from that home is reclaimed at every launch, so a name a stopped\nseat used stays launchable.\n\nConnector diagnostics are written both to the spawning terminal and to an owner-only\n`<private-home>/logs/connector-<timestamp>-<pid>.log`, so a failed launch remains inspectable after\nthe manager's launch error scrolls away. Public startup failures stay scrubbed to allow-listed\ncodes rather than arbitrary Harness API messages.\n\nCredential mirroring is mandatory for managed Jcode seats: each launch atomically refreshes the\nallowlisted Jcode, provider-config, and external-login destinations, and removes a destination when\nits source login was removed. Cleanup addresses only that explicit inventory; transcripts, MCP\nconfiguration, logs, and other private-home state are untouched. Copy, mkdir, and removal walk the\nparents with `O_NOFOLLOW`, then publish, create, or unlink the leaf through the pinned parent rather\nthan through a path the kernel re-walks. Replacing a walked directory with a symlink cannot write,\ncreate, or delete a namesake outside the private home.\n\nTwo mechanisms provide that pin. On Linux the leaf is named `/dev/fd/<fd>/<name>`, the openat and\nunlinkat equivalent Node does not expose, after `/dev/fd/<fd>/.` is proven to traverse. macOS mounts\n`/dev/fd` but has no subpath namespace under a descriptor, so there the connector pins the parent as\nthe process working directory instead: a single-component name resolves from that directory's inode\nand no ancestor is walked again. Entering by path is verified rather than trusted, because `chdir`\ntakes a path: the entered directory's inode must equal the inode of the descriptor opened a moment\nearlier, and a mismatch is refused by name. The working directory is restored on every exit,\nincluding the refusing ones. If neither pin is available, the connector throws a named error and\nmirrors nothing. Once a pin holds, `ENOENT` on a child means the mirror path is absent.\n\nThere is no credential-free opt-out today because the private instance must reproduce the operator's\ncurrent provider-login state rather than silently start with stale or partial authorization.\n\nIf a provider failure closes the private Harness API connection during a mesh-driven turn, the\nconnector leaves that turn's inbox batch unacknowledged and opens one bounded recovery window for a\nprivate replacement connection to the same session. A transient launch or attach failure retries\ninside that window, so a loaded host gets the same result as a fast one without creating an\nunbounded connector relaunch loop. The seat reports `waiting` while it reconnects, then redrives\nthat unacknowledged batch only after the session attaches. Each failed replacement must be proven\nstopped before another launch. A permanent Harness refusal, including an invalid request, missing\nsession, protocol mismatch, missing binary, or socket permission denial, ends the seat immediately;\nanother launch cannot change it. An unprovable teardown, the recovery window expiring, or a second\ndisconnect after a successful replacement also ends the seat. An unrecognized Harness SDK error\ncode remains transient by default and retries inside the same bounded window; new permanent codes\nmust be added to the explicit classifier and its exact-count regression.\n\nJcode currently supports **stdio** MCP servers. The connector writes only its own `cotal` entry to\nthe private `JCODE_HOME/mcp.json`; it starts a stdio MCP bridge for that entry and relays its calls\nto the host's one `MeshAgent`. The Jcode/MCP child receives a per-launch relay capability, but not\nthe Cotal broker credential or its launch-material pointer. Jcode also overlays project\n`.jcode/mcp.json`, `.mcp.json`, and `.claude/mcp.json`; a managed launch **refuses** a workspace\ncontaining any of those files, because one could replace the `cotal` bridge or add tools that were\nnot explicitly shared. Operator MCP configuration is isolated in the private home and project MCP\nconfiguration is not supported yet.\n\nBefore the seat joins the mesh, the host runs a mandatory Jcode turn that calls\n`cotal_orientation`. Jcode loads MCP tools asynchronously; its first turn can use the pre-MCP tool\nsnapshot immediately before Jcode rebuilds that snapshot. The host repeats the identical proof once\nin that case. A second absence fails the launch, so a bridge that never comes up remains a loud\nfailure rather than an agent that is present but mute. The persona is already in that transcript as\na no-reply message; the spawn `--prompt` is not submitted until after join. While the proof is in\nflight the connector log names `pre-join readiness` and the bound. That in-flight line only means\nstartup reached the gate; it is not a hang, a missing prompt, or a provider refusal by itself.\nAfter the turn, a second line names the outcome and is what separates those cases:\n`orientation proved; joining with no spawn --prompt` or `joining, then submitting the spawn\n--prompt` when the proof passed; `provider refusal` when the provider rejected the turn; `timeout`\nwhen the bound fired. A genuine hang that never returns and never hits the bound has no outcome\nline. A one-line log with no route line is not that signal. The proof itself is bounded to the\nsame three minutes the connector declares to the manager (`readinessTimeoutMs` is the exported\n`JCODE_READINESS_TIMEOUT_MS`). Tests may shorten the host bound through\n`COTAL_JCODE_READINESS_TIMEOUT_MS`; that override is not an operator setting and does not change\nthe window the connector declares to the manager. If the turn overruns that bound the host\nexits `readiness_timeout` and never joins, rather than working invisibly. That teardown does not\nwait for the in-flight turn: it kills the private Jcode tree and discards whatever that turn had\ngenerated. Nothing from it is recoverable; inspect the seat connector log for the timeout\noutcome, then spawn again. The manager's wait can still report `uncertain` when join itself is\nslow after a passing proof; that is not a cleanup verdict, and it is not the same as a host\n`readiness_timeout`. Use `cotal attach <name>` or `cotal ps` to inspect an `uncertain` launch. The\nhost then waits for the mesh connection and presence bind to complete before it adds a no-reply\nnotice that the bootstrap orientation predates the join and that a new orientation is live\ncontext. During a broker outage, it stays waiting and sends no connected notice.\n\nA refused post-join notice is logged without ending the joined session. The startup prompt stays\npending while the native session is busy or its bridge reconnects. Once the host invokes the request,\nit consumes that prompt and does not retry it after an ambiguous error or close. This prevents a\nsecond submission; it cannot prove whether the first request executed.\n\nThe startup prompt excludes the automatic inbox. Messages buffered before it run in the following\nturn, including ordinary channel traffic held in `dnd`. Quiet-channel traffic remains available only\nthrough an explicit inbox pull. The connector log names a startup prompt waiting on in-flight\nsteering and a turn deferred because native state changed during that wait.\n\n\nFor a foreground launch, the TUI opens as soon as the session is ready, before the readiness turn,\nso it streams boot activity instead of leaving the terminal blank. Presence still begins only after\nthe readiness proof passes. An inbound peer message then wakes a Harness API turn. A directed message\nthat arrives while the Harness session is busy (a Cotal-owned `run()`, a TUI-owned turn, or an\nadvisory idle pulse between tool rounds of a still-open Cotal-owned run) enters Jcode's session-owned\nsoft-interrupt queue. Ambient channel traffic stays buffered for the next turn. The host marks\npresence working while the session is busy, publishes `activity` naming automatic queue depth and age\nwhile anything remains uncommitted, and acknowledges every initial or soft-interrupted inbox id only\nafter that containing turn succeeds. A failed Cotal-owned turn or private Harness replacement leaves\nthose ids unacknowledged for mesh redelivery. `cotal_inbox` pulls only buffered quiet\nambient from that host-owned queue; its shared optional `peek` argument is supported, so `peek: true`\nshows those messages without clearing them.\n\n## Model limits\n\n`--model` is passed to Jcode's session-level Harness API model selector. Jcode validates the model\nagainst the active provider, then the connector reads runtime identity back and refuses startup if\nit is not the requested model; a seat is never allowed to join under a model label it did not\nreceive.\n\nModel startup refusals are named without exposing provider output: `model_prefix_rejected` means a\n`provider/model` value was supplied where the Harness API requires a bare id, `model_refused` means\nJcode rejected that bare id, and `model_mismatch` means Jcode accepted the request but reported a\ndifferent effective model. `private_state` names a different step: the seat's private home, its\ncredential mirror, or its short socket alias could not be prepared.\n\n`cotal models --agent jcode` reads the declared catalog from the operator Jcode home's\n`config.toml`: each provider with `model_catalog = true`, its `[[providers.<name>.models]]` ids,\nand any declared `reasoning_efforts`. This is the same config Jcode copies into a private managed\ninstance. The command fails loud when the file is unreadable, malformed, or enables a catalog\nwithout model entries.\n\nThe listed effort tiers are declarations, not provider-verified capabilities. `cotal models` prints\nthat caveat inline as `variants (declared, not provider-verified)` beside each configured tier list,\nso it cannot be missed by reading only the model rows. Providers can reject a tier the file names,\nso launch remains the authority: Jcode applies the requested value and a provider rejection ends the\nlaunch. `--refresh` does not turn this local declaration into a live probe.\n\nThe Harness API can set a requested effort but cannot read an effective effort back. Its runtime\nidentity reports provider, model, and routes only; no reply or event carries the applied tier. Cotal\ntherefore records the accepted request and does not relabel it as an observed effect.\n\n`--variant` is the session's **reasoning effort**, applied after the model and before the seat's\nfirst turn, so a seat never serves a turn at an effort nobody chose. A persona's `variant:` is the\ndefault and `--variant` overrides it, the same way `model:` and `--model` work:\n\n```bash\ncotal spawn --agent jcode --model gpt-5.6-sol --variant high\n```\n\nWhich tiers exist depends on the provider **and** model. The connector does not carry a copy of\nthose ladders: it passes the requested tier to Jcode, which validates it against the active model's\nladder. A rejected tier, or a model with no reasoning-effort surface, ends the launch rather than\nquietly starting the seat at another effort. The external observer/UI receives only the requested\ntier, effective model, fixed `invalid_request` provider code, and an accepted-tier ladder when it\ncan be safely parsed; arbitrary provider rejection text stays private. Omit `--variant` to keep\nJcode's configured default.\n\nIf the mandatory readiness turn receives a provider `invalid_request` refusal for a model id or\nreasoning-effort value, the launch diagnostic names only the provider error code and rejected\nvalue. Other provider response text remains scrubbed, so an external observer/UI can correct\nconnector-visible input without exposing private harness output.\n\nThe following fail loud before a new session is provisioned where the manager can preflight them,\nor at connector launch as a backstop:\n\n- **Resume /continuation:** a Cotal seat owns a new private Jcode instance. Reusing a session from\n an operator or another seat would violate that ownership boundary.\n- **Tool sharing:** Jcode resolves its MCP configuration from several global and project sources.\n The connector owns a private configuration containing only `cotal`, rather than claim a chosen\n subset can be safely merged.\n- **Events:** Jcode's Harness API does not provide the durable structured rollout surface required\n by Cotal's event plane.\n- **Launch options:** the connector does not map arbitrary flags/config into the Harness API.\n- **Containers:** the current deploy image does not bundle Jcode, so there is no containerized Jcode connector today.\n\n## Security limits\n\nThe private home protects against accidental sharing and stale session selection; it is not an\nOS-user isolation boundary. A hostile process running as the same user can still read that user's\nfiles or inspect another same-user process. Use OS/container isolation where peers must be mutually\nhostile.\n\nThe model can receive remote peer messages and Jcode is an autonomous coding harness. Treat its\nprovider credentials, filesystem access, and network capability as the privileges of the OS user\nrunning the seat. Cotal's spawn capability governs who may create a seat; it is not a sandbox for\nwhat a model can be persuaded to do after creation.\n"
|
|
57091
57091
|
},
|
|
57092
57092
|
{
|
|
57093
57093
|
"slug": "connect-opencode",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotal-ai/pi",
|
|
3
|
-
"version": "0.48.
|
|
3
|
+
"version": "0.48.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"esbuild": "^0.28.0",
|
|
35
35
|
"typebox": "^1.1.0",
|
|
36
36
|
"zod": "^4.4.3",
|
|
37
|
-
"@cotal-ai/core": "0.48.
|
|
38
|
-
"@cotal-ai/connector-core": "0.48.
|
|
37
|
+
"@cotal-ai/core": "0.48.2",
|
|
38
|
+
"@cotal-ai/connector-core": "0.48.2"
|
|
39
39
|
},
|
|
40
40
|
"files": [
|
|
41
41
|
"dist"
|