@kodama-run/sdk 0.3.0 → 0.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kodama-run/sdk",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts"
@@ -27,9 +27,9 @@
27
27
  "typescript": "*"
28
28
  },
29
29
  "scripts": {
30
- "build": "echo 'No build step for sdk (source-only)'",
30
+ "build": "bun build src/adapters/mcp.ts --target=node --outfile=bin/mcp.js && bun build src/adapters/cli.ts --target=node --outfile=bin/cli.js && chmod +x bin/mcp.js bin/cli.js",
31
31
  "test": "bun test",
32
32
  "lint": "tsc --noEmit",
33
- "clean": "rm -rf dist"
33
+ "clean": "rm -rf bin dist"
34
34
  }
35
35
  }
@@ -167,7 +167,7 @@ const server = new McpServer(
167
167
  "Kodama is a chatroom where AI agents have conversations on behalf of their owners.",
168
168
  "",
169
169
  "WORKFLOW:",
170
- "1. kodama_create_room — creates a room AND auto-joins. Display the COMPLETE output to the user verbatim — ALL links (invite, owner view, spectate) and room code. Do NOT summarize or rephrase. Then IMMEDIATELY call kodama_listen to start waiting for the other agent.",
170
+ "1. kodama_create_and_join_room — creates a room AND auto-joins. Display the COMPLETE output to the user verbatim — ALL links (invite, owner view, spectate) and room code. Do NOT summarize or rephrase. Then IMMEDIATELY call kodama_listen to start waiting for the other agent.",
171
171
  "2. kodama_listen — wait for messages. If status is 'waiting', call kodama_listen again. If status is 'your_turn', respond with kodama_say.",
172
172
  "3. After kodama_say: immediately call kodama_listen again.",
173
173
  "4. REPEAT steps 2-3. NEVER stop this loop unless the response says action=CONVERSATION_OVER. When you get CONVERSATION_OVER, call kodama_leave to disconnect, then summarize the outcome for the user.",
@@ -179,11 +179,11 @@ const server = new McpServer(
179
179
  );
180
180
 
181
181
  // ---------------------------------------------------------------------------
182
- // kodama_create_room
182
+ // kodama_create_and_join_room
183
183
  // ---------------------------------------------------------------------------
184
184
 
185
185
  server.tool(
186
- "kodama_create_room",
186
+ "kodama_create_and_join_room",
187
187
  "Create a Kodama chatroom and automatically join it. IMPORTANT: Display the returned output VERBATIM to the user — do not rephrase or summarize. Then immediately call kodama_listen to start waiting for the other agent. Do NOT wait for the user to tell you to proceed.",
188
188
  {
189
189
  topic: z.string().optional().describe("What this conversation is about, e.g. 'Dinner planning with Brandon'"),
@@ -238,22 +238,14 @@ server.tool(
238
238
  await room.join();
239
239
  } catch (err) {
240
240
  turnHandlerInstalled = false;
241
-
242
- let newToken: string | null = null;
243
- try {
244
- const inviteRes = await fetch(`${DEFAULT_RELAY}/api/rooms/${data.code}/invite`, {
245
- method: "POST",
246
- headers: { "Content-Type": "application/json" },
247
- body: JSON.stringify({}),
248
- });
249
- if (inviteRes.ok) {
250
- const inviteData = (await inviteRes.json()) as { inviteToken: string };
251
- newToken = inviteData.inviteToken;
252
- }
253
- } catch {}
254
-
255
- savedInviteToken = newToken;
256
-
241
+ savedInviteToken = null;
242
+
243
+ // Note: we used to call POST /api/rooms/:code/invite here to mint a
244
+ // fresh invite token for retry. That endpoint now requires an
245
+ // ownerToken (security fix: otherwise anyone with a public room code
246
+ // could generate valid invite tokens), and we don't have one yet
247
+ // because the join itself failed. Tell the user to create a new
248
+ // room instead — simpler and safer.
257
249
  const msg = (err as Error).message;
258
250
  return {
259
251
  content: [{
@@ -267,9 +259,7 @@ server.tool(
267
259
  `Share with your friend:`,
268
260
  `${WEB_URL}/room/${data.code}#invite=${friendToken}`,
269
261
  ``,
270
- newToken
271
- ? `Your invite token has been saved. Call kodama_join to retry.`
272
- : `Could not regenerate invite token. Create a new room instead.`,
262
+ `Call kodama_create_and_join_room again to start fresh.`,
273
263
  ].join("\n"),
274
264
  }],
275
265
  isError: true,
@@ -328,7 +318,7 @@ server.tool(
328
318
 
329
319
  if (!code || !token) {
330
320
  return {
331
- content: [{ type: "text" as const, text: "Missing room_code or invite_token. Create a room first with kodama_create_room, or provide both parameters." }],
321
+ content: [{ type: "text" as const, text: "Missing room_code or invite_token. Create a room first with kodama_create_and_join_room, or provide both parameters." }],
332
322
  isError: true,
333
323
  };
334
324
  }
package/src/room.ts CHANGED
@@ -9,6 +9,12 @@ import { createAgentCard } from "@kodama-run/shared";
9
9
  import { KodamaCrypto } from "./crypto.ts";
10
10
  import { PermissionChecker } from "./permissions.ts";
11
11
 
12
+ // Use ws package for Node <22 compatibility, native WebSocket otherwise
13
+ // @ts-ignore - dynamic require for Node compat
14
+ const WS: typeof WebSocket = typeof globalThis.WebSocket !== "undefined"
15
+ ? globalThis.WebSocket
16
+ : (() => { try { return require("ws"); } catch { return null; } })();
17
+
12
18
  // ---------------------------------------------------------------------------
13
19
  // Types
14
20
  // ---------------------------------------------------------------------------
@@ -139,7 +145,7 @@ export class Kodama {
139
145
  */
140
146
  leave(): void {
141
147
  this.closed = true;
142
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
148
+ if (this.ws && this.ws.readyState === WS.OPEN) {
143
149
  this.ws.send(JSON.stringify({ action: "leave" }));
144
150
  this.ws.close();
145
151
  }
@@ -172,7 +178,7 @@ export class Kodama {
172
178
  // -----------------------------------------------------------------------
173
179
 
174
180
  private connect(): void {
175
- const ws = new WebSocket(this.relayUrl);
181
+ const ws = new WS(this.relayUrl) as unknown as WebSocket;
176
182
  this.ws = ws;
177
183
 
178
184
  ws.addEventListener("open", () => {
@@ -343,7 +349,7 @@ export class Kodama {
343
349
  // Filter response through permissions
344
350
  const filtered = this.permissions.filterMessage(content);
345
351
 
346
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
352
+ if (this.ws && this.ws.readyState === WS.OPEN) {
347
353
  this.ws.send(
348
354
  JSON.stringify({
349
355
  action: "message",