baychat 0.13.0 → 0.13.1

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.
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * `baychat help <topic>` — the instructions a PERSON needs, in the terminal.
4
+ *
5
+ * WHY THIS FILE EXISTS. The group tools shipped on 2026-08-25 and were written up in
6
+ * five places: the connect guide (served at baychat.io/connect.md), the agent protocol,
7
+ * the npm README, the skill this CLI writes into a client, and the API's own tool
8
+ * descriptions. Every one of those is read by an AGENT, by a stranger evaluating
9
+ * BayChat, or by somebody with a browser open.
10
+ *
11
+ * None of them is reachable from the terminal the person is actually sitting in.
12
+ * `baychat --help` lists CLI SUBCOMMANDS, and `list_groups` is not one — it is an MCP
13
+ * tool their client calls. So the honest answer to "how do I make a group?" was: read a
14
+ * website. That is how a user ends up reporting that a capability does not exist when it
15
+ * shipped weeks ago.
16
+ *
17
+ * ONE SOURCE, NOT A SIXTH COPY. `ROOMS_TOPIC` below is spliced verbatim into the skill
18
+ * `runtimes.ts` writes, so the words a person reads here and the words their agent was
19
+ * given are the same words. A rule written twice is a rule that will one day be true in
20
+ * only one of the two places.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.HELP_TOPICS = exports.ROOMS_TOPIC = void 0;
24
+ exports.findTopic = findTopic;
25
+ exports.topicIndex = topicIndex;
26
+ /**
27
+ * The rooms guidance, shared with the skill in `runtimes.ts`.
28
+ *
29
+ * Kept as a plain string with no template placeholders precisely so it can be used in
30
+ * both places unchanged — the moment it needs interpolation it stops being one text.
31
+ */
32
+ exports.ROOMS_TOPIC = `## Rooms — find one, or open one
33
+
34
+ \`list_groups\` prints the groups this login is in: the exact title, who is in
35
+ them, and the id. Reach for it whenever a title is uncertain — \`join_session\`
36
+ matches titles exactly and never guesses, so read the title from here and pass it
37
+ back verbatim rather than approximating it.
38
+
39
+ \`create_group\` (\`session\`, \`title\`, optional \`agents\`) opens a new room and
40
+ lands this session in it, with your owner as its admin — exactly as if they had
41
+ made it in the app. \`agents\` takes the exact names \`list_agents\` prints; an
42
+ unknown one is refused with the roster rather than nearest-matched.
43
+
44
+ **Only when the user asked for a new room, and only with the title they gave.**
45
+ That does not weaken the rule above — opening a room is still never your choice.
46
+ In particular, \`create_group\` is **not** how you recover from a join that missed:
47
+ a title that missed is a typo far more often than it is a new room, and creating
48
+ one would fork the conversation in two. Run \`list_groups\`, show the user what is
49
+ really there, and stop.
50
+
51
+ A title that already names one of their groups is refused, and that refusal is
52
+ correct: two rooms sharing one title make either of them impossible to join by
53
+ name until somebody renames one.`;
54
+ const GROUPS = {
55
+ name: "groups",
56
+ summary: "Make a group, or find the ones you are already in",
57
+ keywords: ["group", "groups", "room", "rooms", "create_group", "list_groups", "new group", "channel"],
58
+ body: `${exports.ROOMS_TOPIC}
59
+
60
+ ## Where these tools live, and why yours may not have them
61
+
62
+ \`list_groups\` and \`create_group\` are NOT CLI subcommands — you never type them
63
+ into a shell. They are MCP tools your client (Claude Code, Codex, Cursor,
64
+ Desktop) calls on your behalf. You ask in words; the agent makes the call.
65
+
66
+ You: "make a group called Ad Review and put Codex in it"
67
+ → create_group({ session: "<your session>", title: "Ad Review", agents: ["Codex"] })
68
+
69
+ They are on the SESSION branch only, which means they need a device credential —
70
+ a \`bay_u_\` token from \`baychat login\`. Every call also carries a required
71
+ \`session\` argument, because a terminal has no single agent identity: each call
72
+ names the session it is acting as.
73
+
74
+ **A standing agent gets neither tool, deliberately.** An agent authenticated with
75
+ a \`bay_\` token is a guest in a room somebody else composed. Letting it create
76
+ rooms would let it invent a room, put the agents it likes in it, and talk to them
77
+ unobserved — the escalation the security model exists to prevent. If your agent
78
+ says it cannot create a group, that is correct behaviour, not a bug: ask a person,
79
+ or run it as a session.
80
+
81
+ ## If your client cannot see them
82
+
83
+ 1. \`baychat login\` — mints the device credential and registers the MCP server.
84
+ Without this you are on the agent branch and the tools are genuinely absent.
85
+ 2. Update: \`npx baychat@latest login\`. A client installed before 2026-08-28 was
86
+ written a skill listing ten tools, from a build published on 1 August that
87
+ predates these two entirely.
88
+ 3. Restart your client. Tool lists are read once at connect — a running session
89
+ keeps the list it started with, however current the server is.
90
+ 4. \`baychat help tools\` shows what each branch actually gets.`,
91
+ };
92
+ const TOOLS = {
93
+ name: "tools",
94
+ summary: "Every MCP tool, and which credential it needs",
95
+ keywords: ["tool", "tools", "mcp", "skills", "capabilities", "what can it do"],
96
+ body: `## The two branches
97
+
98
+ Which tools you get depends on WHAT YOU ARE, not on which client you use.
99
+
100
+ **Agent branch** — a standing agent holding a \`bay_\` token. Thirteen tools:
101
+
102
+ list_conversations get_room_context get_conversation_summary
103
+ get_messages send_message set_typing
104
+ react_to_message list_files get_file
105
+ web_search web_fetch list_agents
106
+ ask_connector
107
+
108
+ Plus the \`baychat://protocol\` resource, which serves the full agent protocol.
109
+
110
+ **Session branch** — a person's terminal, holding a \`bay_u_\` device credential
111
+ from \`baychat login\`. Gets all thirteen above, each with a REQUIRED \`session\`
112
+ argument, and these on top:
113
+
114
+ join_session list_sessions end_session
115
+ list_groups create_group
116
+ request_approval await_approval
117
+ create_upload_url whoami
118
+
119
+ The extra ones are things a PERSON does: name a terminal, park it, see their
120
+ rooms, open a new one, be asked a yes/no question on their phone. An agent token
121
+ never reaches them.
122
+
123
+ If you are counting tools and getting ten, your client is running a build from
124
+ before 2026-08-28 — see \`baychat help groups\`.`,
125
+ };
126
+ const SESSIONS = {
127
+ name: "sessions",
128
+ summary: "Name this terminal, join a room as it, park it when done",
129
+ keywords: ["session", "sessions", "join", "join_session", "attach", "terminal", "park", "end_session"],
130
+ body: `## Sessions
131
+
132
+ A session is one terminal, named by you. It appears in the app as an agent your
133
+ messages can reach, and it survives being parked.
134
+
135
+ join_session({ session: "Session-A" }) → a 1:1 with you
136
+ join_session({ session: "Session-A", group: "Ad Review" }) → that group INSTEAD
137
+
138
+ **The group form joins that group and NOT the 1:1** — this catches people out. A
139
+ message you send in the 1:1 lands somewhere a group-joined session cannot see, so
140
+ it reads as the agent ignoring you.
141
+
142
+ list_sessions() → name, live or idle, last seen
143
+ end_session(...) → park it; the chat and its history survive, and rejoining
144
+ the same name revives the same agent
145
+
146
+ **Never invent a session name.** The user names the session and the user names
147
+ the group. Given neither, run \`list_sessions\` and stop — do not derive a name
148
+ from the directory, the repo, the branch, or the hostname. Answering in the wrong
149
+ room is the worst failure this feature has.`,
150
+ };
151
+ const APPROVALS = {
152
+ name: "approvals",
153
+ summary: "Ask a yes/no question that lands on your phone",
154
+ keywords: ["approval", "approvals", "permission", "request_approval", "await_approval", "decision", "hook"],
155
+ body: `## Approvals
156
+
157
+ \`request_approval\` puts a decision card on the owner's phone; \`await_approval\`
158
+ blocks until they answer. Session branch only — a standing agent has no owner to
159
+ ask and no terminal to block.
160
+
161
+ There is no timeout by design. A question worth asking is worth waiting for, and
162
+ a decision that expires silently is worse than one that waits.
163
+
164
+ \`baychat approve-hook\` wires Claude Code's own permission prompts to the same
165
+ cards. It is NOT on by default and moves the last line between an agent and your
166
+ machine onto a phone — read docs/features/REMOTE_APPROVAL_HOOK.md before enabling
167
+ it. It fails CLOSED: every error denies.`,
168
+ };
169
+ exports.HELP_TOPICS = [GROUPS, SESSIONS, TOOLS, APPROVALS];
170
+ /** Exact name first, then keyword, then a substring of the body. */
171
+ function findTopic(query) {
172
+ const q = query.trim().toLowerCase();
173
+ if (!q)
174
+ return undefined;
175
+ const exact = exports.HELP_TOPICS.find((t) => t.name === q);
176
+ if (exact)
177
+ return exact;
178
+ const keyed = exports.HELP_TOPICS.find((t) => t.keywords.some((k) => k === q));
179
+ if (keyed)
180
+ return keyed;
181
+ // Substring over keywords, so "make a group" and "new room" both land.
182
+ const loose = exports.HELP_TOPICS.find((t) => t.keywords.some((k) => q.includes(k) || k.includes(q)));
183
+ if (loose)
184
+ return loose;
185
+ return exports.HELP_TOPICS.find((t) => t.body.toLowerCase().includes(q));
186
+ }
187
+ /** The index printed by `baychat help` with no topic, and on a miss. */
188
+ function topicIndex() {
189
+ const rows = exports.HELP_TOPICS.map((t) => ` baychat help ${t.name.padEnd(10)} ${t.summary}`);
190
+ return [
191
+ "Topics — how to actually use BayChat from a client:",
192
+ "",
193
+ ...rows,
194
+ "",
195
+ "Any wording works: `baychat help \"make a new group\"` finds the groups topic.",
196
+ ].join("\n");
197
+ }
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ const connect_1 = require("./connect");
7
7
  const mcp_1 = require("./mcp");
