kojee-mcp 0.7.5-staging.2 → 0.8.0-beta.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 (38) hide show
  1. package/README.md +100 -0
  2. package/dist/{chunk-67F67AQ6.js → chunk-3N4YYIOJ.js} +28 -0
  3. package/dist/{chunk-WBXU27BF.js → chunk-7J5CSXGS.js} +1 -1
  4. package/dist/{chunk-XPIW4N55.js → chunk-BO52SILE.js} +1 -1
  5. package/dist/{chunk-TCWIXG5C.js → chunk-D5VPUCPY.js} +1 -1
  6. package/dist/{chunk-CUOJHP2S.js → chunk-GP7XNGUR.js} +10 -2
  7. package/dist/{chunk-TNZITJAB.js → chunk-HMS4PZLC.js} +1 -1
  8. package/dist/chunk-JRCHS6CP.js +14 -0
  9. package/dist/chunk-NMHM6SII.js +118 -0
  10. package/dist/{chunk-A4IOKD4Z.js → chunk-P2R3KWYK.js} +1 -1
  11. package/dist/{chunk-FJUAMJHU.js → chunk-QLARIAUJ.js} +26 -2
  12. package/dist/{chunk-ZSHCHOPL.js → chunk-TXGO2W5I.js} +6 -14
  13. package/dist/{send-cli-45RLGYIC.js → chunk-VGZ4M3FP.js} +8 -11
  14. package/dist/{chunk-PKOBR7ZN.js → chunk-WZP7HXXN.js} +9 -0
  15. package/dist/cli.js +75 -13
  16. package/dist/{codex-prompt-submit-hook-J5PZEJSK.js → codex-prompt-submit-hook-6QUCHZB2.js} +1 -1
  17. package/dist/{codex-stop-hook-32W4BIOM.js → codex-stop-hook-5LVFGGFE.js} +1 -1
  18. package/dist/{connect-handler-NZINEMG3.js → connect-handler-5NACO67R.js} +2 -2
  19. package/dist/cowork-E46Z2QLU.js +370 -0
  20. package/dist/cowork-worker-RSXWOYUQ.js +316 -0
  21. package/dist/{doctor-WUU5BVPT.js → doctor-DK72HJI3.js} +4 -4
  22. package/dist/{doctor-codex-PJFWIABF.js → doctor-codex-MECEBXJC.js} +1 -1
  23. package/dist/{event-log-2NBJEIEP.js → event-log-QLWB22KX.js} +2 -2
  24. package/dist/{event-stream-ATKYXDBV.js → event-stream-L432EP4U.js} +1 -1
  25. package/dist/{hook-server-CWBH6PFL.js → hook-server-6EVSFLNW.js} +1 -1
  26. package/dist/index.js +5 -4
  27. package/dist/lib.d.ts +37 -1
  28. package/dist/lib.js +1 -1
  29. package/dist/messages-cli-KKFCKV55.js +24 -0
  30. package/dist/{registry-XJ67EO22.js → registry-E2DW73RE.js} +14 -14
  31. package/dist/send-cli-NPFQWSBM.js +18 -0
  32. package/dist/{server-DU3LFS32.js → server-D5VAMAQW.js} +2 -2
  33. package/dist/{session-discovery-NHCZ2XWA.js → session-discovery-4LZNLBPN.js} +3 -1
  34. package/dist/{setup-handler-44ASXYMS.js → setup-handler-ANXNXK6I.js} +2 -2
  35. package/dist/{stop-hook-N6TX4YQT.js → stop-hook-QZRUUX22.js} +4 -4
  36. package/dist/{tail-stream-G4WGTXDS.js → tail-stream-EK66YLNF.js} +103 -25
  37. package/dist/{user-prompt-submit-hook-DTXXFDSD.js → user-prompt-submit-hook-O7KIT3RR.js} +2 -2
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -555,6 +555,106 @@ literal URL in the body; de-fang or split it; this is **not** an auth failure),
555
555
  `approval_required`, `not_paired`, `not_enrolled`, `bad_request`,
556
556
  `unauthorized`, `send_unavailable`, `send_failed` (raw text preserved).
557
557
 
