baychat 0.21.4 → 0.22.0

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.
Files changed (44) hide show
  1. package/README.md +74 -13
  2. package/dist/approve-hook.js +5 -2
  3. package/dist/claude-onboarding.js +6 -12
  4. package/dist/commands.js +11 -6
  5. package/dist/connect-claude.js +19 -4
  6. package/dist/connect-dsh.js +165 -0
  7. package/dist/connect-plan.js +47 -1
  8. package/dist/connect.js +40 -9
  9. package/dist/doctor-command.js +7 -0
  10. package/dist/doctor.js +94 -1
  11. package/dist/dsh-config.js +147 -0
  12. package/dist/index.js +37 -2
  13. package/dist/relay/acp/agents.js +186 -0
  14. package/dist/relay/acp/approval.js +67 -0
  15. package/dist/relay/acp/client.js +253 -0
  16. package/dist/relay/acp/commands.js +69 -0
  17. package/dist/relay/acp/daemon-glue.js +201 -0
  18. package/dist/relay/acp/dump-config.js +31 -0
  19. package/dist/relay/acp/modes.js +36 -0
  20. package/dist/relay/acp/permissions.js +42 -0
  21. package/dist/relay/acp/policy.js +137 -0
  22. package/dist/relay/acp/presence.js +87 -0
  23. package/dist/relay/acp/prompt.js +69 -0
  24. package/dist/relay/acp/runner.js +311 -0
  25. package/dist/relay/acp/sdk.js +19 -0
  26. package/dist/relay/acp/turn-queue.js +163 -0
  27. package/dist/relay/acp/types.js +2 -0
  28. package/dist/relay/commands.js +28 -0
  29. package/dist/relay/daemon.js +195 -0
  30. package/dist/relay/mailbox.js +1 -0
  31. package/dist/relay/profiles.js +22 -0
  32. package/dist/relay/registry.js +46 -1
  33. package/dist/relay/session-commands.js +89 -0
  34. package/dist/relay/session-reply.js +25 -0
  35. package/dist/relay/terminal-pane.js +197 -0
  36. package/dist/relay/types.js +0 -9
  37. package/dist/runtime-install.js +9 -2
  38. package/dist/runtimes.js +16 -0
  39. package/dist/session-command.js +16 -3
  40. package/dist/session-setup.js +38 -0
  41. package/dist/skill-bootstrap.js +47 -0
  42. package/dist/start-command.js +235 -0
  43. package/dist/update-command.js +201 -0
  44. package/package.json +3 -1
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.START_RUNTIMES = void 0;
37
+ exports.startRefusal = startRefusal;
38
+ exports.cmdStart = cmdStart;
39
+ const child_process_1 = require("child_process");
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const readline = __importStar(require("readline"));
43
+ const config_1 = require("./config");
44
+ const terminal_pane_1 = require("./relay/terminal-pane");
45
+ const profiles_1 = require("./relay/profiles");
46
+ const runtime_binary_1 = require("./runtime-binary");
47
+ const update_command_1 = require("./update-command");
48
+ /**
49
+ * `baychat start [name]` — the whole product in one command.
50
+ *
51
+ * ── THE BAR THIS EXISTS TO MEET ───────────────────────────────────────────────────────
52
+ *
53
+ * Karmen's, 2026-09-14: *"everything we do we have to make very easy, basically just
54
+ * download baychat, and then start session, nothing else."*
55
+ *
56
+ * So: this launches the person's agent, inside a terminal multiplexer when one is
57
+ * available, and says nothing about multiplexers at all. They type one word. `/clear` and
58
+ * `/new` from the app then work, because `relay attach` finds `$TMUX`/`$TMUX_PANE` waiting
59
+ * for it — and if no multiplexer is installed, the agent still starts and everything except
60
+ * those two commands still works. **The fallback is silent on purpose.** A warning about a
61
+ * program they have never heard of, at the moment they are trying to start work, is exactly
62
+ * the "something else" this command exists to remove.
63
+ *
64
+ * ── WHY NOT A PSEUDO-TERMINAL OF OUR OWN ──────────────────────────────────────────────
65
+ *
66
+ * `node-pty` would remove the multiplexer requirement completely and work on Windows too.
67
+ * It is 64 MB unpacked and compiles through node-gyp on install, so `npx baychat` would
68
+ * start demanding a C++ toolchain — which fails for everyone, not only the people who
69
+ * wanted `/clear`. Checked 2026-09-14 and rejected on that basis, not on taste.
70
+ *
71
+ * ── WHY IT DOES NOT JUST JOIN, TOO ────────────────────────────────────────────────────
72
+ *
73
+ * Joining happens INSIDE the agent (the installed skill runs `baychat join`), because that
74
+ * is the only place the session's own runtime id, binary and pane can be read. This command
75
+ * gets the agent into a terminal we can reach; the agent does the rest, exactly as it does
76
+ * today.
77
+ */
78
+ const PREFERENCE_FILE = "start-runtime.json";
79
+ /**
80
+ * Runtimes `start` never probes for or launches. dsh (Ruling R27): it is experimental and
81
+ * meant to run only in a VM, and BayChat runs it through the relay over ACP
82
+ * (`baychat connect dsh`), which enforces its modes — a terminal launch would not.
83
+ */
84
+ const NOT_STARTED = {
85
+ dsh: "DeepSeek Harness is not started from here: the relay runs it over ACP. Set it up with `baychat connect dsh`.",
86
+ };
87
+ /** Runtimes worth probing for, best-supported first. */
88
+ exports.START_RUNTIMES = ["claude", "codex", ...profiles_1.RUNTIME_PROFILES.map((p) => p.id)].filter((id) => !Object.hasOwn(NOT_STARTED, id));
89
+ /** Why `start` will not launch `runtime`, or undefined when it will. */
90
+ function startRefusal(runtime) {
91
+ return Object.hasOwn(NOT_STARTED, runtime) ? NOT_STARTED[runtime] : undefined;
92
+ }
93
+ async function cmdStart(args) {
94
+ if (!(0, config_1.loadDeviceCredentials)()) {
95
+ console.error("This machine is not signed in yet. Run `npx baychat login` first.");
96
+ return 1;
97
+ }
98
+ // THE UPDATE NOBODY HAS TO REMEMBER.
99
+ //
100
+ // Shipping a CLI change reaches nobody until the package is refetched, every connected
101
+ // client's config is rewritten, and the relay is restarted — a live daemon holds old code
102
+ // in memory indefinitely. Three steps, in different places, and the failure is silence.
103
+ // So the one command people actually run does them, once a day.
104
+ //
105
+ // ⚠️ NEVER BLOCKS STARTING WORK. Any failure — offline, npm down, a permissions problem —
106
+ // is swallowed: the agent starts anyway. An update is an improvement to a session, never
107
+ // a precondition for one, and a person who cannot start their agent because a background
108
+ // refresh failed would rightly never trust this command again.
109
+ if (!args.includes("--no-update") && (0, update_command_1.autoUpdateDue)()) {
110
+ try {
111
+ await (0, update_command_1.cmdUpdate)({ quiet: true });
112
+ }
113
+ catch {
114
+ /* Starting the agent matters more than being current. */
115
+ }
116
+ }
117
+ const runtimeFlag = valueAfter(args, "--runtime");
118
+ const positional = args.filter((a) => !a.startsWith("--") && a !== runtimeFlag);
119
+ const name = positional[0]?.trim();
120
+ const runtime = runtimeFlag ?? (await chooseRuntime());
121
+ if (!runtime)
122
+ return 1;
123
+ const refusal = startRefusal(runtime);
124
+ if (refusal) {
125
+ console.error(refusal);
126
+ return 1;
127
+ }
128
+ const binary = (0, runtime_binary_1.resolveRuntimeBinary)(runtime, (0, runtime_binary_1.currentBinaryEnv)());
129
+ if (!binary.ok) {
130
+ console.error(`Could not start ${runtime}: ${binary.reason}`);
131
+ return 1;
132
+ }
133
+ // ALREADY INSIDE ONE? Then just run the agent. Nesting multiplexers sends every prefix
134
+ // key to the wrong one, and the person already has everything this command provides.
135
+ const inside = (0, terminal_pane_1.detectPane)();
136
+ const multiplexer = inside ? undefined : firstAvailable(["tmux", "screen"]);
137
+ if (!multiplexer) {
138
+ const result = (0, child_process_1.spawnSync)(binary.path, [], { stdio: "inherit" });
139
+ return result.status ?? 0;
140
+ }
141
+ const sessionName = name || "baychat";
142
+ const argv = multiplexer === "tmux"
143
+ ? // `-A` attaches to an existing session of that name instead of failing, so running
144
+ // this twice returns you to your agent rather than erroring at you.
145
+ ["new", "-A", "-s", sessionName, binary.path]
146
+ : ["-S", sessionName, "-R", binary.path];
147
+ const result = (0, child_process_1.spawnSync)(multiplexer, argv, { stdio: "inherit" });
148
+ if (result.error) {
149
+ // Falling back rather than failing: the multiplexer is an enhancement, and refusing to
150
+ // start somebody's agent because of one would be the tail wagging the dog.
151
+ const direct = (0, child_process_1.spawnSync)(binary.path, [], { stdio: "inherit" });
152
+ return direct.status ?? 0;
153
+ }
154
+ return result.status ?? 0;
155
+ }
156
+ /**
157
+ * Which agent to start.
158
+ *
159
+ * Asks only when it genuinely cannot tell — one installed runtime needs no question — and
160
+ * remembers the answer, because being asked the same thing every morning is "something
161
+ * else" by another name.
162
+ */
163
+ async function chooseRuntime() {
164
+ const remembered = readPreference();
165
+ if (remembered)
166
+ return remembered;
167
+ const env = (0, runtime_binary_1.currentBinaryEnv)();
168
+ const installed = [...new Set(exports.START_RUNTIMES)].filter((id) => (0, runtime_binary_1.resolveRuntimeBinary)(id, env).ok);
169
+ if (installed.length === 0) {
170
+ console.error("No coding agent found on this machine. Install one (Claude Code or Codex) and run this again.");
171
+ return undefined;
172
+ }
173
+ if (installed.length === 1) {
174
+ writePreference(installed[0]);
175
+ return installed[0];
176
+ }
177
+ console.log("Which agent should BayChat start?");
178
+ installed.forEach((id, index) => console.log(` ${index + 1}. ${id}`));
179
+ const answer = await ask(`Choose 1-${installed.length}: `);
180
+ const chosen = installed[Number(answer.trim()) - 1];
181
+ if (!chosen) {
182
+ console.error("Not one of the options.");
183
+ return undefined;
184
+ }
185
+ writePreference(chosen);
186
+ return chosen;
187
+ }
188
+ function ask(question) {
189
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
190
+ return new Promise((resolve) => rl.question(question, (answer) => {
191
+ rl.close();
192
+ resolve(answer);
193
+ }));
194
+ }
195
+ function preferencePath() {
196
+ return path.join((0, config_1.configDir)(), PREFERENCE_FILE);
197
+ }
198
+ function readPreference() {
199
+ try {
200
+ const raw = JSON.parse(fs.readFileSync(preferencePath(), "utf8"));
201
+ return typeof raw.runtime === "string" ? raw.runtime : undefined;
202
+ }
203
+ catch {
204
+ // Absent or unreadable is simply "not chosen yet" — never an error worth showing.
205
+ return undefined;
206
+ }
207
+ }
208
+ function writePreference(runtime) {
209
+ try {
210
+ fs.mkdirSync((0, config_1.configDir)(), { recursive: true });
211
+ fs.writeFileSync(preferencePath(), JSON.stringify({ runtime }, null, 2));
212
+ }
213
+ catch {
214
+ // Not remembering costs one question next time. Never worth failing a start for.
215
+ }
216
+ }
217
+ /**
218
+ * The first of these that RUNS, not the first on the PATH.
219
+ *
220
+ * The distinction has already cost this codebase a week: the relay's runtime probe picked a
221
+ * binary that existed and could not start, and resolution fell through to a version too old
222
+ * for the feature it was being asked for.
223
+ */
224
+ function firstAvailable(candidates) {
225
+ for (const candidate of candidates) {
226
+ const probe = (0, child_process_1.spawnSync)(candidate, ["--version"], { stdio: "ignore" });
227
+ if (!probe.error && probe.status === 0)
228
+ return candidate;
229
+ }
230
+ return undefined;
231
+ }
232
+ function valueAfter(args, flagName) {
233
+ const index = args.indexOf(flagName);
234
+ return index === -1 ? undefined : args[index + 1];
235
+ }
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.cmdUpdate = cmdUpdate;
37
+ exports.autoUpdateDue = autoUpdateDue;
38
+ const child_process_1 = require("child_process");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const config_1 = require("./config");
42
+ const credential_refresh_1 = require("./credential-refresh");
43
+ const commands_1 = require("./commands");
44
+ const client_paths_1 = require("./client-paths");
45
+ const commands_2 = require("./relay/commands");
46
+ /**
47
+ * `baychat update` — bring this whole machine onto the current version, in one word.
48
+ *
49
+ * ── THE PROBLEM IT EXISTS TO END ──────────────────────────────────────────────────────
50
+ *
51
+ * Karmen, 2026-09-14: *"it's not every time when we make an update the person has to
52
+ * update three different things from two different terminals… I just want it to be like
53
+ * `baychat update` and everything reloads."*
54
+ *
55
+ * She is describing something this project has been bitten by repeatedly. Shipping a CLI
56
+ * change reaches NOBODY until, on every machine: the package is refetched, each connected
57
+ * client's MCP config is rewritten, and **the relay daemon is restarted** — a live daemon
58
+ * holds the old code in memory and will happily keep running it for weeks. Three steps, in
59
+ * different places, none of them discoverable, and the failure is silence rather than an
60
+ * error.
61
+ *
62
+ * ── IT REFRESHES WHAT IS ALREADY THERE, AND CONNECTS NOTHING NEW ──────────────────────
63
+ *
64
+ * Every step is idempotent and every step is a RE-application of a decision the person
65
+ * already made. `refreshExistingClientConfigs` only rewrites config files that already
66
+ * name BayChat; Claude Code is only re-registered if its binary is present; Hermes only if
67
+ * `~/.hermes` exists. Running this on a machine that has never been connected does
68
+ * nothing, loudly — which is the right answer, because "update" must never quietly become
69
+ * "set up", and a command that installed things would not be safe to run automatically.
70
+ *
71
+ * And it must be safe to run automatically, because that is the second half of her ask:
72
+ * `baychat start` calls this on a day-boundary so nobody has to remember it at all.
73
+ */
74
+ /** How often `start` re-checks, when it is driving. One a day is invisible; one an hour is a tax. */
75
+ const AUTO_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
76
+ const STAMP_FILE = "last-update-check.json";
77
+ async function cmdUpdate(opts = {}) {
78
+ const say = (line) => {
79
+ if (!opts.quiet)
80
+ console.log(line);
81
+ };
82
+ const device = (0, config_1.loadDeviceCredentials)();
83
+ if (!device) {
84
+ console.error("This machine is not signed in, so there is nothing to update. Run `npx baychat login` first.");
85
+ return 1;
86
+ }
87
+ const done = [];
88
+ // 1. THE PACKAGE. Only a global install can be upgraded in place; an npx run already
89
+ // fetched whatever npx had cached, and the honest advice there is `@latest`.
90
+ const packageStep = updatePackage();
91
+ if (packageStep)
92
+ done.push(packageStep);
93
+ // 2. EVERY CONNECTED CLIENT's copy of the credential and endpoint.
94
+ try {
95
+ const updated = (0, credential_refresh_1.refreshExistingClientConfigs)({ url: `${device.baseUrl}/api/mcp`, token: device.token }, commands_1.realConfigIo, (0, client_paths_1.currentPathEnv)());
96
+ for (const file of updated)
97
+ done.push(`refreshed ${file}`);
98
+ }
99
+ catch (err) {
100
+ // Never fatal: the next steps are independent, and a machine that failed one of four
101
+ // is better off doing the other three than stopping at the first.
102
+ say(` Could not refresh client configs: ${errText(err)}`);
103
+ }
104
+ // 3. CLAUDE CODE, which is not an MCP-config client and registers through its own CLI.
105
+ if (hasBinary("claude") && (0, commands_1.registerWithClaude)(device.baseUrl, device.token)) {
106
+ done.push("re-registered BayChat with Claude Code");
107
+ }
108
+ // 4. THE RELAY — the step people forget, and the one that silently keeps old code
109
+ // running. Stopped and started rather than signalled: the daemon loads its code at
110
+ // process start, so nothing short of a restart picks up a new version.
111
+ try {
112
+ await (0, commands_2.cmdRelayStop)();
113
+ }
114
+ catch {
115
+ // Not running is the normal case on a fresh boot, and is not a failure to report.
116
+ }
117
+ const relay = await (0, commands_2.ensureRelayInstalled)();
118
+ done.push(relay.startsWith("Relay:") ? relay.toLowerCase() : relay);
119
+ writeStamp();
120
+ if (done.length === 0) {
121
+ say("Everything was already up to date.");
122
+ return 0;
123
+ }
124
+ if (opts.quiet) {
125
+ // Driven by `start`: one line, because the person asked to begin work, not to read a
126
+ // report. The full account is available by running the command by hand.
127
+ console.log(`BayChat updated (${done.length} step${done.length === 1 ? "" : "s"}).`);
128
+ return 0;
129
+ }
130
+ console.log("Updated this machine:");
131
+ for (const line of done)
132
+ console.log(` ✓ ${line}`);
133
+ return 0;
134
+ }
135
+ /**
136
+ * Has it been long enough for `start` to check again?
137
+ *
138
+ * A stamp rather than a version comparison: the point is to bound how often the network is
139
+ * touched on a path whose whole job is to start work quickly.
140
+ */
141
+ function autoUpdateDue(now = Date.now()) {
142
+ try {
143
+ const raw = JSON.parse(fs.readFileSync(stampPath(), "utf8"));
144
+ if (typeof raw.at !== "number")
145
+ return true;
146
+ return now - raw.at >= AUTO_CHECK_INTERVAL_MS;
147
+ }
148
+ catch {
149
+ // Never checked, or unreadable — checking is the safe answer.
150
+ return true;
151
+ }
152
+ }
153
+ function stampPath() {
154
+ return path.join((0, config_1.configDir)(), STAMP_FILE);
155
+ }
156
+ function writeStamp() {
157
+ try {
158
+ fs.mkdirSync((0, config_1.configDir)(), { recursive: true });
159
+ fs.writeFileSync(stampPath(), JSON.stringify({ at: Date.now() }));
160
+ }
161
+ catch {
162
+ // Costs one extra check tomorrow. Never worth failing an update for.
163
+ }
164
+ }
165
+ /**
166
+ * Upgrade the package itself, where that is a thing this process can do.
167
+ *
168
+ * ⚠️ AN `npx` RUN CANNOT UPGRADE ITSELF, and pretending otherwise is worse than saying so:
169
+ * npx resolves from its own cache, so `npx baychat` can keep running a version from weeks
170
+ * ago while reporting success at every step. The honest output there is the one flag that
171
+ * fixes it.
172
+ */
173
+ function updatePackage() {
174
+ const globalInstall = isGlobalInstall();
175
+ if (!globalInstall) {
176
+ console.log("Tip: `npx baychat` can run a cached older copy. Use `npx baychat@latest` (or install it once with `npm i -g baychat`) so updates land.");
177
+ return undefined;
178
+ }
179
+ const result = (0, child_process_1.spawnSync)("npm", ["install", "-g", "baychat@latest"], {
180
+ encoding: "utf8",
181
+ timeout: 120_000,
182
+ });
183
+ if (result.status !== 0) {
184
+ console.log(` Could not upgrade the package: ${result.stderr?.trim() || "npm failed"}`);
185
+ return undefined;
186
+ }
187
+ return "upgraded the baychat package";
188
+ }
189
+ /** Is this process running from a global npm install, rather than an npx cache? */
190
+ function isGlobalInstall() {
191
+ const self = process.argv[1] ?? "";
192
+ // npx unpacks into `_npx` inside the npm cache; a global install never does.
193
+ return self.includes("node_modules") && !self.includes("_npx");
194
+ }
195
+ function hasBinary(name) {
196
+ const probe = (0, child_process_1.spawnSync)(name, ["--version"], { stdio: "ignore", timeout: 15_000 });
197
+ return !probe.error && probe.status === 0;
198
+ }
199
+ function errText(err) {
200
+ return err instanceof Error ? err.message : String(err);
201
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baychat",
3
- "version": "0.21.4",
3
+ "version": "0.22.0",
4
4
  "description": "BayChat connector CLI — pair an agent session (Claude Code, Codex) with BayChat and chat in groups",
5
5
  "bin": {
6
6
  "baychat": "dist/index.js"
@@ -13,6 +13,7 @@
13
13
  "sync-protocol": "node scripts/sync-protocol.mjs",
14
14
  "build": "node scripts/sync-protocol.mjs && node scripts/sync-hermes-plugin.mjs && tsc && node scripts/make-executable.mjs",
15
15
  "test": "vitest run",
16
+ "test:contract": "vitest run src/relay/acp/contract.test.ts",
16
17
  "prepublishOnly": "npm run build && npm test"
17
18
  },
18
19
  "devDependencies": {
@@ -38,6 +39,7 @@
38
39
  "LICENSE"
39
40
  ],
40
41
  "dependencies": {
42
+ "@agentclientprotocol/sdk": "1.4.0",
41
43
  "@modelcontextprotocol/sdk": "^1.29.0",
42
44
  "qrcode": "^1.5.4",
43
45
  "yaml": "^2.9.0",