{"_id":"@agent-marshal/marshal-opencode","name":"@agent-marshal/marshal-opencode","dist-tags":{"latest":"0.52.0"},"versions":{"0.52.0":{"dependencies":{"@myko/core":"^4.24.2","@opencode-ai/plugin":"^1.17.11"},"description":"opencode plugin that connects an opencode session to the marshal coordination daemon — roster registration, send_message/broadcast/join_room/leave_room/set_status tools, and real-time + per-turn delivery of sibling-agent messages. The opencode-native coun","devDependencies":{"@types/bun":"^1.3.14","typescript":"^5.3.3"},"exports":{".":"./src/index.ts"},"flux":{"tasks":["check","publish"]},"keywords":["opencode","opencode-plugin","marshal","agent-coordination"],"license":"MIT OR Apache-2.0","main":"./src/index.ts","name":"@agent-marshal/marshal-opencode","repository":{"directory":"plugins/marshal-opencode","type":"git","url":"git+https://github.com/ignition-is-go/marshal.git"},"scripts":{"check":"tsc --noEmit"},"type":"module","version":"0.52.0","_id":"@agent-marshal/marshal-opencode@0.52.0","gitHead":"35994a4fbf3de051efbf1aec58333114ad30d5d9","bugs":{"url":"https://github.com/ignition-is-go/marshal/issues"},"homepage":"https://github.com/ignition-is-go/marshal#readme","_nodeVersion":"22.23.2","_npmVersion":"10.9.8","dist":{"integrity":"sha512-CrwgZx78gWQWIZD0JrGwvAaVWx/mpIXR8EFZtLfuDaRzyFo1TxlQgqZZ702ZvON+glFwVjdXCaYHutQ2mICSWg==","shasum":"d8926dc70d49dd5d1f23786e38134f6b5d6e9a7f","tarball":"https://registry.npmjs.org/@agent-marshal/marshal-opencode/-/marshal-opencode-0.52.0.tgz","fileCount":6,"unpackedSize":56372,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDf/GnkI3AqUOBN81bW+DbnC764OVknwF3UHpVWgkEpjAIhAI15QSevVOJBYLbVDHqJnI4duP/TMHq7hcOpDKjFlcZL"}]},"_npmUser":{"name":"trevorsargent","email":"trevor.a.sargent@gmail.com"},"directories":{},"maintainers":[{"name":"trevorsargent","email":"trevor.a.sargent@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/marshal-opencode_0.52.0_1788982116027_0.7621994143790376"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-09T19:28:35.860Z","0.52.0":"2026-09-09T19:28:36.176Z","modified":"2026-09-09T19:28:36.411Z"},"maintainers":[{"name":"trevorsargent","email":"trevor.a.sargent@gmail.com"}],"description":"opencode plugin that connects an opencode session to the marshal coordination daemon — roster registration, send_message/broadcast/join_room/leave_room/set_status tools, and real-time + per-turn delivery of sibling-agent messages. The opencode-native coun","homepage":"https://github.com/ignition-is-go/marshal#readme","keywords":["opencode","opencode-plugin","marshal","agent-coordination"],"repository":{"directory":"plugins/marshal-opencode","type":"git","url":"git+https://github.com/ignition-is-go/marshal.git"},"bugs":{"url":"https://github.com/ignition-is-go/marshal/issues"},"license":"MIT OR Apache-2.0","readme":"# marshal-opencode\n\nThe [opencode](https://opencode.ai) counterpart of the Claude Code `marshal-shim`. Connects an opencode session to the [marshal](../../README.md) coordination daemon so it shows up on the same roster as every other live agent session and can pass messages to them.\n\nWhere the Claude integration needs a shim **binary** (an MCP child process), a proprietary `notifications/claude/channel` push, curl **hooks**, and `~/.claude` session-id discovery, this is a single in-process opencode **plugin**. opencode's plugin hooks supply everything the shim had to bolt on for Claude:\n\n| Concern | Claude `marshal-shim` | this plugin |\n|---|---|---|\n| Tools (send/broadcast/…) | stdio MCP server | native opencode tools (`marshal_*`), acting session auto-filled |\n| Roster registration | shim startup + `/hook/session-start` | `event` hook → `SET Session` over the myko client |\n| Session id | `CLAUDE_CODE_SESSION_ID` / `~/.claude` heuristics | handed to the plugin by opencode |\n| Live inbound | `notifications/claude/channel` (proprietary, flag-gated) | `client.tui.showToast` on the daemon's `NotifyChannel` push |\n| In-context delivery | `/hook/prompt-submit` → `<marshal_inbox>` | `experimental.chat.system.transform` → `<marshal_inbox>` |\n| Liveness / cleanup | 5 s shim loop + `/hook/session-end` | 5 s timer + `session.deleted` |\n\nIt talks to the daemon with [`@myko/core`](https://www.npmjs.com/package/@myko/core) — the same myko client the rest of the stack uses — so there is no second source of truth for the wire protocol (the marshal command/query/event shapes live in [`src/entities.ts`](src/entities.ts), hand-mirrored from the Rust `marshal-entities` crate).\n\n## Requirements\n\n- A reachable `marshal-daemon` (see the [top-level README](../../README.md)). The plugin defaults to `ws://localhost:6155`.\n- opencode (the plugin runs under opencode's Bun runtime).\n\n## Install\n\n### Option A — npm package (once published)\n\nAdd it to your opencode config (`opencode.json` / `~/.config/opencode/opencode.json`):\n\n```jsonc\n{\n  \"$schema\": \"https://opencode.ai/config.json\",\n  \"plugin\": [\"marshal-opencode\"]\n}\n```\n\nopencode installs the plugin and its `@myko/core` dependency into `~/.cache/opencode/node_modules` on next start.\n\n### Option B — local checkout (development)\n\nFrom this directory:\n\n```bash\nbun install\n```\n\nthen point opencode at the local plugin. Either symlink the package into opencode's global plugin dir:\n\n```bash\nln -s \"$PWD\" ~/.config/opencode/plugin/marshal-opencode\n```\n\nor reference it from `opencode.json` by path (see `opencode.example.jsonc`).\n\n## Configure\n\nSet these in the environment opencode launches under:\n\n| Var | Default | Meaning |\n|---|---|---|\n| `MARSHAL_DAEMON_ADDRESS` | `ws://localhost:6155` | daemon WebSocket URL (`MYKO_ADDRESS` honored as a fallback) |\n| `MARSHAL_OPERATOR` | `$USER` / `$USERNAME` | who this session belongs to (anchors the `op:*` room) |\n\n## What you get\n\nTools (call them like any other opencode tool):\n\n- `marshal_roster` — list every live session (host, cwd, branch, operator, session id)\n- `marshal_send_message` — direct-message a peer by session id\n- `marshal_broadcast` — message a room (`everyone`, `host:*`, `op:*`, `project:*`, ad-hoc)\n- `marshal_join_room` / `marshal_leave_room`\n- `marshal_set_status` — set the status peers see on the roster\n\nInbound peer messages arrive two ways: an instant TUI toast (real-time) and, authoritatively, as a `<marshal_inbox>` block injected at the start of your next turn (untrusted-input framed, then acked).\n\nMarshal is optional enrichment, not a turn dependency. If the daemon is\ndisconnected or an inbox command does not complete promptly, the hook skips\ninbox injection and lets the OpenCode turn continue; unread messages remain in\nMarshal for a later connected turn.\n\n## Testing\n\n```bash\nbun test                              # unit + integration\nbun test test/entities.test.ts test/daemon.test.ts  # unit only\n```\n\n- **Unit** (`test/entities.test.ts`, `test/daemon.test.ts`) pins the marshal wire shapes and proves a disconnected coordination daemon cannot block an OpenCode turn.\n- **Integration** (`test/integration.test.ts`) spins up a **real `marshal-daemon` binary** and round-trips the plugin's actual `MarshalDaemon` (the `@myko/core` client) against it over the real myko WS wire: roster registration + entity fields, send → inbox-pull + ack, the real-time `NotifyChannel` push, `join_room` + `broadcast` delivery, and `set_status`. It does **not** fake the daemon or the wire — that round-trip is the thing under test, and it is also the **drift guard** for the wire shapes in `entities.ts`: rename a field in the Rust `marshal-entities` source and the round-trip fails. The suite skips (loudly) if no daemon binary is found; enable it with:\n\n  ```bash\n  (cd ../.. && cargo build -p marshal-daemon)   # builds target/debug/marshal-daemon\n  bun test test/integration.test.ts             # or set MARSHAL_DAEMON_BIN=/path/to/marshal-daemon\n  ```\n\n## Status / follow-ups\n\nWhat's verified: typecheck against the real `@opencode-ai/plugin` + `@myko/core` types, and the **full daemon wire round-trip** (the 4 integration tests above). Not yet exercised: the opencode-hook glue inside a *live opencode session* (the hooks are typechecked against opencode's real types but unrun end-to-end in opencode itself).\n\n- `src/entities.ts` is a small hand-mirror of the Rust `marshal-entities` wire shapes, kept honest by the integration test above (the drift guard). `marshal-entities` *can* emit these as TypeScript via `cargo run -p marshal-entities --features codegen --bin typegen -- <dir>` (the `typegen` bin is wired and works), but ts-rs dumps the whole linked type graph — marshal's types plus all of myko's base types — so consuming the full static surface wasn't worth the build pipeline for this 6-verb plugin. The hand-mirror + live round-trip is the deliberate trade.\n- A pulse-deploy Ansible role to deploy + wire this on the fleet is a separate follow-up (parallel to `roles/marshal_shim*`).\n- A couple of opencode-surface details (the exact field carrying the session id into `experimental.chat.system.transform`, and toast variant names) are handled defensively; verify against the opencode version you run.\n\n> Note: the daemon serves its WebSocket at the `/myko` path. The plugin accepts a bare `ws://host:6155` (the fleet convention) and appends `/myko` automatically — see `withMykoPath` in `src/daemon.ts`.\n","readmeFilename":"README.md","_rev":"1-da81433235cf8742ccb3e8af69c0730d"}