558
+ ## Claude Co-Work (paste-one-command wake)
559
+
560
+ **Claude Co-Work** is a cloud-sandbox harness with no MCP registration and no
561
+ restart available mid-session, so it can't use the `init`-wired MCP proxy above.
562
+ Instead, one line — generated from the Kojee dashboard and pasted into the
563
+ agent's first turn — enrolls the sandbox and gets it streaming Tandem wakes
564
+ immediately. This is a separate, parallel wake path from the MCP proxy; it does
565
+ not use `startProxy` at all.
566
+
567
+ ### The pasted command
568
+
569
+ ```bash
570
+ KOJEE_TOKEN=<token> npx -y kojee-mcp@<version> cowork --tandem <tandem_id> --url <broker>
571
+ ```
572
+
573
+ The dashboard fills in `<token>`, `<version>` (the currently published
574
+ `kojee-mcp` version), `<tandem_id>`, and `<broker>` when it generates this line
575
+ for you. Flags:
576
+
577
+ | Flag | Required | Description |
578
+ |---|---|---|
579
+ | `--tandem <id>` | yes, to launch | Tandem id to subscribe to. |
580
+ | `--url <url>` | yes, to launch | Broker base URL — the token does not encode it. |
581
+ | `--token <token>` | no (fallback) | Gateway token fallback. The canonical intake is the `KOJEE_TOKEN=` env prefix (keeps the secret off `/proc/<pid>/cmdline` and npm debug logs); `--token` costs a few seconds of argv exposure and is never forwarded to the worker process. |
582
+ | `--force` | no | Override the clobber guard — replace a different token/broker already planted in `~/.kojee/config.json`. Without it, `cowork` refuses to overwrite a different credential (protects against an accidental paste evicting another sandbox's DPoP key — see the warning below). |
583
+ | `--detach` | no | Host the worker as a detached background daemon (hidden `cowork-daemon` subcommand) instead of the default Monitor-hosted worker. |
584
+
585
+ Run once, in the foreground, `cowork` enrolls the token into the paired DPoP
586
+ slot, plants `~/.kojee/config.json` + `~/.kojee/keypair.json`, joins the
587
+ tandem, and runs a startup barrier read — so a bad or revoked token, or a
588
+ token that isn't a member of the tandem, fails loudly at paste time (nonzero
589
+ exit, no Monitor recipe printed) instead of silently starting deaf.
590
+
591
+ ### What the agent does with the printed output
592
+
593
+ On success `cowork` prints a recipe similar to:
594
+
595
+ ```
596
+ [kojee cowork] enrolled + subscribed to tandem_abc123 (seat cowork-9f2a1c7b4e05).
597
+ [kojee cowork] baseline cursor = 4821. NOTE: this command contains a secret token — don't re-share this transcript.
598
+
599
+ 1) Arm the wake watcher ONCE (persistent Monitor):
600
+ Monitor(command=`npx kojee-mcp@<version> cowork-stream --tandem tandem_abc123`, persistent=true, description="kojee Tandem events")
601
+ ...
602
+ ```
603
+
604
+ The agent should:
605
+
606
+ 1. **Arm the Monitor exactly once**, using the printed command — e.g.
607
+ `Monitor(command=\`npx kojee-mcp@<version> cowork-stream --tandem <tandem_id>\`, persistent=true, description="kojee Tandem events")`.
608
+ This spawns `cowork-stream`, which subscribes over SSE, writes the event
609
+ log, and fuses a tail of it onto its own stdout — each line is one Monitor
610
+ wake, carrying `msg=<id>` and `cursor=<n>`. If a watcher is already armed,
611
+ don't arm a second one.
612
+ 2. **Catch up once, right after arming** — `npx kojee-mcp@<version> messages <tandem_id> --since <baseline cursor>` — to cover the gap between paste and Monitor arm.
613
+ 3. **Reply via the `send` CLI, never MCP** (there are no MCP tools in this
614
+ session) — `npx kojee-mcp@<version> send <tandem_id> --body "..." --reply-to <msg>`.
615
+ One JSON envelope on stdout: `{ok:true, message_id, cursor}` (exit 0) or
616
+ `{ok:false, error, message}` (exit 1). Surface `content_blocked` /
617
+ `rate_limited` / `gateway_auth` distinctly — don't blind-retry.
618
+ 4. **Pull full, untruncated context on any gap** with the same `messages
619
+ --since <last cursor you saw>` command — e.g. after a cursor jump or a
620
+ `status=rotated` reset (event lines on the Monitor stream are truncated to
621
+ 200 characters).
622
+
623
+ Lines the worker prints that start with `[kojee]` (e.g. `[kojee] log=<path>`,
624
+ `[kojee] WARNING: ...`, `[kojee] WORKER EXITED ...`) are telemetry, not Tandem
625
+ messages — never reply to them.
626
+
627
+ ### Stopping it
628
+
629
+ ```bash
630
+ npx kojee-mcp@<version> cowork stop --tandem <tandem_id>
631
+ ```
632
+
633
+ Scans for live cowork workers on that tandem (omit `--tandem` to stop all live
634
+ cowork workers on the box), SIGTERMs each, and cleans up its discovery file.
635
+
636
+ **Merged (default) vs `--detach`:** by default the persistent Monitor *is* the
637
+ host for the worker (`cowork-stream`), so if the wake stream dies the harness
638
+ sees the Monitor's own process exit. `--detach` instead spawns a headless
639
+ `cowork-daemon` child in the background and has the agent's Monitor tail its
640
+ event-log file — use it only on a sandbox that won't keep a long-lived Monitor
641
+ child alive.
642
+
643
+ > **WARNING — mint a fresh token per sandbox.** Enrolling with `cowork` claims
644
+ > the token's single DPoP key slot: the Kojee backend keeps **exactly one**
645
+ > DPoP key per gateway token, and every enrollment **replaces** it. Paste the
646
+ > *same* token into a second sandbox and the first sandbox's credential is
647
+ > silently evicted — it goes offline. Mint a **fresh** cowork token per
648
+ > sandbox from the dashboard; never reuse one across sandboxes.
649
+ >
650
+ > The pasted command also **contains a secret token in plaintext**. It
651
+ > persists in the agent's transcript and shell history for as long as those
652
+ > are retained — don't re-share the transcript, and treat the token as
653
+ > compromised if it does leak out.
654
+
655
+ Keep the `@<version>` pin — seat coherence relies on a feature that only
656
+ exists in that version; do not drop it.
657
+
558
658
  ## Development