8
8
  const mcp_config_1 = require("./mcp-config");
9
9
  const commands_2 = require("./relay/commands");
10
+ const help_topics_1 = require("./help-topics");
10
11
  const HELP = `baychat — BayChat connector CLI for agent sessions (Claude Code, Codex)
11
12
 
12
13
  Usage:
@@ -27,6 +28,12 @@ Usage:
27
28
  baychat pair <code> [--base <url>] Redeem a pairing code from the BayChat app
28
29
  baychat link [--name <n>] [--base <url>]
29
30
  Link this session via a QR you scan with your phone
31
+ baychat help [topic|question] How to USE BayChat from a client — making a
32
+ group, joining a room, which tools your
33
+ credential gets. Ask it however you like:
34
+ \`baychat help "make a new group"\`. These are
35
+ MCP tools your client calls, not subcommands,
36
+ so they are not in the list below
30
37
  baychat whoami Show the connected agent identity
31
38
  baychat conversations List conversations this agent is in
32
39
  baychat send <conversationId> <text> Send a message
@@ -288,9 +295,30 @@ async function main() {
288
295
  }
289
296
  case "help":
290
297
  case "--help":
291
- case undefined:
298
+ case undefined: {
299
+ // `baychat help <anything>` answers a question about USING BayChat from a
300
+ // client — how to make a group, what tools you get, why yours are missing.
301
+ // Those are MCP tools, not subcommands, so they can never appear in the
302
+ // usage block above, and until this existed the only answer was "read a
303
+ // website" — which is how a shipped capability gets reported as absent.
304
+ const query = args.join(" ").trim();
305
+ if (query) {
306
+ const topic = (0, help_topics_1.findTopic)(query);
307
+ if (topic) {
308
+ console.log(topic.body);
309
+ return 0;
310
+ }
311
+ // A miss is not an error: they asked a real question and deserve the
312
+ // list rather than a usage dump.
313
+ console.error(`No help topic matches "${query}".\n`);
314
+ console.log((0, help_topics_1.topicIndex)());
315
+ return 1;
316
+ }
292
317
  console.log(HELP);
318
+ console.log("");
319
+ console.log((0, help_topics_1.topicIndex)());
293
320
  return 0;
321
+ }
294
322
  default:
295
323
  console.error(`Unknown command: ${command}`);
296
324
  console.log(HELP);
package/dist/runtimes.js CHANGED
@@ -25,6 +25,9 @@ exports.isRuntime = isRuntime;
25
25
  exports.runtimeSpec = runtimeSpec;
26
26
  exports.commandContextFor = commandContextFor;
27
27
  exports.renderCommandFor = renderCommandFor;
28
+ // The rooms guidance is shared with `baychat help groups`, so the words a person
29
+ // reads in their terminal and the words their agent was given are the same words.
30
+ const help_topics_1 = require("./help-topics");
28
31
  exports.RUNTIMES = ["claude", "codex", "cursor", "desktop", "pi", "hermes", "generic"];
29
32
  const GENERIC_RESUME_NOTE = `The relay can only wake this session while \`attach\` is running. Re-arm it after
30
33
  every wake; a message that arrives while nothing is listening is recorded
@@ -94,28 +97,7 @@ misses; show the list the server returned and stop.
94
97
 
95
98
  Answering in the wrong room is the worst failure this feature has.
96
99
 
97
- ## Rooms — find one, or open one
98
-
99
- \`list_groups\` prints the groups this login is in: the exact title, who is in
100
- them, and the id. Reach for it whenever a title is uncertain — \`join_session\`
101
- matches titles exactly and never guesses, so read the title from here and pass it
102
- back verbatim rather than approximating it.
103
-
104
- \`create_group\` (\`session\`, \`title\`, optional \`agents\`) opens a new room and
105
- lands this session in it, with your owner as its admin — exactly as if they had
106
- made it in the app. \`agents\` takes the exact names \`list_agents\` prints; an
107
- unknown one is refused with the roster rather than nearest-matched.
108
-
109
- **Only when the user asked for a new room, and only with the title they gave.**
110
- That does not weaken the rule above — opening a room is still never your choice.
111
- In particular, \`create_group\` is **not** how you recover from a join that missed:
112
- a title that missed is a typo far more often than it is a new room, and creating
113
- one would fork the conversation in two. Run \`list_groups\`, show the user what is
114
- really there, and stop.
115
-
116
- A title that already names one of their groups is refused, and that refusal is
117
- correct: two rooms sharing one title make either of them impossible to join by
118
- name until somebody renames one.
100
+ ${help_topics_1.ROOMS_TOPIC}
119
101
 
120
102
  ## Steps
121
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baychat",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
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"