kojee-mcp 0.7.0 → 0.7.2

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 (33) hide show
  1. package/dist/chunk-5DHIUN73.js +25 -0
  2. package/dist/{chunk-OB5T6P24.js → chunk-E35VWFZV.js} +1 -1
  3. package/dist/{chunk-GVWYW7MY.js → chunk-EDHG4375.js} +241 -58
  4. package/dist/{chunk-FSKGQ6GT.js → chunk-HI42GBQ3.js} +5 -0
  5. package/dist/chunk-IZN7IZPW.js +132 -0
  6. package/dist/{chunk-5H75AEZ2.js → chunk-R5GC2GRD.js} +62 -11
  7. package/dist/{chunk-OBIJ6WQP.js → chunk-RVLUZLXD.js} +63 -22
  8. package/dist/{chunk-QJFMU4QC.js → chunk-TMCNB4JH.js} +1 -1
  9. package/dist/{chunk-QEJUNP3X.js → chunk-XFGGMDZ4.js} +6 -5
  10. package/dist/chunk-XLRF5ATG.js +103 -0
  11. package/dist/{chunk-SGRVG4HW.js → chunk-ZUIYFRO5.js} +62 -30
  12. package/dist/cli.js +24 -14
  13. package/dist/codex-prompt-submit-hook-FOBPGZHJ.js +39 -0
  14. package/dist/codex-stop-hook-PNWYNQKM.js +136 -0
  15. package/dist/{connect-handler-HIRM624N.js → connect-handler-2JFIMQY6.js} +15 -7
  16. package/dist/{doctor-PLKLHTJM.js → doctor-HXMCO5PR.js} +2 -2
  17. package/dist/doctor-codex-ZSALZPH3.js +370 -0
  18. package/dist/{event-stream-KRYWEYWO.js → event-stream-WPN3EN7C.js} +5 -1
  19. package/dist/index.js +6 -5
  20. package/dist/{install-V7LSQCYZ.js → install-GGI6GU6C.js} +1 -1
  21. package/dist/lib.d.ts +40 -15
  22. package/dist/lib.js +7 -7
  23. package/dist/pending-state-6TVRR63P.js +134 -0
  24. package/dist/{registry-VRFHKOPP.js → registry-42Y45L6I.js} +114 -47
  25. package/dist/{server-YTHOMA4H.js → server-JWIH7OFA.js} +5 -2
  26. package/dist/{setup-handler-UHVKSX7E.js → setup-handler-GBPXDETR.js} +8 -7
  27. package/dist/{stop-hook-YI4KRKF2.js → stop-hook-PWTAP227.js} +2 -2
  28. package/dist/{tail-stream-NBGHHBS4.js → tail-stream-N43D53RC.js} +99 -19
  29. package/package.json +3 -2
  30. package/skills/using-tandems/SKILL.md +52 -0
  31. package/dist/chunk-WIU5WGDF.js +0 -10
  32. package/dist/codex-stop-hook-EOMQD3J4.js +0 -69
  33. package/dist/doctor-codex-HARAAH4Y.js +0 -154
@@ -1,8 +1,12 @@
1
+ import {
2
+ VERSION
3
+ } from "./chunk-5DHIUN73.js";
1
4
  import {
2
5
  buildCatchUpNote,
6
+ buildCondensedTandemRules,
3
7
  buildMonitorSpawn,
4
8
  buildReplyRecipe
5
- } from "./chunk-QEJUNP3X.js";
9
+ } from "./chunk-XFGGMDZ4.js";
6
10
  import {
7
11
  translateToolCallResult
8
12
  } from "./chunk-PPTKGWFF.js";
@@ -14,32 +18,9 @@ import {
14
18
  ListToolsRequestSchema,
15
19
  CallToolRequestSchema
16
20
  } from "@modelcontextprotocol/sdk/types.js";