559
659
 
560
660
  Run tests:
@@ -73,6 +73,33 @@ function readSessionDiscoveryByKey(key) {
73
73
  return null;
74
74
  }
75
75
  }
76
+ function scanLiveCoworkWorkers(opts = {}) {
77
+ const dir = opts.dir ?? sessionDiscoveryDir();
78
+ const alive = opts.isAlive ?? isProcessAlive;
79
+ let names;
80
+ try {
81
+ names = fs.readdirSync(dir);
82
+ } catch {
83
+ return [];
84
+ }
85
+ const out = [];
86
+ for (const name of names) {
87
+ if (!name.startsWith("cc-") || !name.endsWith(".json")) continue;
88
+ const filePath = path.join(dir, name);
89
+ let entry = null;
90
+ try {
91
+ entry = JSON.parse(fs.readFileSync(filePath, "utf8"));
92
+ } catch {
93
+ continue;
94
+ }
95
+ if (!entry || entry.origin !== "cowork") continue;
96
+ const pid = entry.proxyPid ?? entry.pid;
97
+ if (typeof pid !== "number" || !alive(pid)) continue;
98
+ const key = name.slice("cc-".length, -".json".length);
99
+ out.push({ key, entry, path: filePath, pid });
100
+ }
101
+ return out;
102
+ }
76
103
  var DEFAULT_SWEEP_MIN_AGE_MS = 6e4;
77
104
  function isProcessAlive(pid) {
78
105
  try {
@@ -143,5 +170,6 @@ export {
143
170
  writeDiscoveryByKey,
144
171
  cleanupDiscoveryByKey,
145
172
  readSessionDiscoveryByKey,
173
+ scanLiveCoworkWorkers,
146
174
  sweepStaleDiscovery
147
175
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readSessionDiscoveryByKey
3
- } from "./chunk-67F67AQ6.js";
3
+ } from "./chunk-3N4YYIOJ.js";
4
4
  import {
5
5
  deriveDiscoveryKey,
6
6
  findClaudeAncestorPid
@@ -49,7 +49,7 @@ import {
49
49
  CODEX_LISTEN_CAP_MS,
50
50
  buildCondensedTandemRules,
51
51
  buildWebhookReceiverNote
52
- } from "./chunk-FJUAMJHU.js";
52
+ } from "./chunk-QLARIAUJ.js";
53
53
 
54
54
  // src/wizard/pair-slot.ts
55
55
  import fs from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildReplyRecipe
3
- } from "./chunk-FJUAMJHU.js";
3
+ } from "./chunk-QLARIAUJ.js";
4
4
 
