@cotal-ai/connector-opencode 0.14.0 → 0.14.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 CHANGED
@@ -136,7 +136,7 @@ import { isConcreteChannel as isConcreteChannel3, channelInAllow as channelInAll
136
136
 
137
137
  // ../connector-core/dist/docs-bundle.generated.js
138
138
  var DOCS_BUNDLE = {
139
- "version": "0.14.0",
139
+ "version": "0.14.2",
140
140
  "generatedFrom": "docs/*.md + SPEC.md + spec/cotal.schema.json",
141
141
  "pages": [
142
142
  {
@@ -347,7 +347,7 @@ var DOCS_BUNDLE = {
347
347
  "title": "Setup internals (maintainer notes)",
348
348
  "kind": "Project (non-normative maintainer notes)",
349
349
  "summary": "cotal setup (implementations/cli/src/commands/setup.ts) is configure-only and state-independent: it checks prerequisites, installs the Claude Code plugin, and seeds persona files, and it launches n\u2026",
350
- "body": "# Setup internals (maintainer notes)\n\n> **Project** (non-normative maintainer notes) \xB7 **For:** maintainers changing how setup works\n>\n> How `cotal setup` works, and the cross-repo couplings it depends on. If you change one of\n> the things in the **Invariants** table, update the listed siblings in the same change, or\n> setup silently breaks for npx users.\n\n## The flow\n\n`cotal setup`\n([`implementations/cli/src/commands/setup.ts`](../implementations/cli/src/commands/setup.ts))\nis **configure-only and state-independent**: it checks prerequisites, installs the Claude Code\nplugin, and seeds persona files, and it **launches nothing**: no mesh, no web dashboard, no\nmanager, no delivery daemon, no cmux/tmux session, no demo. Starting the stack is `cotal up`; the\ndashboard is `cotal web`. Every file it writes is announced (`\u2192 wrote \u2026` via `provenance.wrote`).\nIt is two-tier, gated on a machine marker.\n\n**First run** (no `~/.cotal/onboarded.json`, or `--full`, or `--yes`) runs `runFirstRun(yes)`:\n\n- splash \u2192 intro \u2192 core **checks** (Node >= 20; **locate** `nats-server`: located, never\n started) \u2192 **connector picker** \u2192 write the demo personas (david/sven/me) and seed the generic\n `default` \u2192 **offer a global install** (`offerGlobalInstall`) \u2192 onboarded marker \u2192 a finale that\n lists the commands to start things (`cotal up --detach`, `cotal web`, `cotal spawn \u2026`,\n `cotal console`, `cotal down`). Nothing is running when it returns.\n- The old `--auth` / `--open` flags are **gone**: they set the mesh MODE at launch time, and setup\n no longer launches; mode is now `cotal up [--open]`'s concern (an unknown-option error names\n them, no silent no-op).\n\n**Later runs** run `runEnsure`: re-seed the `default` persona if it's missing (announced), then\nprint the **status card** (`readyCard`). The card is **read-only probes** (`machineStatus`/`meshStatus`/`webUp`/`managerUp` for NATS, the plugin, the mesh, the web\ndashboard, and the manager) and for anything down it prints the exact command to start it\n(`cotal up --detach`, `cotal web`, `cotal supervise`). Displaying state never depends on it; setup\nstill launches nothing.\n\nSteps run in-process via `runSteps`\n([`lib/steps.ts`](../implementations/cli/src/lib/steps.ts)). A step can be `optional` (asked\nY/n), carry a `confirm` consent prompt, or be `live` (it draws its own pane via\n[`lib/live-window.ts`](../implementations/cli/src/lib/live-window.ts)). On failure, an\ninteractive run offers a Claude handoff\n([`lib/assist.ts`](../implementations/cli/src/lib/assist.ts)).\n\nThe **connector picker** (`pickConnectors`) multiselects Claude / OpenCode (detected\npre-checked). Only **Claude** runs an install (its wake channel binds to an *installed* plugin);\n**OpenCode auto-wires at spawn** (it injects its plugin via `buildLaunch`, never writing the\nuser's config), so the picker just marks it ready. Two experts (david, the engineer; sven, the\nguide) plus the operator's own driving session (`me`) are written by default, and `me` is the\npersona `cotal spawn me` drives.\n\n**`--yes`** forces non-interactive accept-all even on a TTY: optional plus `confirm` steps run\n(so the demo personas are written), the global install takes its default, and a failure aborts\nwith the log path and a non-zero exit. It still launches nothing. The control plane comes up with\n`cotal up --detach`. This is the agent/CI contract; keep it working.\n\n## Invariants\n\n| Thing | Must stay in sync across | Why |\n|---|---|---|\n| Marketplace name **`cotal-mesh`** | `setup.ts` (materialized `marketplace.json`), `CHANNEL_REF` in [`extensions/connector-claude-code/src/extension.ts`](../extensions/connector-claude-code/src/extension.ts), repo [`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json) | The wake channel ref `plugin:cotal@cotal-mesh` binds by this name |\n| Plugin assets | `setup.ts` copy list (`dist/mcp.cjs`, `dist/hook.cjs`, `.claude-plugin/plugin.json`, `.mcp.json`, `hooks/hooks.json`) and the connector `package.json` `files` field | Setup materializes the plugin from `Connector.pluginRoot`; missing or renamed assets break the install |\n| `Connector.pluginRoot` | [`packages/core/src/connector.ts`](../packages/core/src/connector.ts) (contract) plus set in the claude connector's `extension.ts` | How setup finds the plugin dir without importing the extension |\n| `BUNDLED_PKG_PREFIX` | [`lib/nats-bin.ts`](../implementations/cli/src/lib/nats-bin.ts) \u2194 the `@eplightning/nats-server-*` `optionalDependencies` in [`implementations/cli/package.json`](../implementations/cli/package.json) | The bundled NATS binary is resolved by `${prefix}-${platform}-${arch}`. (Future: swap the prefix to our own `@cotal-ai/nats-server-*`.) |\n| Onboard marker plus `ONBOARD_VERSION` | `~/.cotal/onboarded.json` in [`lib/onboard.ts`](../implementations/cli/src/lib/onboard.ts); version const in `setup.ts` | Flips first-run vs ensure |\n| Demo-agent format | `DEMO_AGENTS` in `setup.ts` matches the frontmatter shape read by [`packages/core/src/agent-file.ts`](../packages/core/src/agent-file.ts) (same as `examples/01-lateral-coordination/agents/`) | `cotal spawn <name>` loads these |\n| Managed personas | each `DEMO_AGENTS` body carries a `# managed by cotal-setup` frontmatter marker; `writeDemoAgent` refreshes the file when the body changes, backing a marker-less (user-edited) file up to `<name>.md.bak` first | Edit `DEMO_AGENTS` plus re-run setup to update david/sven/me; delete the marker line to take ownership |\n| `DEFAULT_SERVER` | [`packages/core/src/endpoint.ts`](../packages/core/src/endpoint.ts) | The address `cotal up` starts and the status card probes |\n\n## Background processes (`cotal up`)\n\n`cotal up` brings up the whole local stack in one place; since setup became configure-only\n(stage 2b), this is where the mesh and control plane start, so `cotal spawn --detach` /\n`cotal_spawn` find a manager right after `up`. The control plane comes up in cutover order:\nold-manager preflight \u2192 **delivery daemon** (auth mode only) \u2192 **manager**, via\n`ensureControlPlane`\n([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)). The detached\nprocesses, all stopped by `cotal down`:\n\nWith no explicit `--server`, `cotal up` auto-selects a free local port when the default broker\naddress is already held by another root or an unrecorded broker; an explicit `--server` remains\nfail-loud on collision.\n\n- **Mesh:** `startMeshDetached`\n ([`commands/up.ts`](../implementations/cli/src/commands/up.ts)) is the one place that boots a\n background nats-server (foreground `up` and `up --detach` both route through it). Writes\n `.cotal/nats.pid` and tails `.cotal/nats.log`.\n- **Delivery daemon:** `startDeliveryDetached` / `ensureDelivery`\n ([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)) re-execs `cotal\n deliver` detached with a pre-minted scoped `delivery.creds` (auth mode only, the durable\n backstop; open mode has none). Writes `.cotal/delivery.pid` and `.cotal/delivery.log`.\n- **Manager:** `startManagerDetached` / `ensureManager`\n ([`lib/manager-proc.ts`](../implementations/cli/src/lib/manager-proc.ts)) re-execs `cotal\n supervise` detached (pty runtime); it answers the control plane\n (`cotal_spawn` / `cotal_despawn` / `cotal_persona`). Writes `.cotal/manager.pid` and\n `.cotal/manager.log`; `managerUp()` checks pid liveness for setup's status card.\n\nThe **web dashboard** is *not* part of `cotal up`. It ships inside `cotal-ai` as the `@cotal-ai/web`\nextension and is seeded automatically by the boot reconcile \u2014 the same durable, version-locked path as\nthe built-in connectors (`SEEDED_EXTENSIONS`) \u2014 so it always matches the CLI version and needs no\nseparate install. Start it with `cotal web`; it records\n`.cotal/web.pid`, self-registers that process with `down`, and is addressed as\n`http://cotal.localhost:7799` (binds loopback; `*.localhost` resolves in Chrome/Firefox/Edge,\nSafari may need plain `127.0.0.1`). `webUp()` probes the port for setup's status card.\n\nAll recorded local processes self-register `local-process` descriptors. Bare `cotal down` resolves\nthe full set and stops it in dependency order; `cotal down manager` (or another component name)\nselects only that descriptor. Installed extensions cache their contributed registry keys, so the\nbase CLI does not hardcode optional package pidfiles.\n\nAll re-execs resolve this CLI via `selfArgv()` / `selfCotal()`\n([`lib/self-exec.ts`](../implementations/cli/src/lib/self-exec.ts)) = `[node, ...loaderFlags,\nentry]` (tsx loader in dev, compiled JS in prod), so they never need `cotal` on PATH; the stack\ncomes up identically via `npx`, `npm i -g`, and a dev clone.\n\nFor ergonomics only, an npx run with no global `cotal` offers to `npm i -g cotal-ai`\n(`offerGlobalInstall`, pinned to the running version): gated on `isNpx()` plus a PATH scan\n(`cotalOnPath()`, not `onPath(\"cotal\")`, since `cotal --version` is not a real command). The\ninteractive prompt defaults to yes, the non-interactive path (`--yes` or no TTY) takes the\ndefault, and a failed install is non-fatal (warn plus manual command). The same `self-exec.ts`\nexposes `displayCmd()`, the prefix (`cotal` / `npx cotal-ai` / `pnpm cotal`) used in the\nstatus-card hints so they match how you ran it.\n\n## Built-in connectors are seeded extensions\n\nThe four first-party connectors (`claude`, `opencode`, `hermes`, `pi`) are **not** static-imported by\nthe binary. The composition root (`bin/cotal.ts`) registers no connector; they self-register only when\nimported, and they are imported only once installed. On the first real command of each boot the CLI\n**seeds** them through the same `cotal ext add` path a third party uses, so they are ordinary\nextensions you can `cotal ext remove`. Code lives in [`implementations/cli/src/seed/`](../implementations/cli/src/seed/);\nthe entry is `reconcileSeededConnectors()`, gated in `runCli` before the manifest overlay so\n`ext seed --repair` survives a corrupt manifest.\n\n**What ships where.** The connectors are `devDependencies` of `cotal-ai` (not runtime deps), and a\n`prepack` step ([`bin/scripts/copy-seeded-connectors.mjs`](../bin/scripts/copy-seeded-connectors.mjs))\n`npm pack`s each into `bin/seeded-connectors/<name>/` (honoring each connector's own `files`), added to\nthe package `files`. `SEEDED_EXTENSIONS` (`@cotal-ai/workspace`) is the shared list \u2014 the connectors plus\n`web` \u2014 and the prepack asserts every bundled payload's `name` and `version` match the umbrella (the\n`fixed` changeset group keeps them lockstep), so a version-skewed payload can never be published; `web`\nalso emits `dist/web/vendor/vendor-manifest.json` (name/version/license/sha512) as the auditable\ninventory of its vendored browser libs (marked/DOMPurify ship as opaque `dist` bytes, not runtime deps).\n`seed/paths.ts:shippedSourceDir` resolves the live `extensions/<pkg>` dir in a\nsource checkout and `<cotal-ai>/seeded-connectors/<name>` in a published install. The reconcile copies\nthat payload into the durable store `seed/store/<version>/<name>` and `ext add --install-links` reifies\nthe `file:` dep from THAT stable path (a volatile source would fail to re-reify); `ext add` then\njunction-links each `@cotal-ai/*` peer to the binary's own copy. Before the first lazy import in each\nprocess, materialization rechecks those links by realpath and rebinds stale links under the extension\nlock. This lets the registry-facing imports of a global install, npx, and source worktrees share the\nmachine prefix while each process still gets its host's single `@cotal-ai/core` registry instance;\nlauncher artifacts are self-contained and do not resolve those mutable links later.\n\n**Reconcile policy** (generation = the `cotal-ai` version): a never-seeded built-in is seeded; a\nstill-installed one WE seeded (`source: \"seeded\"`) is refreshed only when the version bumps (semver\ncompare) or under `--force`; an operator-managed official entry (a manual `ext add` at a chosen\nversion, no seeded marker) is left untouched on upgrade; a deliberately-removed one stays removed. The\n`ever-seeded` **authority** (`seed/authority.json`, mirrored to a monotonic `.bak`) is the sole arbiter\nof removed-vs-never-seeded and is unioned with its backup on read, so a truncated authority never\nresurrects a removal. Every (re)install is verified before the generation stamp is written \u2014 recorded in\nthe manifest, present on disk with its entry file resolvable, and at the generation version \u2014 so a\nversion-skewed payload fails loud (`ext seed --repair`) rather than being stamped as current.\n\n**Crash safety.** One shared advisory lock ([`packages/workspace/src/advisory-lock.ts`](../packages/workspace/src/advisory-lock.ts):\natomic hard-link publish, PID + process-start liveness, bounded wait, dead-owner reclaim) guards the\nwhole reconcile and every `cotal ext` mutation; a live reconcile is waited on, not mistaken for a crash.\nA crash **cursor** is journaled before each connector mutation and cleared only at the final commit, so\na SIGKILL mid-run is detected on the next boot (fail loud \u2192 `ext seed --repair` re-installs the\ninterrupted connector before it clears the evidence). Seed children are authenticated (they carry the\nlive lock's nonce + parent PID, not a bare env flag) and record a liveness marker so a post-crash repair\nrefuses to race an orphaned installer. `ext seed --reset` quarantines corrupt manifest/authority state\naside and rebuilds. See [cli.md `ext`](cli.md#ext) for the operator-facing flags.\n"
350
+ "body": "# Setup internals (maintainer notes)\n\n> **Project** (non-normative maintainer notes) \xB7 **For:** maintainers changing how setup works\n>\n> How `cotal setup` works, and the cross-repo couplings it depends on. If you change one of\n> the things in the **Invariants** table, update the listed siblings in the same change, or\n> setup silently breaks for npx users.\n\n## The flow\n\n`cotal setup`\n([`implementations/cli/src/commands/setup.ts`](../implementations/cli/src/commands/setup.ts))\nis **configure-only and state-independent**: it checks prerequisites, installs the Claude Code\nplugin, and seeds persona files, and it **launches nothing**: no mesh, no web dashboard, no\nmanager, no delivery daemon, no cmux/tmux session, no demo. Starting the stack is `cotal up`; the\ndashboard is `cotal web`. Every file it writes is announced (`\u2192 wrote \u2026` via `provenance.wrote`).\nIt is two-tier, gated on a machine marker.\n\n**First run** (no `~/.cotal/onboarded.json`, or `--full`, or `--yes`) runs `runFirstRun(yes)`:\n\n- splash \u2192 intro \u2192 core **checks** (Node >= 20; **locate** `nats-server`: located, never\n started) \u2192 **connector picker** \u2192 write the demo personas (david/sven/me) and seed the generic\n `default` \u2192 **offer a global install** (`offerGlobalInstall`) \u2192 onboarded marker \u2192 a finale that\n lists the commands to start things (`cotal up --detach`, `cotal web`, `cotal spawn \u2026`,\n `cotal console`, `cotal down`). Nothing is running when it returns.\n- The old `--auth` / `--open` flags are **gone**: they set the mesh MODE at launch time, and setup\n no longer launches; mode is now `cotal up [--open]`'s concern (an unknown-option error names\n them, no silent no-op).\n\n**Later runs** run `runEnsure`: re-seed the `default` persona if it's missing (announced),\nre-offer the **global install** (`offerGlobalInstall`, same `isNpx()` + PATH-scan gate as first\nrun \u2014 so a repeat `npx cotal-ai setup` on a machine that still lacks a durable `cotal` finally\ninstalls it), then print the **status card** (`readyCard`). The card is **read-only probes** (`machineStatus`/`meshStatus`/`webUp`/`managerUp` for NATS, the plugin, the mesh, the web\ndashboard, and the manager) and for anything down it prints the exact command to start it\n(`cotal up --detach`, `cotal web`, `cotal supervise`). Displaying state never depends on it; setup\nstill launches nothing.\n\nSteps run in-process via `runSteps`\n([`lib/steps.ts`](../implementations/cli/src/lib/steps.ts)). A step can be `optional` (asked\nY/n), carry a `confirm` consent prompt, or be `live` (it draws its own pane via\n[`lib/live-window.ts`](../implementations/cli/src/lib/live-window.ts)). On failure, an\ninteractive run offers a Claude handoff\n([`lib/assist.ts`](../implementations/cli/src/lib/assist.ts)).\n\nThe **connector picker** (`pickConnectors`) multiselects Claude / OpenCode (detected\npre-checked). Only **Claude** runs an install (its wake channel binds to an *installed* plugin);\n**OpenCode auto-wires at spawn** (it injects its plugin via `buildLaunch`, never writing the\nuser's config), so the picker just marks it ready. Two experts (david, the engineer; sven, the\nguide) plus the operator's own driving session (`me`) are written by default, and `me` is the\npersona `cotal spawn me` drives.\n\n**`--yes`** forces non-interactive accept-all even on a TTY: optional plus `confirm` steps run\n(so the demo personas are written), the global install takes its default, and a failure aborts\nwith the log path and a non-zero exit. It still launches nothing. The control plane comes up with\n`cotal up --detach`. This is the agent/CI contract; keep it working.\n\n## Invariants\n\n| Thing | Must stay in sync across | Why |\n|---|---|---|\n| Marketplace name **`cotal-mesh`** | `setup.ts` (materialized `marketplace.json`), `CHANNEL_REF` in [`extensions/connector-claude-code/src/extension.ts`](../extensions/connector-claude-code/src/extension.ts), repo [`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json) | The wake channel ref `plugin:cotal@cotal-mesh` binds by this name |\n| Plugin assets | `setup.ts` copy list (`dist/mcp.cjs`, `dist/hook.cjs`, `.claude-plugin/plugin.json`, `.mcp.json`, `hooks/hooks.json`) and the connector `package.json` `files` field | Setup materializes the plugin from `Connector.pluginRoot`; missing or renamed assets break the install |\n| `Connector.pluginRoot` | [`packages/core/src/connector.ts`](../packages/core/src/connector.ts) (contract) plus set in the claude connector's `extension.ts` | How setup finds the plugin dir without importing the extension |\n| `BUNDLED_PKG_PREFIX` | [`lib/nats-bin.ts`](../implementations/cli/src/lib/nats-bin.ts) \u2194 the `@eplightning/nats-server-*` `optionalDependencies` in [`implementations/cli/package.json`](../implementations/cli/package.json) | The bundled NATS binary is resolved by `${prefix}-${platform}-${arch}`. (Future: swap the prefix to our own `@cotal-ai/nats-server-*`.) |\n| Onboard marker plus `ONBOARD_VERSION` | `~/.cotal/onboarded.json` in [`lib/onboard.ts`](../implementations/cli/src/lib/onboard.ts); version const in `setup.ts` | Flips first-run vs ensure |\n| Demo-agent format | `DEMO_AGENTS` in `setup.ts` matches the frontmatter shape read by [`packages/core/src/agent-file.ts`](../packages/core/src/agent-file.ts) (same as `examples/01-lateral-coordination/agents/`) | `cotal spawn <name>` loads these |\n| Managed personas | each `DEMO_AGENTS` body carries a `# managed by cotal-setup` frontmatter marker; `writeDemoAgent` refreshes the file when the body changes, backing a marker-less (user-edited) file up to `<name>.md.bak` first | Edit `DEMO_AGENTS` plus re-run setup to update david/sven/me; delete the marker line to take ownership |\n| `DEFAULT_SERVER` | [`packages/core/src/endpoint.ts`](../packages/core/src/endpoint.ts) | The address `cotal up` starts and the status card probes |\n\n## Background processes (`cotal up`)\n\n`cotal up` brings up the whole local stack in one place; since setup became configure-only\n(stage 2b), this is where the mesh and control plane start, so `cotal spawn --detach` /\n`cotal_spawn` find a manager right after `up`. The control plane comes up in cutover order:\nold-manager preflight \u2192 **delivery daemon** (auth mode only) \u2192 **manager**, via\n`ensureControlPlane`\n([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)). The detached\nprocesses, all stopped by `cotal down`:\n\nWith no explicit `--server`, `cotal up` auto-selects a free local port when the default broker\naddress is already held by another root or an unrecorded broker; an explicit `--server` remains\nfail-loud on collision.\n\n- **Mesh:** `startMeshDetached`\n ([`commands/up.ts`](../implementations/cli/src/commands/up.ts)) is the one place that boots a\n background nats-server (foreground `up` and `up --detach` both route through it). Writes\n `.cotal/nats.pid` and tails `.cotal/nats.log`.\n- **Delivery daemon:** `startDeliveryDetached` / `ensureDelivery`\n ([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)) re-execs `cotal\n deliver` detached with a pre-minted scoped `delivery.creds` (auth mode only, the durable\n backstop; open mode has none). Writes `.cotal/delivery.pid` and `.cotal/delivery.log`.\n- **Manager:** `startManagerDetached` / `ensureManager`\n ([`lib/manager-proc.ts`](../implementations/cli/src/lib/manager-proc.ts)) re-execs `cotal\n supervise` detached (pty runtime); it answers the control plane\n (`cotal_spawn` / `cotal_despawn` / `cotal_persona`). Writes `.cotal/manager.pid` and\n `.cotal/manager.log`; `managerUp()` checks pid liveness for setup's status card.\n\nThe **web dashboard** is *not* part of `cotal up`. It ships inside `cotal-ai` as the `@cotal-ai/web`\nextension and is seeded automatically by the boot reconcile \u2014 the same durable, version-locked path as\nthe built-in connectors (`SEEDED_EXTENSIONS`) \u2014 so it always matches the CLI version and needs no\nseparate install. Start it with `cotal web`; it records\n`.cotal/web.pid`, self-registers that process with `down`, and is addressed as\n`http://cotal.localhost:7799` (binds loopback; `*.localhost` resolves in Chrome/Firefox/Edge,\nSafari may need plain `127.0.0.1`). `webUp()` probes the port for setup's status card.\n\nAll recorded local processes self-register `local-process` descriptors. Bare `cotal down` resolves\nthe full set and stops it in dependency order; `cotal down manager` (or another component name)\nselects only that descriptor. Installed extensions cache their contributed registry keys, so the\nbase CLI does not hardcode optional package pidfiles.\n\nAll re-execs resolve this CLI via `selfArgv()` / `selfCotal()`\n([`lib/self-exec.ts`](../implementations/cli/src/lib/self-exec.ts)) = `[node, ...loaderFlags,\nentry]` (tsx loader in dev, compiled JS in prod), so they never need `cotal` on PATH; the stack\ncomes up identically via `npx`, `npm i -g`, and a dev clone.\n\nFor ergonomics only, an npx run with no global `cotal` offers to `npm i -g cotal-ai`\n(`offerGlobalInstall`, pinned to the running version): gated on `isNpx()` plus a PATH scan\n(`cotalOnPath()`, not `onPath(\"cotal\")`, since `cotal --version` is not a real command). The\ninteractive prompt defaults to yes, the non-interactive path (`--yes` or no TTY) takes the\ndefault, and a failed install is non-fatal (warn plus manual command). The same `self-exec.ts`\nexposes `displayCmd()`, the prefix (`cotal` / `npx cotal-ai` / `pnpm cotal`) used in the\nstatus-card hints so they match how you ran it.\n\n## Built-in connectors are seeded extensions\n\nThe four first-party connectors (`claude`, `opencode`, `hermes`, `pi`) are **not** static-imported by\nthe binary. The composition root (`bin/cotal.ts`) registers no connector; they self-register only when\nimported, and they are imported only once installed. On the first real command of each boot the CLI\n**seeds** them through the same `cotal ext add` path a third party uses, so they are ordinary\nextensions you can `cotal ext remove`. Code lives in [`implementations/cli/src/seed/`](../implementations/cli/src/seed/);\nthe entry is `reconcileSeededConnectors()`, gated in `runCli` before the manifest overlay so\n`ext seed --repair` survives a corrupt manifest.\n\n**What ships where.** The connectors are `devDependencies` of `cotal-ai` (not runtime deps), and a\n`prepack` step ([`bin/scripts/copy-seeded-connectors.mjs`](../bin/scripts/copy-seeded-connectors.mjs))\n`npm pack`s each into `bin/seeded-connectors/<name>/` (honoring each connector's own `files`), added to\nthe package `files`. `SEEDED_EXTENSIONS` (`@cotal-ai/workspace`) is the shared list \u2014 the connectors plus\n`web` \u2014 and the prepack asserts every bundled payload's `name` and `version` match the umbrella (the\n`fixed` changeset group keeps them lockstep), so a version-skewed payload can never be published; `web`\nalso emits `dist/web/vendor/vendor-manifest.json` (name/version/license/sha512) as the auditable\ninventory of its vendored browser libs (marked/DOMPurify ship as opaque `dist` bytes, not runtime deps).\n`seed/paths.ts:shippedSourceDir` resolves the live `extensions/<pkg>` dir in a\nsource checkout and `<cotal-ai>/seeded-connectors/<name>` in a published install. The reconcile copies\nthat payload into the durable store `seed/store/<version>/<name>` and `ext add --install-links` reifies\nthe `file:` dep from THAT stable path (a volatile source would fail to re-reify); `ext add` then\njunction-links each `@cotal-ai/*` peer to the binary's own copy. Before the first lazy import in each\nprocess, materialization rechecks those links by realpath and rebinds stale links under the extension\nlock. This lets the registry-facing imports of a global install, npx, and source worktrees share the\nmachine prefix while each process still gets its host's single `@cotal-ai/core` registry instance;\nlauncher artifacts are self-contained and do not resolve those mutable links later.\n\n**Reconcile policy** (generation = the `cotal-ai` version): a never-seeded built-in is seeded; a\nstill-installed one WE seeded (`source: \"seeded\"`) is refreshed only when the version bumps (semver\ncompare) or under `--force`; an operator-managed official entry (a manual `ext add` at a chosen\nversion, no seeded marker) is left untouched on upgrade; a deliberately-removed one stays removed. The\n`ever-seeded` **authority** (`seed/authority.json`, mirrored to a monotonic `.bak`) is the sole arbiter\nof removed-vs-never-seeded and is unioned with its backup on read, so a truncated authority never\nresurrects a removal. Every (re)install is verified before the generation stamp is written \u2014 recorded in\nthe manifest, present on disk with its entry file resolvable, and at the generation version \u2014 so a\nversion-skewed payload fails loud (`ext seed --repair`) rather than being stamped as current.\n\n**Crash safety.** One shared advisory lock ([`packages/workspace/src/advisory-lock.ts`](../packages/workspace/src/advisory-lock.ts):\natomic hard-link publish, PID + process-start liveness, bounded wait, dead-owner reclaim) guards the\nwhole reconcile and every `cotal ext` mutation; a live reconcile is waited on, not mistaken for a crash.\nA crash **cursor** is journaled before each connector mutation and cleared only at the final commit, so\na SIGKILL mid-run is detected on the next boot (fail loud \u2192 `ext seed --repair` re-installs the\ninterrupted connector before it clears the evidence). Seed children are authenticated (they carry the\nlive lock's nonce + parent PID, not a bare env flag) and record a liveness marker so a post-crash repair\nrefuses to race an orphaned installer. `ext seed --reset` quarantines corrupt manifest/authority state\naside and rebuilds. See [cli.md `ext`](cli.md#ext) for the operator-facing flags.\n"
351
351
  },
352
352
  {
353
353
  "slug": "spaces",
@@ -43556,7 +43556,7 @@ config(en_default());
43556
43556
 
43557
43557
  // ../connector-core/dist/docs-bundle.generated.js
43558
43558
  var DOCS_BUNDLE = {
43559
- "version": "0.14.0",
43559
+ "version": "0.14.2",
43560
43560
  "generatedFrom": "docs/*.md + SPEC.md + spec/cotal.schema.json",
43561
43561
  "pages": [
43562
43562
  {
@@ -43767,7 +43767,7 @@ var DOCS_BUNDLE = {
43767
43767
  "title": "Setup internals (maintainer notes)",
43768
43768
  "kind": "Project (non-normative maintainer notes)",
43769
43769
  "summary": "cotal setup (implementations/cli/src/commands/setup.ts) is configure-only and state-independent: it checks prerequisites, installs the Claude Code plugin, and seeds persona files, and it launches n\u2026",
43770
- "body": "# Setup internals (maintainer notes)\n\n> **Project** (non-normative maintainer notes) \xB7 **For:** maintainers changing how setup works\n>\n> How `cotal setup` works, and the cross-repo couplings it depends on. If you change one of\n> the things in the **Invariants** table, update the listed siblings in the same change, or\n> setup silently breaks for npx users.\n\n## The flow\n\n`cotal setup`\n([`implementations/cli/src/commands/setup.ts`](../implementations/cli/src/commands/setup.ts))\nis **configure-only and state-independent**: it checks prerequisites, installs the Claude Code\nplugin, and seeds persona files, and it **launches nothing**: no mesh, no web dashboard, no\nmanager, no delivery daemon, no cmux/tmux session, no demo. Starting the stack is `cotal up`; the\ndashboard is `cotal web`. Every file it writes is announced (`\u2192 wrote \u2026` via `provenance.wrote`).\nIt is two-tier, gated on a machine marker.\n\n**First run** (no `~/.cotal/onboarded.json`, or `--full`, or `--yes`) runs `runFirstRun(yes)`:\n\n- splash \u2192 intro \u2192 core **checks** (Node >= 20; **locate** `nats-server`: located, never\n started) \u2192 **connector picker** \u2192 write the demo personas (david/sven/me) and seed the generic\n `default` \u2192 **offer a global install** (`offerGlobalInstall`) \u2192 onboarded marker \u2192 a finale that\n lists the commands to start things (`cotal up --detach`, `cotal web`, `cotal spawn \u2026`,\n `cotal console`, `cotal down`). Nothing is running when it returns.\n- The old `--auth` / `--open` flags are **gone**: they set the mesh MODE at launch time, and setup\n no longer launches; mode is now `cotal up [--open]`'s concern (an unknown-option error names\n them, no silent no-op).\n\n**Later runs** run `runEnsure`: re-seed the `default` persona if it's missing (announced), then\nprint the **status card** (`readyCard`). The card is **read-only probes** (`machineStatus`/`meshStatus`/`webUp`/`managerUp` for NATS, the plugin, the mesh, the web\ndashboard, and the manager) and for anything down it prints the exact command to start it\n(`cotal up --detach`, `cotal web`, `cotal supervise`). Displaying state never depends on it; setup\nstill launches nothing.\n\nSteps run in-process via `runSteps`\n([`lib/steps.ts`](../implementations/cli/src/lib/steps.ts)). A step can be `optional` (asked\nY/n), carry a `confirm` consent prompt, or be `live` (it draws its own pane via\n[`lib/live-window.ts`](../implementations/cli/src/lib/live-window.ts)). On failure, an\ninteractive run offers a Claude handoff\n([`lib/assist.ts`](../implementations/cli/src/lib/assist.ts)).\n\nThe **connector picker** (`pickConnectors`) multiselects Claude / OpenCode (detected\npre-checked). Only **Claude** runs an install (its wake channel binds to an *installed* plugin);\n**OpenCode auto-wires at spawn** (it injects its plugin via `buildLaunch`, never writing the\nuser's config), so the picker just marks it ready. Two experts (david, the engineer; sven, the\nguide) plus the operator's own driving session (`me`) are written by default, and `me` is the\npersona `cotal spawn me` drives.\n\n**`--yes`** forces non-interactive accept-all even on a TTY: optional plus `confirm` steps run\n(so the demo personas are written), the global install takes its default, and a failure aborts\nwith the log path and a non-zero exit. It still launches nothing. The control plane comes up with\n`cotal up --detach`. This is the agent/CI contract; keep it working.\n\n## Invariants\n\n| Thing | Must stay in sync across | Why |\n|---|---|---|\n| Marketplace name **`cotal-mesh`** | `setup.ts` (materialized `marketplace.json`), `CHANNEL_REF` in [`extensions/connector-claude-code/src/extension.ts`](../extensions/connector-claude-code/src/extension.ts), repo [`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json) | The wake channel ref `plugin:cotal@cotal-mesh` binds by this name |\n| Plugin assets | `setup.ts` copy list (`dist/mcp.cjs`, `dist/hook.cjs`, `.claude-plugin/plugin.json`, `.mcp.json`, `hooks/hooks.json`) and the connector `package.json` `files` field | Setup materializes the plugin from `Connector.pluginRoot`; missing or renamed assets break the install |\n| `Connector.pluginRoot` | [`packages/core/src/connector.ts`](../packages/core/src/connector.ts) (contract) plus set in the claude connector's `extension.ts` | How setup finds the plugin dir without importing the extension |\n| `BUNDLED_PKG_PREFIX` | [`lib/nats-bin.ts`](../implementations/cli/src/lib/nats-bin.ts) \u2194 the `@eplightning/nats-server-*` `optionalDependencies` in [`implementations/cli/package.json`](../implementations/cli/package.json) | The bundled NATS binary is resolved by `${prefix}-${platform}-${arch}`. (Future: swap the prefix to our own `@cotal-ai/nats-server-*`.) |\n| Onboard marker plus `ONBOARD_VERSION` | `~/.cotal/onboarded.json` in [`lib/onboard.ts`](../implementations/cli/src/lib/onboard.ts); version const in `setup.ts` | Flips first-run vs ensure |\n| Demo-agent format | `DEMO_AGENTS` in `setup.ts` matches the frontmatter shape read by [`packages/core/src/agent-file.ts`](../packages/core/src/agent-file.ts) (same as `examples/01-lateral-coordination/agents/`) | `cotal spawn <name>` loads these |\n| Managed personas | each `DEMO_AGENTS` body carries a `# managed by cotal-setup` frontmatter marker; `writeDemoAgent` refreshes the file when the body changes, backing a marker-less (user-edited) file up to `<name>.md.bak` first | Edit `DEMO_AGENTS` plus re-run setup to update david/sven/me; delete the marker line to take ownership |\n| `DEFAULT_SERVER` | [`packages/core/src/endpoint.ts`](../packages/core/src/endpoint.ts) | The address `cotal up` starts and the status card probes |\n\n## Background processes (`cotal up`)\n\n`cotal up` brings up the whole local stack in one place; since setup became configure-only\n(stage 2b), this is where the mesh and control plane start, so `cotal spawn --detach` /\n`cotal_spawn` find a manager right after `up`. The control plane comes up in cutover order:\nold-manager preflight \u2192 **delivery daemon** (auth mode only) \u2192 **manager**, via\n`ensureControlPlane`\n([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)). The detached\nprocesses, all stopped by `cotal down`:\n\nWith no explicit `--server`, `cotal up` auto-selects a free local port when the default broker\naddress is already held by another root or an unrecorded broker; an explicit `--server` remains\nfail-loud on collision.\n\n- **Mesh:** `startMeshDetached`\n ([`commands/up.ts`](../implementations/cli/src/commands/up.ts)) is the one place that boots a\n background nats-server (foreground `up` and `up --detach` both route through it). Writes\n `.cotal/nats.pid` and tails `.cotal/nats.log`.\n- **Delivery daemon:** `startDeliveryDetached` / `ensureDelivery`\n ([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)) re-execs `cotal\n deliver` detached with a pre-minted scoped `delivery.creds` (auth mode only, the durable\n backstop; open mode has none). Writes `.cotal/delivery.pid` and `.cotal/delivery.log`.\n- **Manager:** `startManagerDetached` / `ensureManager`\n ([`lib/manager-proc.ts`](../implementations/cli/src/lib/manager-proc.ts)) re-execs `cotal\n supervise` detached (pty runtime); it answers the control plane\n (`cotal_spawn` / `cotal_despawn` / `cotal_persona`). Writes `.cotal/manager.pid` and\n `.cotal/manager.log`; `managerUp()` checks pid liveness for setup's status card.\n\nThe **web dashboard** is *not* part of `cotal up`. It ships inside `cotal-ai` as the `@cotal-ai/web`\nextension and is seeded automatically by the boot reconcile \u2014 the same durable, version-locked path as\nthe built-in connectors (`SEEDED_EXTENSIONS`) \u2014 so it always matches the CLI version and needs no\nseparate install. Start it with `cotal web`; it records\n`.cotal/web.pid`, self-registers that process with `down`, and is addressed as\n`http://cotal.localhost:7799` (binds loopback; `*.localhost` resolves in Chrome/Firefox/Edge,\nSafari may need plain `127.0.0.1`). `webUp()` probes the port for setup's status card.\n\nAll recorded local processes self-register `local-process` descriptors. Bare `cotal down` resolves\nthe full set and stops it in dependency order; `cotal down manager` (or another component name)\nselects only that descriptor. Installed extensions cache their contributed registry keys, so the\nbase CLI does not hardcode optional package pidfiles.\n\nAll re-execs resolve this CLI via `selfArgv()` / `selfCotal()`\n([`lib/self-exec.ts`](../implementations/cli/src/lib/self-exec.ts)) = `[node, ...loaderFlags,\nentry]` (tsx loader in dev, compiled JS in prod), so they never need `cotal` on PATH; the stack\ncomes up identically via `npx`, `npm i -g`, and a dev clone.\n\nFor ergonomics only, an npx run with no global `cotal` offers to `npm i -g cotal-ai`\n(`offerGlobalInstall`, pinned to the running version): gated on `isNpx()` plus a PATH scan\n(`cotalOnPath()`, not `onPath(\"cotal\")`, since `cotal --version` is not a real command). The\ninteractive prompt defaults to yes, the non-interactive path (`--yes` or no TTY) takes the\ndefault, and a failed install is non-fatal (warn plus manual command). The same `self-exec.ts`\nexposes `displayCmd()`, the prefix (`cotal` / `npx cotal-ai` / `pnpm cotal`) used in the\nstatus-card hints so they match how you ran it.\n\n## Built-in connectors are seeded extensions\n\nThe four first-party connectors (`claude`, `opencode`, `hermes`, `pi`) are **not** static-imported by\nthe binary. The composition root (`bin/cotal.ts`) registers no connector; they self-register only when\nimported, and they are imported only once installed. On the first real command of each boot the CLI\n**seeds** them through the same `cotal ext add` path a third party uses, so they are ordinary\nextensions you can `cotal ext remove`. Code lives in [`implementations/cli/src/seed/`](../implementations/cli/src/seed/);\nthe entry is `reconcileSeededConnectors()`, gated in `runCli` before the manifest overlay so\n`ext seed --repair` survives a corrupt manifest.\n\n**What ships where.** The connectors are `devDependencies` of `cotal-ai` (not runtime deps), and a\n`prepack` step ([`bin/scripts/copy-seeded-connectors.mjs`](../bin/scripts/copy-seeded-connectors.mjs))\n`npm pack`s each into `bin/seeded-connectors/<name>/` (honoring each connector's own `files`), added to\nthe package `files`. `SEEDED_EXTENSIONS` (`@cotal-ai/workspace`) is the shared list \u2014 the connectors plus\n`web` \u2014 and the prepack asserts every bundled payload's `name` and `version` match the umbrella (the\n`fixed` changeset group keeps them lockstep), so a version-skewed payload can never be published; `web`\nalso emits `dist/web/vendor/vendor-manifest.json` (name/version/license/sha512) as the auditable\ninventory of its vendored browser libs (marked/DOMPurify ship as opaque `dist` bytes, not runtime deps).\n`seed/paths.ts:shippedSourceDir` resolves the live `extensions/<pkg>` dir in a\nsource checkout and `<cotal-ai>/seeded-connectors/<name>` in a published install. The reconcile copies\nthat payload into the durable store `seed/store/<version>/<name>` and `ext add --install-links` reifies\nthe `file:` dep from THAT stable path (a volatile source would fail to re-reify); `ext add` then\njunction-links each `@cotal-ai/*` peer to the binary's own copy. Before the first lazy import in each\nprocess, materialization rechecks those links by realpath and rebinds stale links under the extension\nlock. This lets the registry-facing imports of a global install, npx, and source worktrees share the\nmachine prefix while each process still gets its host's single `@cotal-ai/core` registry instance;\nlauncher artifacts are self-contained and do not resolve those mutable links later.\n\n**Reconcile policy** (generation = the `cotal-ai` version): a never-seeded built-in is seeded; a\nstill-installed one WE seeded (`source: \"seeded\"`) is refreshed only when the version bumps (semver\ncompare) or under `--force`; an operator-managed official entry (a manual `ext add` at a chosen\nversion, no seeded marker) is left untouched on upgrade; a deliberately-removed one stays removed. The\n`ever-seeded` **authority** (`seed/authority.json`, mirrored to a monotonic `.bak`) is the sole arbiter\nof removed-vs-never-seeded and is unioned with its backup on read, so a truncated authority never\nresurrects a removal. Every (re)install is verified before the generation stamp is written \u2014 recorded in\nthe manifest, present on disk with its entry file resolvable, and at the generation version \u2014 so a\nversion-skewed payload fails loud (`ext seed --repair`) rather than being stamped as current.\n\n**Crash safety.** One shared advisory lock ([`packages/workspace/src/advisory-lock.ts`](../packages/workspace/src/advisory-lock.ts):\natomic hard-link publish, PID + process-start liveness, bounded wait, dead-owner reclaim) guards the\nwhole reconcile and every `cotal ext` mutation; a live reconcile is waited on, not mistaken for a crash.\nA crash **cursor** is journaled before each connector mutation and cleared only at the final commit, so\na SIGKILL mid-run is detected on the next boot (fail loud \u2192 `ext seed --repair` re-installs the\ninterrupted connector before it clears the evidence). Seed children are authenticated (they carry the\nlive lock's nonce + parent PID, not a bare env flag) and record a liveness marker so a post-crash repair\nrefuses to race an orphaned installer. `ext seed --reset` quarantines corrupt manifest/authority state\naside and rebuilds. See [cli.md `ext`](cli.md#ext) for the operator-facing flags.\n"
43770
+ "body": "# Setup internals (maintainer notes)\n\n> **Project** (non-normative maintainer notes) \xB7 **For:** maintainers changing how setup works\n>\n> How `cotal setup` works, and the cross-repo couplings it depends on. If you change one of\n> the things in the **Invariants** table, update the listed siblings in the same change, or\n> setup silently breaks for npx users.\n\n## The flow\n\n`cotal setup`\n([`implementations/cli/src/commands/setup.ts`](../implementations/cli/src/commands/setup.ts))\nis **configure-only and state-independent**: it checks prerequisites, installs the Claude Code\nplugin, and seeds persona files, and it **launches nothing**: no mesh, no web dashboard, no\nmanager, no delivery daemon, no cmux/tmux session, no demo. Starting the stack is `cotal up`; the\ndashboard is `cotal web`. Every file it writes is announced (`\u2192 wrote \u2026` via `provenance.wrote`).\nIt is two-tier, gated on a machine marker.\n\n**First run** (no `~/.cotal/onboarded.json`, or `--full`, or `--yes`) runs `runFirstRun(yes)`:\n\n- splash \u2192 intro \u2192 core **checks** (Node >= 20; **locate** `nats-server`: located, never\n started) \u2192 **connector picker** \u2192 write the demo personas (david/sven/me) and seed the generic\n `default` \u2192 **offer a global install** (`offerGlobalInstall`) \u2192 onboarded marker \u2192 a finale that\n lists the commands to start things (`cotal up --detach`, `cotal web`, `cotal spawn \u2026`,\n `cotal console`, `cotal down`). Nothing is running when it returns.\n- The old `--auth` / `--open` flags are **gone**: they set the mesh MODE at launch time, and setup\n no longer launches; mode is now `cotal up [--open]`'s concern (an unknown-option error names\n them, no silent no-op).\n\n**Later runs** run `runEnsure`: re-seed the `default` persona if it's missing (announced),\nre-offer the **global install** (`offerGlobalInstall`, same `isNpx()` + PATH-scan gate as first\nrun \u2014 so a repeat `npx cotal-ai setup` on a machine that still lacks a durable `cotal` finally\ninstalls it), then print the **status card** (`readyCard`). The card is **read-only probes** (`machineStatus`/`meshStatus`/`webUp`/`managerUp` for NATS, the plugin, the mesh, the web\ndashboard, and the manager) and for anything down it prints the exact command to start it\n(`cotal up --detach`, `cotal web`, `cotal supervise`). Displaying state never depends on it; setup\nstill launches nothing.\n\nSteps run in-process via `runSteps`\n([`lib/steps.ts`](../implementations/cli/src/lib/steps.ts)). A step can be `optional` (asked\nY/n), carry a `confirm` consent prompt, or be `live` (it draws its own pane via\n[`lib/live-window.ts`](../implementations/cli/src/lib/live-window.ts)). On failure, an\ninteractive run offers a Claude handoff\n([`lib/assist.ts`](../implementations/cli/src/lib/assist.ts)).\n\nThe **connector picker** (`pickConnectors`) multiselects Claude / OpenCode (detected\npre-checked). Only **Claude** runs an install (its wake channel binds to an *installed* plugin);\n**OpenCode auto-wires at spawn** (it injects its plugin via `buildLaunch`, never writing the\nuser's config), so the picker just marks it ready. Two experts (david, the engineer; sven, the\nguide) plus the operator's own driving session (`me`) are written by default, and `me` is the\npersona `cotal spawn me` drives.\n\n**`--yes`** forces non-interactive accept-all even on a TTY: optional plus `confirm` steps run\n(so the demo personas are written), the global install takes its default, and a failure aborts\nwith the log path and a non-zero exit. It still launches nothing. The control plane comes up with\n`cotal up --detach`. This is the agent/CI contract; keep it working.\n\n## Invariants\n\n| Thing | Must stay in sync across | Why |\n|---|---|---|\n| Marketplace name **`cotal-mesh`** | `setup.ts` (materialized `marketplace.json`), `CHANNEL_REF` in [`extensions/connector-claude-code/src/extension.ts`](../extensions/connector-claude-code/src/extension.ts), repo [`.claude-plugin/marketplace.json`](../.claude-plugin/marketplace.json) | The wake channel ref `plugin:cotal@cotal-mesh` binds by this name |\n| Plugin assets | `setup.ts` copy list (`dist/mcp.cjs`, `dist/hook.cjs`, `.claude-plugin/plugin.json`, `.mcp.json`, `hooks/hooks.json`) and the connector `package.json` `files` field | Setup materializes the plugin from `Connector.pluginRoot`; missing or renamed assets break the install |\n| `Connector.pluginRoot` | [`packages/core/src/connector.ts`](../packages/core/src/connector.ts) (contract) plus set in the claude connector's `extension.ts` | How setup finds the plugin dir without importing the extension |\n| `BUNDLED_PKG_PREFIX` | [`lib/nats-bin.ts`](../implementations/cli/src/lib/nats-bin.ts) \u2194 the `@eplightning/nats-server-*` `optionalDependencies` in [`implementations/cli/package.json`](../implementations/cli/package.json) | The bundled NATS binary is resolved by `${prefix}-${platform}-${arch}`. (Future: swap the prefix to our own `@cotal-ai/nats-server-*`.) |\n| Onboard marker plus `ONBOARD_VERSION` | `~/.cotal/onboarded.json` in [`lib/onboard.ts`](../implementations/cli/src/lib/onboard.ts); version const in `setup.ts` | Flips first-run vs ensure |\n| Demo-agent format | `DEMO_AGENTS` in `setup.ts` matches the frontmatter shape read by [`packages/core/src/agent-file.ts`](../packages/core/src/agent-file.ts) (same as `examples/01-lateral-coordination/agents/`) | `cotal spawn <name>` loads these |\n| Managed personas | each `DEMO_AGENTS` body carries a `# managed by cotal-setup` frontmatter marker; `writeDemoAgent` refreshes the file when the body changes, backing a marker-less (user-edited) file up to `<name>.md.bak` first | Edit `DEMO_AGENTS` plus re-run setup to update david/sven/me; delete the marker line to take ownership |\n| `DEFAULT_SERVER` | [`packages/core/src/endpoint.ts`](../packages/core/src/endpoint.ts) | The address `cotal up` starts and the status card probes |\n\n## Background processes (`cotal up`)\n\n`cotal up` brings up the whole local stack in one place; since setup became configure-only\n(stage 2b), this is where the mesh and control plane start, so `cotal spawn --detach` /\n`cotal_spawn` find a manager right after `up`. The control plane comes up in cutover order:\nold-manager preflight \u2192 **delivery daemon** (auth mode only) \u2192 **manager**, via\n`ensureControlPlane`\n([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)). The detached\nprocesses, all stopped by `cotal down`:\n\nWith no explicit `--server`, `cotal up` auto-selects a free local port when the default broker\naddress is already held by another root or an unrecorded broker; an explicit `--server` remains\nfail-loud on collision.\n\n- **Mesh:** `startMeshDetached`\n ([`commands/up.ts`](../implementations/cli/src/commands/up.ts)) is the one place that boots a\n background nats-server (foreground `up` and `up --detach` both route through it). Writes\n `.cotal/nats.pid` and tails `.cotal/nats.log`.\n- **Delivery daemon:** `startDeliveryDetached` / `ensureDelivery`\n ([`lib/delivery-proc.ts`](../implementations/cli/src/lib/delivery-proc.ts)) re-execs `cotal\n deliver` detached with a pre-minted scoped `delivery.creds` (auth mode only, the durable\n backstop; open mode has none). Writes `.cotal/delivery.pid` and `.cotal/delivery.log`.\n- **Manager:** `startManagerDetached` / `ensureManager`\n ([`lib/manager-proc.ts`](../implementations/cli/src/lib/manager-proc.ts)) re-execs `cotal\n supervise` detached (pty runtime); it answers the control plane\n (`cotal_spawn` / `cotal_despawn` / `cotal_persona`). Writes `.cotal/manager.pid` and\n `.cotal/manager.log`; `managerUp()` checks pid liveness for setup's status card.\n\nThe **web dashboard** is *not* part of `cotal up`. It ships inside `cotal-ai` as the `@cotal-ai/web`\nextension and is seeded automatically by the boot reconcile \u2014 the same durable, version-locked path as\nthe built-in connectors (`SEEDED_EXTENSIONS`) \u2014 so it always matches the CLI version and needs no\nseparate install. Start it with `cotal web`; it records\n`.cotal/web.pid`, self-registers that process with `down`, and is addressed as\n`http://cotal.localhost:7799` (binds loopback; `*.localhost` resolves in Chrome/Firefox/Edge,\nSafari may need plain `127.0.0.1`). `webUp()` probes the port for setup's status card.\n\nAll recorded local processes self-register `local-process` descriptors. Bare `cotal down` resolves\nthe full set and stops it in dependency order; `cotal down manager` (or another component name)\nselects only that descriptor. Installed extensions cache their contributed registry keys, so the\nbase CLI does not hardcode optional package pidfiles.\n\nAll re-execs resolve this CLI via `selfArgv()` / `selfCotal()`\n([`lib/self-exec.ts`](../implementations/cli/src/lib/self-exec.ts)) = `[node, ...loaderFlags,\nentry]` (tsx loader in dev, compiled JS in prod), so they never need `cotal` on PATH; the stack\ncomes up identically via `npx`, `npm i -g`, and a dev clone.\n\nFor ergonomics only, an npx run with no global `cotal` offers to `npm i -g cotal-ai`\n(`offerGlobalInstall`, pinned to the running version): gated on `isNpx()` plus a PATH scan\n(`cotalOnPath()`, not `onPath(\"cotal\")`, since `cotal --version` is not a real command). The\ninteractive prompt defaults to yes, the non-interactive path (`--yes` or no TTY) takes the\ndefault, and a failed install is non-fatal (warn plus manual command). The same `self-exec.ts`\nexposes `displayCmd()`, the prefix (`cotal` / `npx cotal-ai` / `pnpm cotal`) used in the\nstatus-card hints so they match how you ran it.\n\n## Built-in connectors are seeded extensions\n\nThe four first-party connectors (`claude`, `opencode`, `hermes`, `pi`) are **not** static-imported by\nthe binary. The composition root (`bin/cotal.ts`) registers no connector; they self-register only when\nimported, and they are imported only once installed. On the first real command of each boot the CLI\n**seeds** them through the same `cotal ext add` path a third party uses, so they are ordinary\nextensions you can `cotal ext remove`. Code lives in [`implementations/cli/src/seed/`](../implementations/cli/src/seed/);\nthe entry is `reconcileSeededConnectors()`, gated in `runCli` before the manifest overlay so\n`ext seed --repair` survives a corrupt manifest.\n\n**What ships where.** The connectors are `devDependencies` of `cotal-ai` (not runtime deps), and a\n`prepack` step ([`bin/scripts/copy-seeded-connectors.mjs`](../bin/scripts/copy-seeded-connectors.mjs))\n`npm pack`s each into `bin/seeded-connectors/<name>/` (honoring each connector's own `files`), added to\nthe package `files`. `SEEDED_EXTENSIONS` (`@cotal-ai/workspace`) is the shared list \u2014 the connectors plus\n`web` \u2014 and the prepack asserts every bundled payload's `name` and `version` match the umbrella (the\n`fixed` changeset group keeps them lockstep), so a version-skewed payload can never be published; `web`\nalso emits `dist/web/vendor/vendor-manifest.json` (name/version/license/sha512) as the auditable\ninventory of its vendored browser libs (marked/DOMPurify ship as opaque `dist` bytes, not runtime deps).\n`seed/paths.ts:shippedSourceDir` resolves the live `extensions/<pkg>` dir in a\nsource checkout and `<cotal-ai>/seeded-connectors/<name>` in a published install. The reconcile copies\nthat payload into the durable store `seed/store/<version>/<name>` and `ext add --install-links` reifies\nthe `file:` dep from THAT stable path (a volatile source would fail to re-reify); `ext add` then\njunction-links each `@cotal-ai/*` peer to the binary's own copy. Before the first lazy import in each\nprocess, materialization rechecks those links by realpath and rebinds stale links under the extension\nlock. This lets the registry-facing imports of a global install, npx, and source worktrees share the\nmachine prefix while each process still gets its host's single `@cotal-ai/core` registry instance;\nlauncher artifacts are self-contained and do not resolve those mutable links later.\n\n**Reconcile policy** (generation = the `cotal-ai` version): a never-seeded built-in is seeded; a\nstill-installed one WE seeded (`source: \"seeded\"`) is refreshed only when the version bumps (semver\ncompare) or under `--force`; an operator-managed official entry (a manual `ext add` at a chosen\nversion, no seeded marker) is left untouched on upgrade; a deliberately-removed one stays removed. The\n`ever-seeded` **authority** (`seed/authority.json`, mirrored to a monotonic `.bak`) is the sole arbiter\nof removed-vs-never-seeded and is unioned with its backup on read, so a truncated authority never\nresurrects a removal. Every (re)install is verified before the generation stamp is written \u2014 recorded in\nthe manifest, present on disk with its entry file resolvable, and at the generation version \u2014 so a\nversion-skewed payload fails loud (`ext seed --repair`) rather than being stamped as current.\n\n**Crash safety.** One shared advisory lock ([`packages/workspace/src/advisory-lock.ts`](../packages/workspace/src/advisory-lock.ts):\natomic hard-link publish, PID + process-start liveness, bounded wait, dead-owner reclaim) guards the\nwhole reconcile and every `cotal ext` mutation; a live reconcile is waited on, not mistaken for a crash.\nA crash **cursor** is journaled before each connector mutation and cleared only at the final commit, so\na SIGKILL mid-run is detected on the next boot (fail loud \u2192 `ext seed --repair` re-installs the\ninterrupted connector before it clears the evidence). Seed children are authenticated (they carry the\nlive lock's nonce + parent PID, not a bare env flag) and record a liveness marker so a post-crash repair\nrefuses to race an orphaned installer. `ext seed --reset` quarantines corrupt manifest/authority state\naside and rebuilds. See [cli.md `ext`](cli.md#ext) for the operator-facing flags.\n"
43771
43771
  },
43772
43772
  {
43773
43773
  "slug": "spaces",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/connector-opencode",
3
3
  "description": "Cotal connector for OpenCode: a native in-process plugin that joins a session to the mesh.",
4
- "version": "0.14.0",
4
+ "version": "0.14.2",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,8 +24,8 @@
24
24
  "@opencode-ai/plugin": "^1.16.2",
25
25
  "esbuild": "^0.28.0",
26
26
  "tsx": "^4.22.4",
27
- "@cotal-ai/core": "0.14.0",
28
- "@cotal-ai/connector-core": "0.14.0"
27
+ "@cotal-ai/core": "0.14.2",
28
+ "@cotal-ai/connector-core": "0.14.2"
29
29
  },
30
30
  "files": [
31
31
  "dist"