{"_id":"@agent-ops/synadia-agent-shim","_rev":"2-9e15cb9a4fd3ca6415520f6abd538390","name":"@agent-ops/synadia-agent-shim","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@agent-ops/synadia-agent-shim","version":"1.0.0","keywords":["agent","agents","multi-agent","synadia","nats","claude-code","codex","gemini","tmux","orchestration","ai"],"author":"Daniel Mestas","license":"Apache-2.0","_id":"@agent-ops/synadia-agent-shim@1.0.0","maintainers":[{"name":"danmestas","email":"daniel.mestas@craftdesign.group"}],"homepage":"https://github.com/danmestas/synadia-agent-shim","bugs":{"url":"https://github.com/danmestas/synadia-agent-shim/issues"},"bin":{"orch-agent-shim":"bin/orch-agent-shim","synadia-agent-shim":"bin/synadia-agent-shim"},"dist":{"shasum":"76d8f17a7777365496949eabf043588d871c6f49","tarball":"https://registry.npmjs.org/@agent-ops/synadia-agent-shim/-/synadia-agent-shim-1.0.0.tgz","fileCount":6,"integrity":"sha512-syyDZSsmC33ZvdxHth8jEw1ETI/YjRTMRmm3cCXM9OCtZGcX2wxb3rT8scG4LIq6iF7cM09JS7/VRwDZgp5nkQ==","signatures":[{"sig":"MEUCIQDZc0L60BimVkUBT76AY1JjVZfaUsMaLmczIvkMu3RCDQIgICgzkBZzTNCFEakYL53SE+8zg0inOp15nvoFmR3FjVE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24418},"shasum":"76d8f17a7777365496949eabf043588d871c6f49","engines":{"node":">=18"},"scripts":{"postinstall":"node scripts/postinstall.js"},"_npmUser":{"name":"danmestas","email":"daniel.mestas@craftdesign.group"},"_integrity":"sha512-syyDZSsmC33ZvdxHth8jEw1ETI/YjRTMRmm3cCXM9OCtZGcX2wxb3rT8scG4LIq6iF7cM09JS7/VRwDZgp5nkQ==","repository":{"url":"git+https://github.com/danmestas/synadia-agent-shim.git","type":"git"},"_npmVersion":"10.8.3","description":"Synadia Agent Protocol v0.3 shim — wraps agent CLIs (claude-code, codex, pi, gemini) and exposes them on a NATS bus.","directories":{},"_nodeVersion":"24.3.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/synadia-agent-shim_1.0.0_1779549448031_0.772602291319799","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@agent-ops/synadia-agent-shim","version":"1.1.0","description":"Synadia Agent Protocol v0.3 shim — wraps agent CLIs (claude-code, codex, pi, gemini) and exposes them on a NATS bus.","license":"Apache-2.0","author":{"name":"Daniel Mestas"},"repository":{"type":"git","url":"git+https://github.com/danmestas/synadia-agent-shim.git"},"homepage":"https://github.com/danmestas/synadia-agent-shim","bugs":{"url":"https://github.com/danmestas/synadia-agent-shim/issues"},"engines":{"node":">=18"},"bin":{"synadia-agent-shim":"bin/synadia-agent-shim","orch-agent-shim":"bin/orch-agent-shim"},"scripts":{"postinstall":"node scripts/postinstall.js"},"keywords":["agent","agents","multi-agent","synadia","nats","claude-code","codex","gemini","tmux","orchestration","ai"],"_id":"@agent-ops/synadia-agent-shim@1.1.0","gitHead":"ec29c351f65e90fc3d4386fa5c8efceed1870f17","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-4moLioZ1Qb9JMfFFOKdyNBcnpRc34/TV3+/N0TuZ+lYmz39T+oizvHt47nncDq0pdKK8fBH2QlVeK3iyJjVBpw==","shasum":"dd688501d46f55df0955d3c6624bee95a330bade","tarball":"https://registry.npmjs.org/@agent-ops/synadia-agent-shim/-/synadia-agent-shim-1.1.0.tgz","fileCount":6,"unpackedSize":26570,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDkhdyTf3Snt0kgb3jEvxAFWQ9a2U9KIH6H1dghdS2PEAiBilDlP1jyBgKh1GODJ+iXEoQNF3SxUMe+oqoI3OIx0kg=="}]},"_npmUser":{"name":"danmestas","email":"daniel.mestas@craftdesign.group"},"directories":{},"maintainers":[{"name":"danmestas","email":"daniel.mestas@craftdesign.group"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/synadia-agent-shim_1.1.0_1779630772441_0.15486787113137157"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-23T15:17:27.852Z","modified":"2026-05-24T13:52:52.688Z","1.0.0":"2026-05-23T15:17:28.174Z","1.1.0":"2026-05-24T13:52:52.567Z"},"bugs":{"url":"https://github.com/danmestas/synadia-agent-shim/issues"},"author":{"name":"Daniel Mestas"},"license":"Apache-2.0","homepage":"https://github.com/danmestas/synadia-agent-shim","keywords":["agent","agents","multi-agent","synadia","nats","claude-code","codex","gemini","tmux","orchestration","ai"],"repository":{"type":"git","url":"git+https://github.com/danmestas/synadia-agent-shim.git"},"description":"Synadia Agent Protocol v0.3 shim — wraps agent CLIs (claude-code, codex, pi, gemini) and exposes them on a NATS bus.","maintainers":[{"name":"danmestas","email":"daniel.mestas@craftdesign.group"}],"readme":"# synadia-agent-shim\n\nA Go shim that wraps an agent CLI (claude-code, codex, pi, gemini, or\nany custom adapter) and exposes it on a NATS bus using the\n[Synadia Agent Protocol v0.3](https://github.com/synadia-io/synadia-spec).\n\nOperators publish a prompt on `agents.prompt.<token>.<owner>.<pane>`;\nthe shim translates it into the CLI's native invocation, streams chunks\nback, and emits heartbeats + status per spec. Control-plane verbs\n(`interrupt`, `redirect`) are routed via `orch.signal.>`.\n\nExtracted from [danmestas/orch](https://github.com/danmestas/orch);\nsee [`docs/proposals/0001-extract-synadia-agent-shim.md`](https://github.com/danmestas/orch/blob/main/docs/proposals/0001-extract-synadia-agent-shim.md)\nfor the rationale.\n\n## Install\n\n### As a binary (npm)\n\n```sh\nnpm install -g @agent-ops/synadia-agent-shim\n```\n\nThe postinstall script fetches the matching release binary for your\nOS/arch and places it at `vendor/synadia-agent-shim`. Two wrapper\nscripts are exposed on `$PATH`:\n\n- `synadia-agent-shim` — canonical\n- `orch-agent-shim` — backwards-compat alias for one orch major release\n\n### As a Go library\n\n```sh\ngo get github.com/danmestas/synadia-agent-shim/shim\ngo get github.com/danmestas/synadia-agent-shim/adapter/echo\n```\n\n## Usage (CLI)\n\n```sh\nsynadia-agent-shim --agent claude-code --locator tmux:%37\nsynadia-agent-shim --agent claude-code --locator cmux:surface:30\nsynadia-agent-shim --agent claude-code --locator zmx:engineer-a\nsynadia-agent-shim --agent claude-code --pane %37     # deprecated alias for --locator tmux:%37\n```\n\nResolution order (most explicit wins):\n\n| Setting | Source |\n| --- | --- |\n| NATS URL | `--nats` → `$NATS_URL` → `~/.sesh/hub.nats.url` → `nats://127.0.0.1:4222` |\n| Owner | `--owner` → `$ORCH_OWNER` → `$USER` → `/etc/passwd` lookup |\n| Session | `--session` → `$SESH_SESSION` → omitted from metadata |\n| Instance ID | `--instance-id` → omitted (no slug-keyed subjects) |\n| CWD | `--cwd` → `tmux display-message -p '#{pane_current_path}'` |\n| Locator | `--locator` → `--pane` (deprecated; infers `tmux:`) → autodetect via `$CMUX_SURFACE_ID` / `$ZMX_SESSION` / `$TMUX_PANE` |\n\nThe shim exits when the bound pane dies (SIGCHLD from the parent\nshell). `orch-spawn` backstops this by `wait`-ing on a sentinel pid.\nThe pane-watchdog (`tmux display-message` poll) currently only runs\nunder the tmux engine; cmux and zmx manage surface lifetime themselves.\n\n### Engine support matrix\n\nThe shim dispatches the inbound-prompt send-verb based on the\npersistence engine it's running under. The engine is detected from\nenv vars at startup, or set explicitly via `--locator`.\n\n| Engine | Detection (env) | Locator form | Send verb | Interrupt verb |\n| --- | --- | --- | --- | --- |\n| `tmux` | `$TMUX_PANE` (preferred) or `$TMUX` | `tmux:%37` | `tmux send-keys -l -t %37 <text>` + `tmux send-keys -t %37 Enter` | `tmux send-keys -t %37 C-c` |\n| `cmux` | `$CMUX_SURFACE_ID` | `cmux:surface:30` or `cmux:<UUID>` | `cmux send --surface <ref> -- <text>\\n` | `cmux send-key --surface <ref> ctrl+c` |\n| `zmx` | `$ZMX_SESSION` | `zmx:engineer-a` | `zmx send <session> <text>\\r` | `zmx send <session> $'\\x03'` |\n\nDetection precedence: cmux → zmx → tmux (most-specific first; cmux and\nzmx pane environments sometimes also expose `$TMUX`, so engine-specific\nmarkers win).\n\nHeartbeats and `$SRV.INFO.agents` metadata publish both the new\n`engine` and `locator` fields alongside the back-compat `pane_id`:\n\n```json\n{\n  \"metadata\": {\n    \"agent\": \"claude-code\",\n    \"owner\": \"tester\",\n    \"pane_id\": \"surface:30\",\n    \"engine\": \"cmux\",\n    \"locator\": \"cmux:surface:30\"\n  }\n}\n```\n\n`pane_id` will be retired once downstream registry consumers\n(orch-registry) adopt `locator`. The dual surface mirrors the\n`--instance-id` dual-publish window.\n\n### `--pane` deprecation\n\n`--pane VALUE` continues to work and is interpreted as\n`--locator tmux:VALUE`, with a one-line stderr deprecation notice on\nstartup. It will be removed in the **next** shim release (see\n`CHANGELOG.md`). Callers should migrate to `--locator`.\n\n### `--instance-id`\n\n`--instance-id <slug>` attaches a human-readable worker identity to the\nshim. Subject-safe charset `[a-zA-Z0-9._-]`, length 1-128 — invalid\nslugs are rejected at startup so a typo fails loud, not at first publish.\n\nWhen set, the shim:\n\n1. Adds `instance_id: \"<slug>\"` to `$SRV.INFO.agents` metadata\n   alongside the existing `pane_id`. Discovery tools can filter by\n   either; `pane_id` stays so pane-watchdog and `tmux send-keys`\n   consumers keep working.\n2. Registers a SECOND prompt + status endpoint on the slug-keyed\n   subjects:\n   - `agents.prompt.<token>.<owner>.<slug>`\n   - `agents.status.<token>.<owner>.<slug>`\n3. Publishes heartbeats on the slug-keyed subject too:\n   - `agents.hb.<token>.<owner>.<slug>`\n\nDual-publish is gated by env var `ORCH_SLUG_DUAL_PUBLISH`:\n\n| Value | Behavior (with `--instance-id` set) |\n| --- | --- |\n| unset / `1` | legacy `pct<N>` track + slug track both live (default — safe during rollout) |\n| `0` | slug track only; legacy `pct<N>` subjects have no subscriber |\n\nWhen `--instance-id` is **not** set, the shim runs as before — only the\nlegacy `pct<N>`-keyed track is registered, regardless of the env var.\n\nThe dual-publish window is intended to last **two releases**; the\nlegacy `pct<N>` track will be retired in a follow-up issue. Track the\ndeprecation in `CHANGELOG.md`.\n\n## Usage (Go SDK)\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com/danmestas/synadia-agent-shim/adapter/echo\"\n    \"github.com/danmestas/synadia-agent-shim/shim\"\n)\n\nfunc main() {\n    cfg := shim.Config{\n        Agent:   \"echo\",\n        Pane:    \"%1\",\n        Owner:   \"you\",\n        NATSURL: shim.ReadNATSURL(\"\"),\n        Adapter: echo.New(),\n        // SubjectPrefix defaults to \"agents\".\n        // SignalPrefix defaults to \"orch.signal\".\n    }\n    if err := shim.Run(context.Background(), cfg); err != nil {\n        log.Fatal(err)\n    }\n}\n```\n\nNon-orch consumers can retarget the namespace:\n\n```go\ncfg.SubjectPrefix = \"dagnats\"      // dagnats.prompt.*, dagnats.status.*, ...\ncfg.SignalPrefix  = \"dagnats.signal\"\n```\n\n## Writing a custom adapter\n\nSee [`docs/adapter-sdk.md`](docs/adapter-sdk.md). Minimal contract:\n\n```go\ntype Adapter interface {\n    Start(ctx context.Context) error\n    OnPrompt(ctx context.Context, prompt string) error\n    Events() <-chan Chunk\n    Close() error\n}\n```\n\nAdapters that need imperative interrupt (TUI harnesses that don't\nhonour `ctx.Done()`) implement the optional `Aborter` interface — the\nshim type-asserts and calls `Abort` on `orch.signal.interrupt` arrival.\n\n## Built-in adapters\n\n- [`adapter/claudecode`](adapter/claudecode) — Anthropic claude-code CLI\n- [`adapter/codex`](adapter/codex) — OpenAI codex\n- [`adapter/pi`](adapter/pi) — Inflection pi\n- [`adapter/gemini`](adapter/gemini) — Google gemini-cli\n- [`adapter/echo`](adapter/echo) — reference adapter (no external deps)\n\n## Versioning\n\n- shim v1.0.0 = current behavior at extraction time from orch\n- Each Synadia spec version bump → shim major version bump\n- Adapter API additions → minor version bump\n- Bug fixes → patch\n\nThe `Adapter` and `Config` shapes are frozen at v1.\n\n## Releasing\n\nReleases are tag-driven. Pushing an annotated tag `vX.Y.Z` triggers\n`.github/workflows/release.yml`:\n\n1. `goreleaser` builds platform archives + creates the GitHub Release.\n2. `publish-npm` syncs `package.json` version from the tag, then runs\n   `npm publish --access public`.\n\nTo cut a release:\n\n```sh\ngit tag vX.Y.Z\ngit push --tags\n```\n\nOne-time operator setup: set the `NPM_TOKEN` secret in the repo's\nGitHub settings (Settings → Secrets → Actions) with an npm automation\ntoken that has publish access to `@agent-ops/synadia-agent-shim`.\nPRs run `npm publish --dry-run` in CI to catch packaging breakage\nbefore a tag is pushed.\n\n## Wire surface\n\n- Service name: `<SubjectPrefix>` (default `agents`), per Synadia §3.1\n- Prompt subject: `<SubjectPrefix>.prompt.<token>.<owner>.<session-or-pane>`\n- Status subject: `<SubjectPrefix>.status.<token>.<owner>.<session-or-pane>`\n- Heartbeat: `<SubjectPrefix>.hb.<token>.<owner>.<session-or-pane>` every 30s\n- Signal: `<SignalPrefix>.<verb>.<token>.<owner>.<pane>` (orch#133)\n\nEnvelope headers: W3C `traceparent` + `Sesh-Task-Id` / `Sesh-Attempt`\nwhen set. See [`docs/architecture.md`](docs/architecture.md) for the\nfull layout.\n\n## License\n\nApache-2.0. See [LICENSE](LICENSE).\n","readmeFilename":"README.md"}