17
-
18
- // src/version.ts
19
- import fs from "fs";
20
- import path from "path";
21
- import { fileURLToPath } from "url";
22
- var FALLBACK_VERSION = "0.0.0-unknown";
23
- function resolveVersion() {
24
- try {
25
- const here = path.dirname(fileURLToPath(import.meta.url));
26
- const parsed = JSON.parse(
27
- fs.readFileSync(path.join(here, "..", "package.json"), "utf8")
28
- );
29
- return typeof parsed?.version === "string" && parsed.version ? parsed.version : FALLBACK_VERSION;
30
- } catch (err) {
31
- process.stderr.write(
32
- `kojee-mcp: could not resolve version from package.json, falling back to ${FALLBACK_VERSION}: ${String(err)}
33
- `
34
- );
35
- return FALLBACK_VERSION;
36
- }
37
- }
38
- var VERSION = resolveVersion();
39
-
40
- // src/server.ts
41
- function buildNonChannelInstructions() {
42
- return "You are connected through the LOCAL kojee proxy. Do ALL Tandem operations (tandem_join/tandem_messages/tandem_send/tandem_set_wake_filter) through THESE tools \u2014 this is your ONLY wake-capable path: the proxy can only wake you for Tandem traffic that flows through it. If a REMOTE Kojee connector (e.g. a claude.ai-hosted 'Kojee' MCP) is ALSO available, do NOT use its Tandem tools \u2014 they cannot wake you.";
21
+ function buildNonChannelInstructions(runtime) {
22
+ const skillPointer = runtime === "codex" ? " Before working in rooms, read the using-tandems skill at ~/.agents/skills/using-tandems/SKILL.md (if present)." : "";
23
+ return "You are connected through the LOCAL kojee proxy. Do ALL Tandem operations (tandem_join/tandem_messages/tandem_send/tandem_set_wake_filter) through THESE tools \u2014 this is your ONLY wake-capable path: the proxy can only wake you for Tandem traffic that flows through it. If a REMOTE Kojee connector (e.g. a claude.ai-hosted 'Kojee' MCP) is ALSO available, do NOT use its Tandem tools \u2014 they cannot wake you.\n\n" + buildCondensedTandemRules() + skillPointer;
43
24
  }
44
25
  function buildChannelInstructions(_tandemMembershipCount, eventLogPath) {
45
26
  const localProxyRule = "You are connected through the LOCAL kojee proxy. Do ALL Tandem operations (join/read/send/set_wake_filter) through THESE tools \u2014 this is your ONLY wake-capable path: it writes the wake log the Monitor and stop-hook read. If a REMOTE Kojee connector (e.g. a claude.ai-hosted 'Kojee' MCP) is ALSO available, do NOT use its Tandem tools \u2014 they cannot wake you.\n\n";
@@ -53,11 +34,44 @@ function buildChannelInstructions(_tandemMembershipCount, eventLogPath) {
53
34
  `;
54
35
  const listenSection = "(3) If you want to BLOCK until any single reply lands (rather than receive a stream of events), call tandem_listen(tandem_id, since=cursor, timeout_ms=N) instead.";
55
36
  const advice = "\n\nPrefer (2) at session start \u2014 it's the default no-allowlist wake mechanism. (1) supplements it when channels are enabled; (3) is for one-shot blocking waits.";
56
- return localProxyRule + intro + monitorSection + listenSection + advice;
37
+ const skillPointer = "\n\nTeamwork guide: read the using-tandems skill at ~/.claude/skills/using-tandems/SKILL.md (if present) before working in rooms.";
38
+ return localProxyRule + intro + monitorSection + listenSection + advice + skillPointer;
57
39
  }
58
40
  function tandemIdArg(args) {
59
41
  return typeof args["tandem_id"] === "string" ? args["tandem_id"] : null;
60
42
  }
43
+ var DRAIN_TOOLS = /* @__PURE__ */ new Set(["tandem_messages", "tandem_ack"]);
44
+ function extractDrainCursor(name, args, content) {
45
+ const candidates = [];
46
+ const push = (v) => {
47
+ if (typeof v === "number" && Number.isFinite(v) && v >= 0) candidates.push(Math.floor(v));
48
+ };
49
+ if (name === "tandem_ack") push(args["cursor"]);
50
+ for (const item of content) {
51
+ if (item.type !== "text" || typeof item.text !== "string") continue;
52
+ let parsed;
53
+ try {
54
+ parsed = JSON.parse(item.text);
55
+ } catch {
56
+ continue;
57
+ }
58
+ const rows = Array.isArray(parsed) ? parsed : [];
59
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
60
+ const obj = parsed;
61
+ push(obj["cursor"]);
62
+ push(obj["latest_cursor"]);
63
+ push(obj["next_cursor"]);
64
+ for (const key of ["messages", "events"]) {
65
+ const arr = obj[key];
66
+ if (Array.isArray(arr)) rows.push(...arr);
67
+ }
68
+ }
69
+ for (const row of rows) {
70
+ if (row !== null && typeof row === "object") push(row["cursor"]);
71
+ }
72
+ }
73
+ return candidates.length > 0 ? Math.max(...candidates) : null;
74
+ }
61
75
  async function executeToolCall(registry, name, args, hooks) {
62
76
  const rawResult = await registry.callTool(name, args);
63
77
  const result = translateToolCallResult(rawResult);
@@ -68,6 +82,24 @@ async function executeToolCall(registry, name, args, hooks) {
68
82
  console.error("[mcp] onTandemJoin hook failed:", err?.message ?? String(err));
69
83
  }
70
84
  }
85
+ if (!result.isError && name === "tandem_leave") {
86
+ try {
87
+ hooks?.onTandemLeave?.(tandemIdArg(args));
88
+ } catch (err) {
89
+ console.error("[mcp] onTandemLeave hook failed:", err?.message ?? String(err));
90
+ }
91
+ }
92
+ if (!result.isError && DRAIN_TOOLS.has(name)) {
93
+ const tandemId = tandemIdArg(args);
94
+ const cursor = tandemId !== null ? extractDrainCursor(name, args, result.content) : null;
95
+ if (tandemId !== null && cursor !== null) {
96
+ try {
97
+ hooks?.onTandemDrain?.(tandemId, cursor);
98
+ } catch (err) {
99
+ console.error("[mcp] onTandemDrain hook failed:", err?.message ?? String(err));
100
+ }
101
+ }
102
+ }
71
103
  return result;
72
104
  }
73
105
  function createMcpServer(registry, adapter, tandemMembershipCount = -1, eventLogPath, hooks) {
@@ -79,7 +111,7 @@ function createMcpServer(registry, adapter, tandemMembershipCount = -1, eventLog
79
111
  { name: "kojee-mcp", version: VERSION },
80
112
  {
81
113
  capabilities,
82
- ...adapter.supportsChannels ? { instructions: buildChannelInstructions(tandemMembershipCount, eventLogPath ?? "") } : { instructions: buildNonChannelInstructions() }
114
+ ...adapter.supportsChannels ? { instructions: buildChannelInstructions(tandemMembershipCount, eventLogPath ?? "") } : { instructions: buildNonChannelInstructions(adapter.runtime) }
83
115
  }
84
116
  );
85
117
  server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -104,9 +136,9 @@ async function startMcpServer(server) {
104
136
  }
105
137
 
106
138
  export {
107
- VERSION,
108
139
  buildNonChannelInstructions,
109
140
  buildChannelInstructions,
141
+ extractDrainCursor,
110
142
  executeToolCall,
111
143
  createMcpServer,
112
144
  startMcpServer
package/dist/cli.js CHANGED
@@ -4,24 +4,25 @@ import {
4
4
  } from "./chunk-EIAUW6KO.js";
5
5
  import {
6
6
  startProxy
7
- } from "./chunk-5H75AEZ2.js";
8
- import "./chunk-OB5T6P24.js";
7
+ } from "./chunk-R5GC2GRD.js";
8
+ import "./chunk-E35VWFZV.js";
9
9
  import {
10
10
  pairedConfigPath
11
11
  } from "./chunk-5SZHXYPK.js";
12
12
  import "./chunk-247WFMCJ.js";
13
- import "./chunk-I67C2HYA.js";
14
13
  import "./chunk-Z5LPNJQ6.js";
14
+ import "./chunk-I67C2HYA.js";
15
15
  import "./chunk-MIEI4PLB.js";
16
16
  import {
17
17
  defaultPairedKeystorePath,
18
18
  deriveKeystorePath
19
19
  } from "./chunk-6G6YYST6.js";
20
20
  import "./chunk-U5HHHRXA.js";
21
+ import "./chunk-ZUIYFRO5.js";
21
22
  import {
22
23
  VERSION
23
- } from "./chunk-SGRVG4HW.js";
24
- import "./chunk-QEJUNP3X.js";
24
+ } from "./chunk-5DHIUN73.js";
25
+ import "./chunk-XFGGMDZ4.js";
25
26
  import "./chunk-PPTKGWFF.js";
26
27
  import "./chunk-XJEBJIQE.js";
27
28
  import "./chunk-KNEJTD6G.js";
@@ -47,7 +48,7 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
47
48
  program.command("connect <code>").description(
48
49
  "Connect this runtime to Kojee with a per-agent pair code from the dashboard (claude-code | codex | openclaw | hermes). claude-code/codex/openclaw write a per-runtime paired slot (~/.kojee/agents/<runtime>/config.json) and point the runtime launcher at it via --paired-config; hermes writes the global ~/.kojee/config.json (its daemon + `kojee-mcp send` read that). Idempotent."
49
50
  ).requiredOption("--runtime <id>", "Target runtime: claude-code | codex | openclaw | hermes").option("--url <url>", "Broker base URL (default: the canonical staging broker)").action(async (code, opts) => {
50
- const { runConnect } = await import("./connect-handler-HIRM624N.js");
51
+ const { runConnect } = await import("./connect-handler-2JFIMQY6.js");
51
52
  const result = await runConnect({
52
53
  code,
53
54
  runtime: opts.runtime,
@@ -57,9 +58,12 @@ program.command("connect <code>").description(
57
58
  console.error(result.output);
58
59
  process.exit(result.exitCode);
59
60
  });
60
- program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption("--type <type>", "Hook type: stop, user-prompt-submit, or codex-stop").action(async (opts) => {
61
+ program.command("hook").description("Run a kojee MCP hook script (called by Claude Code via ~/.claude/settings.json)").requiredOption(
62
+ "--type <type>",
63
+ "Hook type: stop, user-prompt-submit, codex-stop, or codex-prompt-submit"
64
+ ).action(async (opts) => {
61
65
  if (opts.type === "stop") {
62
- const { runStopHook } = await import("./stop-hook-YI4KRKF2.js");
66
+ const { runStopHook } = await import("./stop-hook-PWTAP227.js");
63
67
  await runStopHook();
64
68
  process.exit(0);
65
69
  } else if (opts.type === "user-prompt-submit") {
@@ -67,16 +71,22 @@ program.command("hook").description("Run a kojee MCP hook script (called by Clau
67
71
  await runUserPromptSubmitHook();
68
72
  process.exit(0);
69
73
  } else if (opts.type === "codex-stop") {
70
- const { runCodexStopHook } = await import("./codex-stop-hook-EOMQD3J4.js");
74
+ const { runCodexStopHook } = await import("./codex-stop-hook-PNWYNQKM.js");
71
75
  await runCodexStopHook();
72
76
  process.exit(0);
77
+ } else if (opts.type === "codex-prompt-submit") {
78
+ const { runCodexPromptSubmitHook } = await import("./codex-prompt-submit-hook-FOBPGZHJ.js");
79
+ await runCodexPromptSubmitHook();
80
+ process.exit(0);
73
81
  } else {
74
- console.error(`Unknown hook type: ${opts.type}. Expected 'stop', 'user-prompt-submit', or 'codex-stop'.`);
82
+ console.error(
83
+ `Unknown hook type: ${opts.type}. Expected 'stop', 'user-prompt-submit', 'codex-stop', or 'codex-prompt-submit'.`
84
+ );
75
85
  process.exit(1);
76
86
  }
77
87
  });
78
88
  program.command("install-hooks").description("Install kojee Stop + UserPromptSubmit hooks in ~/.claude/settings.json (idempotent)").option("--hooks-path <path>", "Override default ~/.claude/settings.json").option("--uninstall", "Remove kojee hook entries instead of installing them").action(async (opts) => {
79
- const { installHooks, uninstallHooks } = await import("./install-V7LSQCYZ.js");
89
+ const { installHooks, uninstallHooks } = await import("./install-GGI6GU6C.js");
80
90
  if (opts.uninstall) {
81
91
  const removed = uninstallHooks({ hooksPath: opts.hooksPath });
82
92
  console.error(removed ? "Removed kojee hook entries." : "No kojee hook entries found.");
@@ -105,7 +115,7 @@ program.command("send <tandem_id>").description(
105
115
  process.exit(exitCode);
106
116
  });
107
117
  program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
108
- const { runTail } = await import("./tail-stream-NBGHHBS4.js");
118
+ const { runTail } = await import("./tail-stream-N43D53RC.js");
109
119
  try {
110
120
  await runTail(filePath);
111
121
  } catch (err) {
@@ -114,7 +124,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
114
124
  }
115
125
  });
116
126
  program.command("doctor").description("Diagnose the kojee wake path (proxy, hook-server, SSE stream, event log, Monitor) and print the exact wake recipe").action(async () => {
117
- const { runDoctor } = await import("./doctor-PLKLHTJM.js");
127
+ const { runDoctor } = await import("./doctor-HXMCO5PR.js");
118
128
  const code = await runDoctor();
119
129
  process.exit(code);
120
130
  });
@@ -133,7 +143,7 @@ function addInstallOptions(cmd, runtimeHelp) {
133
143
  function makeInstallAction(verb) {
134
144
  return async (opts) => {
135
145
  const interactive = process.stdin.isTTY === true && opts.runtime === void 0;
136
- const { runSetup, resolvePairCode } = await import("./setup-handler-UHVKSX7E.js");
146
+ const { runSetup, resolvePairCode } = await import("./setup-handler-GBPXDETR.js");
137
147
  const pairCode = resolvePairCode(opts);
138
148
  const result = await runSetup({
139
149
  verb,
@@ -0,0 +1,39 @@
1
+ import {
2
+ readHookStdin
3
+ } from "./chunk-LSUB6QMP.js";
4
+ import {
5
+ listPending
6
+ } from "./chunk-IZN7IZPW.js";
7
+ import {
8
+ CODEX_WAKE_BELL
9
+ } from "./chunk-XFGGMDZ4.js";
10
+
11
+ // src/hooks/codex-prompt-submit-hook.ts
12
+ function buildCodexPromptSubmitOutput() {
13
+ let pending;
14
+ try {
15
+ pending = listPending();
16
+ } catch {
17
+ return "{}";
18
+ }
19
+ if (pending.length === 0) return "{}";
20
+ return JSON.stringify({
21
+ hookSpecificOutput: {
22
+ hookEventName: "UserPromptSubmit",
23
+ additionalContext: CODEX_WAKE_BELL
24
+ }
25
+ });
26
+ }
27
+ async function runCodexPromptSubmitHook() {
28
+ await readHookStdin();
29
+ let out = "{}";
30
+ try {
31
+ out = buildCodexPromptSubmitOutput();
32
+ } catch {
33
+ }
34
+ process.stdout.write(out);
35
+ }
36
+ export {
37
+ buildCodexPromptSubmitOutput,
38
+ runCodexPromptSubmitHook
39
+ };
@@ -0,0 +1,136 @@
1
+ import {
2
+ readHookStdin
3
+ } from "./chunk-LSUB6QMP.js";
4
+ import {
5
+ drainedRoomsPath,
6
+ listPending,
7
+ pendingRoomsPath
8
+ } from "./chunk-IZN7IZPW.js";
9
+ import {
10
+ CODEX_WAKE_BELL
11
+ } from "./chunk-XFGGMDZ4.js";
12
+
13
+ // src/hooks/codex-stop-hook.ts
14
+ import fs from "fs";
15
+ import path2 from "path";
16
+
17
+ // src/hooks/codex-pending-path.ts
18
+ import path from "path";
19
+ function codexNudgeAttemptsPath() {
20
+ return path.join(path.dirname(pendingRoomsPath()), "codex-nudge-attempts");
21
+ }
22
+ function codexWakeDropLogPath() {
23
+ return path.join(path.dirname(pendingRoomsPath()), "codex-pending-wake-drops.log");
24
+ }
25
+
26
+ // src/hooks/codex-stop-hook.ts
27
+ var CODEX_PEEK_MS = clampPeekMs(
28
+ Number.parseInt(process.env["KOJEE_CODEX_PEEK_MS"] ?? "150", 10)
29
+ );
30
+ function clampPeekMs(raw) {
31
+ if (!Number.isFinite(raw) || raw <= 0) return 150;
32
+ return Math.min(raw, 500);
33
+ }
34
+ var MAX_NUDGES_PER_ROOM = 3;
35
+ var ANON_KEY = "-";
36
+ function readNudgeAttempts() {
37
+ const out = /* @__PURE__ */ new Map();
38
+ let raw;
39
+ try {
40
+ raw = fs.readFileSync(codexNudgeAttemptsPath(), "utf8");
41
+ } catch {
42
+ return out;
43
+ }
44
+ for (const line of raw.split("\n")) {
45
+ const parts = line.trim().split(/\s+/);
46
+ if (parts.length !== 3) continue;
47
+ const cursor = Number.parseInt(parts[1], 10);
48
+ const count = Number.parseInt(parts[2], 10);
49
+ if (!Number.isFinite(cursor) || !Number.isFinite(count) || count < 0) continue;
50
+ out.set(parts[0], { cursor, count });
51
+ }
52
+ return out;
53
+ }
54
+ function writeNudgeAttempts(attempts) {
55
+ const filePath = codexNudgeAttemptsPath();
56
+ const tmp = `${filePath}.tmp-${process.pid}`;
57
+ try {
58
+ fs.mkdirSync(path2.dirname(filePath), { recursive: true });
59
+ const body = Array.from(attempts.entries()).map(([key, rec]) => `${key} ${rec.cursor} ${rec.count}`).join("\n");
60
+ fs.writeFileSync(tmp, body === "" ? "" : body + "\n", { mode: 384 });
61
+ fs.renameSync(tmp, filePath);
62
+ } catch {
63
+ try {
64
+ fs.unlinkSync(tmp);
65
+ } catch {
66
+ }
67
+ }
68
+ }
69
+ function logCappedRoom(key, cursor, attempts) {
70
+ try {
71
+ const logPath = codexWakeDropLogPath();
72
+ fs.mkdirSync(path2.dirname(logPath), { recursive: true });
73
+ fs.appendFileSync(
74
+ logPath,
75
+ `${(/* @__PURE__ */ new Date()).toISOString()} stopped nudging room ${key} (cursor=${cursor}) after ${attempts} un-drained block attempts \u2014 either Codex is silently rejecting the Stop-hook output (run \`kojee-mcp doctor\` and approve Codex's trust prompt) or the room belongs to another session; the room stays pending in the ledger until its proxy drains it
76
+ `,
77
+ { mode: 384 }
78
+ );
79
+ } catch {
80
+ }
81
+ }
82
+ function decideCodexStop(stopHookActive) {
83
+ if (stopHookActive) return "{}";
84
+ let pending;
85
+ try {
86
+ pending = listPending();
87
+ } catch {
88
+ return "{}";
89
+ }
90
+ if (pending.length === 0) return "{}";
91
+ const attempts = readNudgeAttempts();
92
+ const next = /* @__PURE__ */ new Map();
93
+ const nudgeable = [];
94
+ for (const room of pending) {
95
+ const key = room.tandemId ?? ANON_KEY;
96
+ const prior = attempts.get(key);
97
+ const count = prior !== void 0 && prior.cursor === room.cursor ? prior.count : 0;
98
+ if (count >= MAX_NUDGES_PER_ROOM) {
99
+ if (count === MAX_NUDGES_PER_ROOM) {
100
+ logCappedRoom(key, room.cursor, count);
101
+ next.set(key, { cursor: room.cursor, count: count + 1 });
102
+ } else {
103
+ next.set(key, { cursor: room.cursor, count });
104
+ }
105
+ continue;
106
+ }
107
+ next.set(key, { cursor: room.cursor, count: count + 1 });
108
+ nudgeable.push(room);
109
+ }
110
+ writeNudgeAttempts(next);
111
+ if (nudgeable.length === 0) return "{}";
112
+ return JSON.stringify({
113
+ decision: "block",
114
+ reason: CODEX_WAKE_BELL
115
+ });
116
+ }
117
+ async function runCodexStopHook() {
118
+ const { stopHookActive } = await readHookStdin();
119
+ let out = "{}";
120
+ try {
121
+ out = decideCodexStop(stopHookActive);
122
+ } catch {
123
+ }
124
+ process.stdout.write(out);
125
+ }
126
+ var CODEX_PEEK_BUDGET_MS = CODEX_PEEK_MS;
127
+ export {
128
+ CODEX_PEEK_BUDGET_MS,
129
+ MAX_NUDGES_PER_ROOM,
130
+ codexNudgeAttemptsPath,
131
+ codexWakeDropLogPath,
132
+ decideCodexStop,
133
+ drainedRoomsPath,
134
+ pendingRoomsPath,
135
+ runCodexStopHook
136
+ };
@@ -2,17 +2,17 @@ import {
2
2
  DEFAULT_BROKER_URL,
3
3
  reconcileConnectPairSlot,
4
4
  runWizard
5
- } from "./chunk-GVWYW7MY.js";
5
+ } from "./chunk-EDHG4375.js";
6
+ import "./chunk-RVLUZLXD.js";
6
7
  import "./chunk-E6WMFMM2.js";