5
5
  // src/adapters/claude-code.ts
6
6
  function computeSeverity(event) {
@@ -82,6 +82,13 @@ async function startEventStream(opts) {
82
82
  headersTimeout: UNDICI_DEFAULT_BODY_TIMEOUT_MS
83
83
  });
84
84
  const cursors = /* @__PURE__ */ new Map();
85
+ if (opts.initialCursors) {
86
+ for (const [tandemId, cursor] of Object.entries(opts.initialCursors)) {
87
+ if (typeof cursor === "number" && Number.isFinite(cursor)) {
88
+ cursors.set(tandemId, cursor);
89
+ }
90
+ }
91
+ }
85
92
  let currentController = new AbortController();
86
93
  let connectGeneration = 0;
87
94
  const state = {
@@ -273,10 +280,11 @@ async function consumeSse(body, opts, controller, state, onCursor) {
273
280
  console.error("[event-stream] event-log append failed:", err);
274
281
  }
275
282
  }
276
- if (opts.adapter.supportsChannels) {
283
+ const channelServer = opts.server;
284
+ if (opts.adapter.supportsChannels && channelServer) {
277
285
  try {
278
286
  const channel = opts.adapter.formatTandemEvent(parsed);
279
- await opts.server.notification({
287
+ await channelServer.notification({
280
288
  method: "notifications/claude/channel",
281
289
  params: channel
282
290
  });
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  readSessionDiscoveryByKey,
3
3
  sessionDiscoveryDir
4
- } from "./chunk-67F67AQ6.js";
4
+ } from "./chunk-3N4YYIOJ.js";
5
5
  import {
6
6
  secureFile
7
7
  } from "./chunk-U5HHHRXA.js";
@@ -0,0 +1,14 @@
1
+ // src/adapters/unknown.ts
2
+ var unknownAdapter = {
3
+ runtime: "unknown",
4
+ supportsChannels: false,
5
+ formatTandemEvent() {
6
+ throw new Error(
7
+ "unknownAdapter.formatTandemEvent() is unreachable \u2014 caller should gate on supportsChannels"
8
+ );
9
+ }
10
+ };
11
+
12
+ export {
13
+ unknownAdapter
14
+ };
@@ -0,0 +1,118 @@
1
+ import {
2
+ createPairedGateway
3
+ } from "./chunk-VGZ4M3FP.js";
4
+ import {
5
+ classifySendFailure,
6
+ ensureJoined,
7
+ sendFailure
8
+ } from "./chunk-WZP7HXXN.js";
9
+ import {
10
+ translateToolCallResult
11
+ } from "./chunk-PPTKGWFF.js";
12
+
13
+ // src/tandem/messages-cli.ts
14
+ import os from "os";
15
+ import path from "path";
16
+ function parseMessagesRequest(input) {
17
+ const tandemId = input.tandem_id;
18
+ if (typeof tandemId !== "string" || tandemId.length === 0) {
19
+ return sendFailure("bad_request", "tandem_id is required and must be a non-empty string");
20
+ }
21
+ const request = { tandem_id: tandemId };
22
+ if (input.since !== void 0) {
23
+ if (typeof input.since !== "string" || input.since.length === 0) {
24
+ return sendFailure("bad_request", "--since must be a non-empty cursor when present");
25
+ }
26
+ request.since = input.since;
27
+ }
28
+ if (input.limit !== void 0) {
29
+ const raw = String(input.limit);
30
+ if (!/^\d+$/.test(raw)) {
31
+ return sendFailure("bad_request", "--limit must be a positive integer");
32
+ }
33
+ const n = Number.parseInt(raw, 10);
34
+ if (!Number.isFinite(n) || n <= 0) {
35
+ return sendFailure("bad_request", "--limit must be a positive integer");
36
+ }
37
+ request.limit = n;
38
+ }
39
+ return { ok: true, request };
40
+ }
41
+ function deriveMaxCursor(messages) {
42
+ let max = null;
43
+ for (const m of messages) {
44
+ if (m !== null && typeof m === "object") {
45
+ const c = m["cursor"];
46
+ if (typeof c === "number" && (max === null || c > max)) max = c;
47
+ }
48
+ }
49
+ return max;
50
+ }
51
+ async function executeRead(gateway, request) {
52
+ const sinceArg = request.since !== void 0 ? /^\d+$/.test(request.since) ? Number(request.since) : request.since : void 0;
53
+ const args = {
54
+ tandem_id: request.tandem_id,
55
+ ...sinceArg !== void 0 ? { since: sinceArg } : {},
56
+ ...request.limit !== void 0 ? { limit: request.limit } : {}
57
+ };
58
+ let result;
59
+ try {
60
+ result = await gateway.sendRpc("tools/call", {
61
+ name: "tandem_messages",
62
+ arguments: args
63
+ });
64
+ } catch (err) {
65
+ return classifySendFailure(
66
+ `tandem_messages failed: ${err?.message ?? String(err)}`
67
+ );
68
+ }
69
+ const translated = translateToolCallResult(result);
70
+ const text = (translated.content ?? []).map((c) => typeof c?.text === "string" ? c.text : "").filter(Boolean).join("\n");
71
+ if (translated.isError) {
72
+ return classifySendFailure(text || "tandem_messages returned an error with no text");
73
+ }
74
+ let messages = [];
75
+ let cursor = null;
76
+ try {
77
+ const parsed = JSON.parse(text);
78
+ if (Array.isArray(parsed)) {
79
+ messages = parsed;
80
+ } else if (parsed !== null && typeof parsed === "object") {
81
+ const obj = parsed;
82
+ if (Array.isArray(obj["messages"])) messages = obj["messages"];
83
+ const rawCursor = obj["cursor"];
84
+ if (typeof rawCursor === "number") cursor = rawCursor;
85
+ }
86
+ if (cursor === null) cursor = deriveMaxCursor(messages);
87
+ } catch {
88
+ }
89
+ return { ok: true, tandem_id: request.tandem_id, messages, cursor, text };
90
+ }
91
+ async function runMessagesCli(args, deps = {}) {
92
+ const parsed = parseMessagesRequest({
93
+ tandem_id: args.tandemId,
94
+ ...args.since !== void 0 ? { since: args.since } : {},
95
+ ...args.limit !== void 0 ? { limit: args.limit } : {}
96
+ });
97
+ if (!parsed.ok) {
98
+ return { exitCode: 1, envelope: parsed };
99
+ }
100
+ const kojeeDir = args.kojeeDir ?? path.join(os.homedir(), ".kojee");
101
+ const createGateway = deps.createGateway ?? createPairedGateway;
102
+ const resolution = await createGateway(kojeeDir);
103
+ if (!resolution.ok) {
104
+ return { exitCode: 1, envelope: resolution };
105
+ }
106
+ const guard = await ensureJoined(resolution.gateway, parsed.request.tandem_id);
107
+ if (!guard.ok) {
108
+ return { exitCode: 1, envelope: guard.failure };
109
+ }
110
+ const envelope = await executeRead(resolution.gateway, parsed.request);
111
+ return { exitCode: envelope.ok ? 0 : 1, envelope };
112
+ }
113
+
114
+ export {
115
+ parseMessagesRequest,
116
+ executeRead,
117
+ runMessagesCli
118
+ };
@@ -6,7 +6,7 @@ import {
6
6
  buildCondensedTandemRules,
7
7
  buildMonitorSpawn,
8
8
  buildReplyRecipe
9
- } from "./chunk-FJUAMJHU.js";
9
+ } from "./chunk-QLARIAUJ.js";
10
10
  import {
11
11
  translateToolCallResult
12
12
  } from "./chunk-PPTKGWFF.js";
@@ -1,10 +1,18 @@
1
1
  // src/tandem/recipe.ts
2
2
  var SEND_BODY_PARAM = "body";
3
+ var KOJEE_PACKAGE = "kojee-mcp";
4
+ var MONITOR_DESCRIPTION = "kojee Tandem events";
5
+ function pinnedPackage(version) {
6
+ return `${KOJEE_PACKAGE}@${version}`;
7
+ }
8
+ function buildMonitorSpawnFor(command) {
9
+ return `Monitor(command=\`${command}\`, persistent=true, description="${MONITOR_DESCRIPTION}")`;
10
+ }
3
11
  function buildMonitorCommand(logPath) {
4
- return `npx kojee-mcp tail "${logPath}"`;
12
+ return `npx ${KOJEE_PACKAGE} tail "${logPath}"`;
5
13
  }
6
14
  function buildMonitorSpawn(logPath) {
7
- return `Monitor(command=\`${buildMonitorCommand(logPath)}\`, persistent=true, description="kojee Tandem events")`;
15
+ return buildMonitorSpawnFor(buildMonitorCommand(logPath));
8
16
  }
9
17
  function buildReplyRecipe(event) {
10
18
  if (event) {
@@ -21,6 +29,18 @@ function buildCondensedTandemRules() {
21
29
  function buildMonitorNudge(logPath) {
22
30
  return `[kojee] Tandem events are being logged but no Monitor is reading them \u2014 you may be missing wake notifications. Spawn the watcher once: ${buildMonitorSpawn(logPath)}. Then ${buildReplyRecipe()}.`;
23
31
  }
32
+ function buildCoworkMonitorSpawn(tandemId, version) {
33
+ return buildMonitorSpawnFor(
34
+ `npx ${pinnedPackage(version)} cowork-stream --tandem ${tandemId}`
35
+ );
36
+ }
37
+ function buildCliReplyRecipe(tandemId, version) {
38
+ return `npx ${pinnedPackage(version)} send ${tandemId} --${SEND_BODY_PARAM} "..." --reply-to <msg>`;
39
+ }
40
+ function buildCliCatchUpNote(tandemId, version, sinceCursor) {
41
+ const since = sinceCursor !== void 0 ? String(sinceCursor) : "<cursor>";
42
+ return `npx ${pinnedPackage(version)} messages ${tandemId} --since ${since}`;
43
+ }
24
44
  var WEBHOOK_BODY_SHAPE = "{ type, id, tandem_id, cursor, time, from{ member_id, principal, agent_id?, session_id?, displayname }, kind, content{ body, format? }, mentions?, reply_to?, severity? }";
25
45
  function buildWebhookReceiverNote(sig) {
26
46
  const header = sig?.header ?? "X-Kojee-Signature";
@@ -32,11 +52,15 @@ var CODEX_LISTEN_CAP_MS = 8e3;
32
52
  var CODEX_WAKE_BELL = "[kojee] Tandem events may be pending. Call tandem_pending now and drain each room it lists (tandem_messages(id, since=cursor)), then reply in the room with a normal message (kind=message \u2014 never status). If it returns none for you, ignore this.";
33
53
 
34
54
  export {
55
+ pinnedPackage,
35
56
  buildMonitorSpawn,
36
57
  buildReplyRecipe,
37
58
  buildCatchUpNote,
38
59
  buildCondensedTandemRules,
39
60
  buildMonitorNudge,
61
+ buildCoworkMonitorSpawn,
62
+ buildCliReplyRecipe,
63
+ buildCliCatchUpNote,
40
64
  buildWebhookReceiverNote,
41
65
  CODEX_LISTEN_CAP_MS,
42
66
  CODEX_WAKE_BELL
@@ -1,6 +1,9 @@
1
+ import {
2
+ unknownAdapter
3
+ } from "./chunk-JRCHS6CP.js";
1
4
  import {
2
5
  claudeCodeAdapter
3
- } from "./chunk-TCWIXG5C.js";
6
+ } from "./chunk-D5VPUCPY.js";
4
7
  import {
5
8
  GatewayClient,
6
9
  applyStableSessionId
@@ -11,7 +14,7 @@ import {
11
14
  import {
12
15
  createMcpServer,
13
16
  startMcpServer
14
- } from "./chunk-A4IOKD4Z.js";
17
+ } from "./chunk-P2R3KWYK.js";
15
18
  import {
16
19
  findClaudeAncestorPid
17
20
  } from "./chunk-XJEBJIQE.js";
@@ -181,17 +184,6 @@ var openclawAdapter = {
181
184
  }
182
185
  };
183
186
 
184
- // src/adapters/unknown.ts
185
- var unknownAdapter = {
186
- runtime: "unknown",
187
- supportsChannels: false,
188
- formatTandemEvent() {
189
- throw new Error(
190
- "unknownAdapter.formatTandemEvent() is unreachable \u2014 caller should gate on supportsChannels"
191
- );
192
- }
193
- };
194
-
195
187
  // src/index.ts
196
188
  var DEFAULT_KEYSTORE_PATH = path.join(os.homedir(), ".kojee", "keypair.json");
197
189
  function isDPoPEnrollmentError(err) {
@@ -295,7 +287,7 @@ async function startProxy(config) {
295
287
  }
296
288
  console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
297
289
  let server;
298
- const { selectDelivery } = await import("./registry-XJ67EO22.js");
290
+ const { selectDelivery } = await import("./registry-E2DW73RE.js");
299
291
  const delivery = selectDelivery(adapter.runtime, {
300
292
  supportsChannels: adapter.supportsChannels,
301
293
  // Per-window delivered mirror for the tandem_pending tool (codex only —
@@ -2,11 +2,11 @@ import {
2
2
  resolvePairedSlot
3
3
  } from "./chunk-EIH2LNF4.js";
4
4
  import {
5
- executeJoin,
5
+ ensureJoined,
6
6
  executeSend,
7
7
  parseSendRequest,
8
8
  sendFailure
9
- } from "./chunk-PKOBR7ZN.js";
9
+ } from "./chunk-WZP7HXXN.js";
10
10
  import {
11
11
  loadPairedConfig
12
12
  } from "./chunk-5SZHXYPK.js";
@@ -14,15 +14,10 @@ import {
14
14
  GatewayClient,
15
15
  applyStableSessionId
16
16
  } from "./chunk-247WFMCJ.js";
17
- import "./chunk-Z5LPNJQ6.js";
18
- import "./chunk-MIEI4PLB.js";
19
17
  import {
20
18
  deriveKeystorePath,
21
19
  loadKeystore
22
20
  } from "./chunk-6G6YYST6.js";
23
- import "./chunk-U5HHHRXA.js";
24
- import "./chunk-PPTKGWFF.js";
25
- import "./chunk-KNEJTD6G.js";
26
21
 
27
22
  // src/tandem/send-cli.ts
28
23
  import os from "os";
@@ -42,7 +37,8 @@ async function createPairedGateway(kojeeDir, stableDeps = {}, slot = {}) {
42
37
  `no paired config at ${configPath} \u2014 run \`kojee-mcp connect <code> --runtime <id>\` first`
43
38
  );
44
39
  }
45
- await applyStableSessionId(paired.token, stableDeps);
40
+ const seatDeps = paired.default_instance ? { instanceKey: paired.default_instance } : stableDeps;
41
+ await applyStableSessionId(paired.token, seatDeps);
46
42
  const brokerUrl = paired.broker_url.replace(/\/+$/, "");
47
43
  const keystorePath = resolution.scope === "paired" ? deriveKeystorePath(paired.token) : path.join(kojeeDir, "keypair.json");
48
44
  const keystore = await loadKeystore(keystorePath, brokerUrl).catch(() => null);
@@ -80,13 +76,14 @@ async function runSendCli(args, deps = {}) {
80
76
  if (!resolution.ok) {
81
77
  return { exitCode: 1, envelope: resolution };
82
78
  }
83
- const joined = await executeJoin(resolution.gateway, parsed.request.tandem_id);
84
- if (!joined.ok && (joined.error === "not_member" || joined.error === "gateway_auth")) {
85
- return { exitCode: 1, envelope: joined };
79
+ const guard = await ensureJoined(resolution.gateway, parsed.request.tandem_id);
80
+ if (!guard.ok) {
81
+ return { exitCode: 1, envelope: guard.failure };
86
82
  }
87
83
  const envelope = await executeSend(resolution.gateway, parsed.request);
88
84
  return { exitCode: envelope.ok ? 0 : 1, envelope };
89
85
  }
86
+
90
87
  export {
91
88
  createPairedGateway,
92
89
  runSendCli
@@ -129,6 +129,13 @@ async function executeJoin(gateway, tandemId) {
129
129
  }
130
130
  return { ok: true, tandem_id: tandemId, message_id: null, cursor: null, text };
131
131
  }
132
+ async function ensureJoined(gateway, tandemId) {
133
+ const joined = await executeJoin(gateway, tandemId);
134
+ if (!joined.ok && (joined.error === "not_member" || joined.error === "gateway_auth")) {
135
+ return { ok: false, failure: joined };
136
+ }
137
+ return { ok: true };
138
+ }
132
139
  var HTTP_STATUS_BY_CODE = {
133
140
  bad_request: 400,
134
141
  unauthorized: 401,
@@ -155,7 +162,9 @@ function httpStatusForEnvelope(envelope) {
155
162
  export {
156
163
  sendFailure,
157
164
  parseSendRequest,
165
+ classifySendFailure,
158
166
  executeSend,
159
167
  executeJoin,
168
+ ensureJoined,
160
169
  httpStatusForEnvelope
161
170
  };