7
- import "./chunk-OBIJ6WQP.js";
8
- import "./chunk-QJFMU4QC.js";
8
+ import "./chunk-77HWBSRH.js";
9
+ import "./chunk-TMCNB4JH.js";
9
10
  import "./chunk-D6JKFJ6A.js";
10
11
  import {
11
12
  CONNECT_RUNTIMES
12
13
  } from "./chunk-EIH2LNF4.js";
13
14
  import "./chunk-RDLF4NQC.js";
14
15
  import "./chunk-SQL56SEB.js";
15
- import "./chunk-77HWBSRH.js";
16
16
  import "./chunk-V5VZPYMZ.js";
17
17
  import {
18
18
  runPair
@@ -29,7 +29,8 @@ import {
29
29
  deriveKeystorePath
30
30
  } from "./chunk-6G6YYST6.js";
31
31
  import "./chunk-U5HHHRXA.js";
32
- import "./chunk-QEJUNP3X.js";
32
+ import "./chunk-5DHIUN73.js";
33
+ import "./chunk-XFGGMDZ4.js";
33
34
 
34
35
  // src/wizard/connect-handler.ts
35
36
  import os from "os";
@@ -47,6 +48,13 @@ function identityLabel(cfg) {
47
48
  if (cfg?.principal_id) return cfg.principal_id;
48
49
  return "this runtime";
49
50
  }
51
+ function identityChangeWarning(runtime, oldConfig, newConfig) {
52
+ if (oldConfig.agent_id && newConfig?.agent_id) {
53
+ if (oldConfig.agent_id === newConfig.agent_id) return null;
54
+ return `\u26A0 WARNING: this paired ${runtime} as a NEW agent identity (${oldConfig.agent_id} \u2192 ${newConfig.agent_id}) \u2014 room seats stay with the old agent. Re-join rooms, or regenerate a code for the SAME agent in the dashboard.`;
55
+ }
56
+ return `Note: identity continuity cannot be verified for this re-connect (the broker response carries no agent id). If this code was minted for a different agent, room seats stay with the old one \u2014 re-join rooms if wakes stop.`;
57
+ }
50
58
  async function runConnect(opts) {
51
59
  if (opts.runtime === "hermes") return runConnectHermes(opts);
52
60
  const messages = [];
@@ -88,8 +96,8 @@ async function runConnect(opts) {
88
96
  const newConfig = loadPairedConfig(configPath);
89
97
  const newLabel = identityLabel(newConfig);
90
98
  const summary = oldConfig ? `\u21BB re-connected ${runtime} as ${newLabel} (was ${identityLabel(oldConfig)})` : `\u2713 connected ${runtime} as ${newLabel} \u2192 ${url}`;
91
- const output = `${summary}
92
- Restart ${runtime} to pick up the connection.`;
99
+ const warning = runtime === "codex" && oldConfig ? identityChangeWarning(runtime, oldConfig, newConfig) : null;
100
+ const output = [summary, ...warning ? [warning] : [], `Restart ${runtime} to pick up the connection.`].join("\n");
93
101
  return { messages, output, exitCode: 0 };
94
102
  }
95
103
  async function runConnectHermes(opts) {
@@ -18,7 +18,7 @@ import "./chunk-U5HHHRXA.js";
18
18
  import {
19
19
  buildMonitorSpawn,
20
20
  buildReplyRecipe
21
- } from "./chunk-QEJUNP3X.js";
21
+ } from "./chunk-XFGGMDZ4.js";
22
22
  import {
23
23
  deriveDiscoveryKey,
24
24
  findClaudeAncestorPid
@@ -354,7 +354,7 @@ function formatDoctorReport(report) {
354
354
  async function runDoctor() {
355
355
  const { readRecordedRuntime } = await import("./runtime-record-OXRLTOLC.js");
356
356
  if (readRecordedRuntime() === "codex") {
357
- const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-HARAAH4Y.js");
357
+ const { collectCodexDoctorReport, formatCodexDoctorReport } = await import("./doctor-codex-ZSALZPH3.js");
358
358
  const report2 = collectCodexDoctorReport();
359
359
  console.error(formatCodexDoctorReport(report2));
360
360
  return report2.verdict === "broken" ? 1 : 0;