kojee-mcp 0.5.17 → 0.5.19
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/dist/{chunk-CO73VGWM.js → chunk-DURRQDYI.js} +1 -1
- package/dist/{chunk-IRD26KZG.js → chunk-PSVVRGJC.js} +12 -86
- package/dist/{chunk-34IRTWP6.js → chunk-U4MNICQI.js} +8 -53
- package/dist/{chunk-CM3EKMDD.js → chunk-XIDCYUTK.js} +5 -13
- package/dist/cli.js +9 -10
- package/dist/{doctor-SMFND2UW.js → doctor-U3MTB4GY.js} +4 -4
- package/dist/{event-log-VZD7NKYX.js → event-log-HOBO3ECB.js} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -4
- package/dist/lib.d.ts +22 -24
- package/dist/lib.js +1 -2
- package/dist/{registry-HPCRIWRF.js → registry-CGLE5X54.js} +5 -5
- package/dist/{send-cli-T6RPZZQ4.js → send-cli-IPADRDD2.js} +1 -2
- package/dist/{server-LBVEDIXP.js → server-LTFG4GMV.js} +1 -1
- package/dist/{stop-hook-CUVDKXP7.js → stop-hook-QZTFH5WV.js} +4 -4
- package/dist/{tail-stream-5DAVRQYK.js → tail-stream-CEJZGSIR.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-YKW54DKF.js +0 -126
- package/dist/ensure-join-5Y5IJ7HN.js +0 -8
- package/dist/{user-prompt-submit-hook-PMBUPKUV.js → user-prompt-submit-hook-FQ43NIHC.js} +3 -3
- /package/dist/{wizard-OI7VDU27.js → wizard-Y3ULSO2C.js} +0 -0
|
@@ -35,7 +35,7 @@ function startEventLog(opts) {
|
|
|
35
35
|
const statusMaxBytes = opts.statusMaxBytes ?? DEFAULT_STATUS_MAX_BYTES;
|
|
36
36
|
fs.mkdirSync(dir, { recursive: true });
|
|
37
37
|
let filePath = path.join(dir, `kojee-events-${opts.key}.log`);
|
|
38
|
-
if (
|
|
38
|
+
if (livePathOwnedBySibling(filePath)) {
|
|
39
39
|
filePath = path.join(dir, `kojee-events-${opts.key}-${process.pid}.log`);
|
|
40
40
|
}
|
|
41
41
|
const statusPath = statusLogPath(filePath);
|
|
@@ -4,29 +4,22 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
GatewayClient,
|
|
6
6
|
applyStableSessionId
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-U4MNICQI.js";
|
|
8
8
|
import {
|
|
9
9
|
AuthModule
|
|
10
10
|
} from "./chunk-JXMVZEQ7.js";
|
|
11
|
-
import {
|
|
12
|
-
secureDir,
|
|
13
|
-
secureFile
|
|
14
|
-
} from "./chunk-BLEGIR35.js";
|
|
15
11
|
import {
|
|
16
12
|
createMcpServer,
|
|
17
13
|
startMcpServer
|
|
18
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-XIDCYUTK.js";
|
|
19
15
|
import {
|
|
20
16
|
findClaudeAncestorPid
|
|
21
17
|
} from "./chunk-VHKPWUX7.js";
|
|
22
|
-
import {
|
|
23
|
-
parseTandemsConfig
|
|
24
|
-
} from "./chunk-YKW54DKF.js";
|
|
25
18
|
|
|
26
19
|
// src/index.ts
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
20
|
+
import fs from "fs";
|
|
21
|
+
import os from "os";
|
|
22
|
+
import path from "path";
|
|
30
23
|
|
|
31
24
|
// src/tool-registry.ts
|
|
32
25
|
var ToolRegistry = class {
|
|
@@ -150,57 +143,8 @@ var unknownAdapter = {
|
|
|
150
143
|
}
|
|
151
144
|
};
|
|
152
145
|
|
|
153
|
-
// src/tandem/room-memory.ts
|
|
154
|
-
import fs from "fs";
|
|
155
|
-
import os from "os";
|
|
156
|
-
import path from "path";
|
|
157
|
-
function defaultKojeeDir() {
|
|
158
|
-
return path.join(os.homedir(), ".kojee");
|
|
159
|
-
}
|
|
160
|
-
function seatedRoomsPath(key, dir = defaultKojeeDir()) {
|
|
161
|
-
return path.join(dir, `seated-rooms-cc-${key}.json`);
|
|
162
|
-
}
|
|
163
|
-
function readSeatedRooms(key, dir = defaultKojeeDir()) {
|
|
164
|
-
let raw;
|
|
165
|
-
try {
|
|
166
|
-
raw = fs.readFileSync(seatedRoomsPath(key, dir), "utf8");
|
|
167
|
-
} catch {
|
|
168
|
-
return [];
|
|
169
|
-
}
|
|
170
|
-
try {
|
|
171
|
-
const parsed = JSON.parse(raw);
|
|
172
|
-
return Array.isArray(parsed.rooms) ? parsed.rooms.filter((r) => typeof r === "string") : [];
|
|
173
|
-
} catch {
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function hasSeatedRoomsFile(key, dir = defaultKojeeDir()) {
|
|
178
|
-
return fs.existsSync(seatedRoomsPath(key, dir));
|
|
179
|
-
}
|
|
180
|
-
function seedSeatedRooms(key, rooms, dir = defaultKojeeDir()) {
|
|
181
|
-
writeSeatedRooms(key, [...new Set(rooms)], dir);
|
|
182
|
-
}
|
|
183
|
-
function writeSeatedRooms(key, rooms, dir) {
|
|
184
|
-
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
185
|
-
secureDir(dir);
|
|
186
|
-
const filePath = seatedRoomsPath(key, dir);
|
|
187
|
-
const body = { schema: 1, rooms, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
188
|
-
fs.writeFileSync(filePath, JSON.stringify(body, null, 2), { mode: 384 });
|
|
189
|
-
secureFile(filePath);
|
|
190
|
-
}
|
|
191
|
-
function addSeatedRoom(key, tandemId, dir = defaultKojeeDir()) {
|
|
192
|
-
const rooms = readSeatedRooms(key, dir);
|
|
193
|
-
if (rooms.includes(tandemId)) return;
|
|
194
|
-
writeSeatedRooms(key, [...rooms, tandemId], dir);
|
|
195
|
-
}
|
|
196
|
-
function removeSeatedRoom(key, tandemId, dir = defaultKojeeDir()) {
|
|
197
|
-
const rooms = readSeatedRooms(key, dir);
|
|
198
|
-
if (!rooms.includes(tandemId)) return;
|
|
199
|
-
writeSeatedRooms(key, rooms.filter((r) => r !== tandemId), dir);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
146
|
// src/index.ts
|
|
203
|
-
var DEFAULT_KEYSTORE_PATH =
|
|
147
|
+
var DEFAULT_KEYSTORE_PATH = path.join(os.homedir(), ".kojee", "keypair.json");
|
|
204
148
|
function isDPoPEnrollmentError(err) {
|
|
205
149
|
const msg = String(err?.message ?? err ?? "").toLowerCase();
|
|
206
150
|
if (msg.includes("invalid or expired") && msg.includes("token")) return false;
|
|
@@ -242,15 +186,9 @@ async function startProxy(config) {
|
|
|
242
186
|
console.error(
|
|
243
187
|
`[kojee-mcp] Ready \u2014 ${registry.toolCount} tools available from ${config.url}`
|
|
244
188
|
);
|
|
245
|
-
|
|
246
|
-
hasMemory: () => hasSeatedRoomsFile(instanceKey),
|
|
247
|
-
read: () => readSeatedRooms(instanceKey),
|
|
248
|
-
seed: (rooms) => seedSeatedRooms(instanceKey, rooms)
|
|
249
|
-
};
|
|
250
|
-
const recordRooms = parseTandemsConfig(process.env["KOJEE_TANDEMS"]).mode === "auto-local";
|
|
251
|
-
if (instanceKey.startsWith("wd-")) {
|
|
189
|
+
if (instanceKey.startsWith("mint-")) {
|
|
252
190
|
console.error(
|
|
253
|
-
"[kojee-mcp]
|
|
191
|
+
"[kojee-mcp] no stable session identity \u2014 minted a fresh per-launch seat (re-seats on restart). Set KOJEE_INSTANCE=<stable-per-session-value> for a restart-stable seat (required for daemons like Hermes)."
|
|
254
192
|
);
|
|
255
193
|
}
|
|
256
194
|
let activeStreamHandle = null;
|
|
@@ -267,12 +205,8 @@ async function startProxy(config) {
|
|
|
267
205
|
return true;
|
|
268
206
|
}
|
|
269
207
|
});
|
|
270
|
-
const onTandemJoin = (
|
|
208
|
+
const onTandemJoin = (_tandemId) => {
|
|
271
209
|
joinReconnect.requestReconnect();
|
|
272
|
-
if (recordRooms && tandemId) addSeatedRoom(instanceKey, tandemId);
|
|
273
|
-
};
|
|
274
|
-
const onTandemLeave = (tandemId) => {
|
|
275
|
-
if (recordRooms && tandemId) removeSeatedRoom(instanceKey, tandemId);
|
|
276
210
|
};
|
|
277
211
|
const teardownSteps = [];
|
|
278
212
|
let shuttingDown = false;
|
|
@@ -295,14 +229,6 @@ async function startProxy(config) {
|
|
|
295
229
|
console.error(`[kojee-mcp] shutting down (${reason}), exiting`);
|
|
296
230
|
process.exit(0);
|
|
297
231
|
}
|
|
298
|
-
const { ensureJoinTandems } = await import("./ensure-join-5Y5IJ7HN.js");
|
|
299
|
-
await ensureJoinTandems({
|
|
300
|
-
gateway,
|
|
301
|
-
env: process.env["KOJEE_TANDEMS"],
|
|
302
|
-
listTandems: () => listTandemIds(gateway),
|
|
303
|
-
roomMemory,
|
|
304
|
-
onJoined: () => joinReconnect.requestReconnect()
|
|
305
|
-
});
|
|
306
232
|
let tandemMembershipCount = -1;
|
|
307
233
|
try {
|
|
308
234
|
const bootIds = await listTandemIds(gateway);
|
|
@@ -312,7 +238,7 @@ async function startProxy(config) {
|
|
|
312
238
|
}
|
|
313
239
|
console.error(`[kojee-mcp] Tandem memberships: ${tandemMembershipCount === -1 ? "unknown" : tandemMembershipCount}`);
|
|
314
240
|
let server;
|
|
315
|
-
const { selectDelivery } = await import("./registry-
|
|
241
|
+
const { selectDelivery } = await import("./registry-CGLE5X54.js");
|
|
316
242
|
const delivery = selectDelivery(adapter.runtime, {
|
|
317
243
|
supportsChannels: adapter.supportsChannels
|
|
318
244
|
});
|
|
@@ -327,7 +253,7 @@ async function startProxy(config) {
|
|
|
327
253
|
ccPid,
|
|
328
254
|
tandemMembershipCount,
|
|
329
255
|
listTandemIds: () => listTandemIds(gateway),
|
|
330
|
-
toolCallHooks: { onTandemJoin
|
|
256
|
+
toolCallHooks: { onTandemJoin },
|
|
331
257
|
onStreamReady: (handle) => {
|
|
332
258
|
activeStreamHandle = handle;
|
|
333
259
|
joinReconnect.notifyReady();
|
|
@@ -387,7 +313,7 @@ async function enrollAndDiscover(config, keystorePath, isRetry = false) {
|
|
|
387
313
|
"[kojee-mcp] Auth failed, attempting recovery with fresh enrollment..."
|
|
388
314
|
);
|
|
389
315
|
try {
|
|
390
|
-
if (
|
|
316
|
+
if (fs.existsSync(keystorePath)) fs.unlinkSync(keystorePath);
|
|
391
317
|
} catch (unlinkErr) {
|
|
392
318
|
console.error("[kojee-mcp] Could not remove stale keystore:", unlinkErr);
|
|
393
319
|
}
|
|
@@ -9,9 +9,6 @@ import {
|
|
|
9
9
|
translateJsonRpcError,
|
|
10
10
|
translateNetworkError
|
|
11
11
|
} from "./chunk-2OLXXOT3.js";
|
|
12
|
-
import {
|
|
13
|
-
findClaudeAncestorPid
|
|
14
|
-
} from "./chunk-VHKPWUX7.js";
|
|
15
12
|
|
|
16
13
|
// src/gateway-client.ts
|
|
17
14
|
import crypto from "crypto";
|
|
@@ -190,70 +187,28 @@ var GatewayClient = class {
|
|
|
190
187
|
};
|
|
191
188
|
|
|
192
189
|
// src/runtime/cc-session-id.ts
|
|
193
|
-
import
|
|
194
|
-
|
|
195
|
-
import { createHash } from "crypto";
|
|
196
|
-
var CC_SESSION_ENV = "CLAUDE_CODE_SESSION_ID";
|
|
197
|
-
function extractCcSessionId(raw) {
|
|
198
|
-
if (raw.includes("\0")) {
|
|
199
|
-
for (const entry of raw.split("\0")) {
|
|
200
|
-
if (entry.startsWith(`${CC_SESSION_ENV}=`)) {
|
|
201
|
-
const v = entry.slice(CC_SESSION_ENV.length + 1);
|
|
202
|
-
return v.length > 0 ? v : null;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
const re = new RegExp(`(?:^|\\s)${CC_SESSION_ENV}=([^\\s]+)`, "g");
|
|
208
|
-
let last = null;
|
|
209
|
-
let m;
|
|
210
|
-
while ((m = re.exec(raw)) !== null) last = m[1];
|
|
211
|
-
return last;
|
|
212
|
-
}
|
|
213
|
-
function defaultReadProcessEnvRaw(pid, platform) {
|
|
214
|
-
try {
|
|
215
|
-
if (platform === "linux") {
|
|
216
|
-
return fs.readFileSync(`/proc/${pid}/environ`, "utf8");
|
|
217
|
-
}
|
|
218
|
-
if (platform === "darwin") {
|
|
219
|
-
return execFileSync("ps", ["eww", "-p", String(pid), "-o", "command="], {
|
|
220
|
-
encoding: "utf8",
|
|
221
|
-
timeout: 2e3
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
} catch {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
190
|
+
import { randomUUID } from "crypto";
|
|
191
|
+
var SESSION_ID_ENV_VARS = ["CLAUDE_CODE_SESSION_ID"];
|
|
229
192
|
function sanitizeKey(value) {
|
|
230
193
|
return value.replace(/[^A-Za-z0-9_-]/g, "");
|
|
231
194
|
}
|
|
232
195
|
function resolveInstanceKey(deps = {}) {
|
|
233
196
|
const env = deps.env ?? process.env;
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const reader = deps.readProcessEnvRaw ?? defaultReadProcessEnvRaw;
|
|
238
|
-
const raw = reader(ccPid, platform);
|
|
239
|
-
if (raw) {
|
|
240
|
-
const sid = sanitizeKey(extractCcSessionId(raw) ?? "");
|
|
241
|
-
if (sid) return sid;
|
|
242
|
-
}
|
|
197
|
+
for (const name of SESSION_ID_ENV_VARS) {
|
|
198
|
+
const sid = sanitizeKey((env[name] ?? "").trim());
|
|
199
|
+
if (sid) return sid;
|
|
243
200
|
}
|
|
244
201
|
const explicit = sanitizeKey((env.KOJEE_INSTANCE ?? "").trim());
|
|
245
202
|
if (explicit) return `inst-${explicit}`;
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
return `wd-${hash}`;
|
|
203
|
+
const mint = deps.randomUUID ?? randomUUID;
|
|
204
|
+
return `mint-${mint()}`;
|
|
249
205
|
}
|
|
250
206
|
|
|
251
207
|
// src/runtime/stable-session.ts
|
|
252
208
|
async function applyStableSessionId(token, deps = {}) {
|
|
253
209
|
let instanceKey = deps.instanceKey;
|
|
254
210
|
if (instanceKey === void 0) {
|
|
255
|
-
|
|
256
|
-
instanceKey = resolveInstanceKey({ ccPid });
|
|
211
|
+
instanceKey = resolveInstanceKey({ env: deps.env });
|
|
257
212
|
}
|
|
258
213
|
const sessionId = deriveStableSessionId(token, instanceKey);
|
|
259
214
|
setStableSessionId(sessionId);
|
|
@@ -57,19 +57,11 @@ function tandemIdArg(args) {
|
|
|
57
57
|
async function executeToolCall(registry, name, args, hooks) {
|
|
58
58
|
const rawResult = await registry.callTool(name, args);
|
|
59
59
|
const result = translateToolCallResult(rawResult);
|
|
60
|
-
if (!result.isError) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
console.error("[mcp] onTandemJoin hook failed:", err?.message ?? String(err));
|
|
66
|
-
}
|
|
67
|
-
} else if (name === "tandem_leave") {
|
|
68
|
-
try {
|
|
69
|
-
hooks?.onTandemLeave?.(tandemIdArg(args));
|
|
70
|
-
} catch (err) {
|
|
71
|
-
console.error("[mcp] onTandemLeave hook failed:", err?.message ?? String(err));
|
|
72
|
-
}
|
|
60
|
+
if (!result.isError && name === "tandem_join") {
|
|
61
|
+
try {
|
|
62
|
+
hooks?.onTandemJoin?.(tandemIdArg(args));
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error("[mcp] onTandemJoin hook failed:", err?.message ?? String(err));
|
|
73
65
|
}
|
|
74
66
|
}
|
|
75
67
|
return result;
|
package/dist/cli.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
} from "./chunk-OGHDTFAX.js";
|
|
5
5
|
import {
|
|
6
6
|
startProxy
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PSVVRGJC.js";
|
|
8
8
|
import "./chunk-XXFVWP6H.js";
|
|
9
9
|
import {
|
|
10
10
|
pairedConfigPath
|
|
11
11
|
} from "./chunk-YH27B6SW.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-U4MNICQI.js";
|
|
13
13
|
import "./chunk-JXMVZEQ7.js";
|
|
14
14
|
import "./chunk-NR4Y54OL.js";
|
|
15
15
|
import {
|
|
@@ -19,11 +19,10 @@ import {
|
|
|
19
19
|
import "./chunk-BLEGIR35.js";
|
|
20
20
|
import {
|
|
21
21
|
VERSION
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-XIDCYUTK.js";
|
|
23
23
|
import "./chunk-X672ZN7V.js";
|
|
24
24
|
import "./chunk-2OLXXOT3.js";
|
|
25
25
|
import "./chunk-VHKPWUX7.js";
|
|
26
|
-
import "./chunk-YKW54DKF.js";
|
|
27
26
|
|
|
28
27
|
// src/cli.ts
|
|
29
28
|
import { Command } from "commander";
|
|
@@ -45,11 +44,11 @@ program.command("pair <code>").description("Pair this machine against Kojee usin
|
|
|
45
44
|
});
|
|
46
45
|
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) => {
|
|
47
46
|
if (opts.type === "stop") {
|
|
48
|
-
const { runStopHook } = await import("./stop-hook-
|
|
47
|
+
const { runStopHook } = await import("./stop-hook-QZTFH5WV.js");
|
|
49
48
|
await runStopHook();
|
|
50
49
|
process.exit(0);
|
|
51
50
|
} else if (opts.type === "user-prompt-submit") {
|
|
52
|
-
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-
|
|
51
|
+
const { runUserPromptSubmitHook } = await import("./user-prompt-submit-hook-FQ43NIHC.js");
|
|
53
52
|
await runUserPromptSubmitHook();
|
|
54
53
|
process.exit(0);
|
|
55
54
|
} else if (opts.type === "codex-stop") {
|
|
@@ -79,7 +78,7 @@ Restart Claude Code for hooks to take effect.`
|
|
|
79
78
|
program.command("send <tandem_id>").description(
|
|
80
79
|
"Send a Tandem message using this machine's paired credentials (~/.kojee). Prints one JSON envelope to stdout: {ok, message_id, cursor, text} on success, {ok:false, error:<typed code>, message} on failure (exit 1)."
|
|
81
80
|
).requiredOption("--body <text>", "Message body (required)").option("--reply-to <message_id>", "Message id this send replies to").option("--kind <kind>", "Message kind: message | status (default: backend default)").action(async (tandemId, opts) => {
|
|
82
|
-
const { runSendCli } = await import("./send-cli-
|
|
81
|
+
const { runSendCli } = await import("./send-cli-IPADRDD2.js");
|
|
83
82
|
const { exitCode, envelope } = await runSendCli({
|
|
84
83
|
tandemId,
|
|
85
84
|
body: opts.body,
|
|
@@ -90,7 +89,7 @@ program.command("send <tandem_id>").description(
|
|
|
90
89
|
process.exit(exitCode);
|
|
91
90
|
});
|
|
92
91
|
program.command("tail <path>").description("Stream a file's contents and follow appends (portable replacement for `tail -F`)").action(async (filePath) => {
|
|
93
|
-
const { runTail } = await import("./tail-stream-
|
|
92
|
+
const { runTail } = await import("./tail-stream-CEJZGSIR.js");
|
|
94
93
|
try {
|
|
95
94
|
await runTail(filePath);
|
|
96
95
|
} catch (err) {
|
|
@@ -99,7 +98,7 @@ program.command("tail <path>").description("Stream a file's contents and follow
|
|
|
99
98
|
}
|
|
100
99
|
});
|
|
101
100
|
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 () => {
|
|
102
|
-
const { runDoctor } = await import("./doctor-
|
|
101
|
+
const { runDoctor } = await import("./doctor-U3MTB4GY.js");
|
|
103
102
|
const code = await runDoctor();
|
|
104
103
|
process.exit(code);
|
|
105
104
|
});
|
|
@@ -140,7 +139,7 @@ program.command("init").description(
|
|
|
140
139
|
console.error("Not paired. Run `kojee-mcp pair <code> --url <broker>` first, then re-run `init` \u2014 or pass --token/--pair-code, or run `init` in a terminal for the guided wizard.");
|
|
141
140
|
process.exit(1);
|
|
142
141
|
}
|
|
143
|
-
const { runWizard } = await import("./wizard-
|
|
142
|
+
const { runWizard } = await import("./wizard-Y3ULSO2C.js");
|
|
144
143
|
const result = await runWizard({
|
|
145
144
|
...opts.runtime !== void 0 ? { runtime: opts.runtime } : {},
|
|
146
145
|
...opts.uninstall ? { uninstall: true } : {},
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import "./chunk-XLKGPGZT.js";
|
|
2
|
-
import {
|
|
3
|
-
loadControlToken
|
|
4
|
-
} from "./chunk-GI2CKKBL.js";
|
|
5
2
|
import {
|
|
6
3
|
monitorHeartbeatPath,
|
|
7
4
|
statusLogPath
|
|
8
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DURRQDYI.js";
|
|
9
6
|
import {
|
|
10
7
|
discoveryPathForKey,
|
|
11
8
|
readSessionDiscoveryByKey,
|
|
12
9
|
sessionDiscoveryDir
|
|
13
10
|
} from "./chunk-DO42NPNR.js";
|
|
11
|
+
import {
|
|
12
|
+
loadControlToken
|
|
13
|
+
} from "./chunk-GI2CKKBL.js";
|
|
14
14
|
import {
|
|
15
15
|
loadPairedConfig
|
|
16
16
|
} from "./chunk-YH27B6SW.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -5,11 +5,11 @@ import 'jose';
|
|
|
5
5
|
* List the tandem_ids where THIS AGENT holds an active seat, via a
|
|
6
6
|
* `tandem_list` tool call. The backend's tandem_list is PRINCIPAL-scoped:
|
|
7
7
|
* rows include rooms where only SIBLING agents of the principal sit
|
|
8
|
-
* (`my_membership: {is_member:false, principal_is_member:true}`).
|
|
9
|
-
* the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* (`my_membership: {is_member:false, principal_is_member:true}`). Consumers are
|
|
9
|
+
* the resubscribe-on-connect touch set and the boot membership-count probe (NOT
|
|
10
|
+
* a join feed — auto-join was removed), so rows are filtered to
|
|
11
|
+
* `my_membership.is_member === true` — FAIL CLOSED: a row missing the flag is
|
|
12
|
+
* excluded, so we only ever touch rooms this agent actually sits in.
|
|
13
13
|
* Returns the id array, or null when the list could not be determined (tool
|
|
14
14
|
* error or unparseable result) — null is the "unknown" signal callers map to a
|
|
15
15
|
* -1 membership count. MINOR 6: called fresh on every reconnect so the touch
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
listTandemIds,
|
|
3
3
|
startProxy
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PSVVRGJC.js";
|
|
5
5
|
import "./chunk-XXFVWP6H.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-U4MNICQI.js";
|
|
7
7
|
import "./chunk-JXMVZEQ7.js";
|
|
8
8
|
import "./chunk-NR4Y54OL.js";
|
|
9
9
|
import "./chunk-CH32ELFX.js";
|
|
10
10
|
import "./chunk-BLEGIR35.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-XIDCYUTK.js";
|
|
12
12
|
import "./chunk-X672ZN7V.js";
|
|
13
13
|
import "./chunk-2OLXXOT3.js";
|
|
14
14
|
import "./chunk-VHKPWUX7.js";
|
|
15
|
-
import "./chunk-YKW54DKF.js";
|
|
16
15
|
export {
|
|
17
16
|
listTandemIds,
|
|
18
17
|
startProxy
|
package/dist/lib.d.ts
CHANGED
|
@@ -440,13 +440,16 @@ declare function resubscribeMemberships(opts: ResubscribeOptions): Promise<numbe
|
|
|
440
440
|
|
|
441
441
|
interface StableSessionDeps {
|
|
442
442
|
/**
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
443
|
+
* DEPRECATED / IGNORED. The resolver no longer reads process ancestry — it
|
|
444
|
+
* reads the runtime's session id from the proxy's OWN env (see
|
|
445
|
+
* {@link resolveInstanceKey}). Accepted only so callers that still pass
|
|
446
|
+
* `{ ccPid }` keep compiling; it has no effect on resolution.
|
|
446
447
|
*/
|
|
447
448
|
ccPid?: number | null;
|
|
449
|
+
/** Environment for {@link resolveInstanceKey} (defaults to `process.env`). Tests pin it. */
|
|
450
|
+
env?: NodeJS.ProcessEnv;
|
|
448
451
|
/**
|
|
449
|
-
* An explicit instanceKey — bypasses
|
|
452
|
+
* An explicit instanceKey — bypasses {@link resolveInstanceKey} entirely.
|
|
450
453
|
* Used where the window identity is known out-of-band (an embedder's account
|
|
451
454
|
* key, tests).
|
|
452
455
|
*/
|
|
@@ -459,10 +462,10 @@ interface StableSessionDeps {
|
|
|
459
462
|
* same id → the backend converges on ONE seat per (credential, window) across
|
|
460
463
|
* restarts (no phantom seats; reaper retired).
|
|
461
464
|
*
|
|
462
|
-
* instanceKey precedence: an explicit `deps.instanceKey` wins; otherwise
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
465
|
+
* instanceKey precedence: an explicit `deps.instanceKey` wins; otherwise
|
|
466
|
+
* {@link resolveInstanceKey} resolves it from the proxy's OWN env → the runtime
|
|
467
|
+
* session id (e.g. CLAUDE_CODE_SESSION_ID), else KOJEE_INSTANCE, else a minted
|
|
468
|
+
* per-launch id. (No process-ancestry read, no directory hash — both removed.)
|
|
466
469
|
*
|
|
467
470
|
* Returns the resolved instanceKey + the installed sessionId so callers that
|
|
468
471
|
* also need the key (room-memory, discovery) don't resolve it twice.
|
|
@@ -519,19 +522,13 @@ interface ToolCallHooks {
|
|
|
519
522
|
* Fired after a SUCCESSFUL tandem_join performed through this proxy by its
|
|
520
523
|
* agent, with the joined tandem id (null if it couldn't be read from args).
|
|
521
524
|
* The proxy wires this to the debounced stream-reconnect scheduler so the
|
|
522
|
-
* event-stream's membership snapshot picks up the just-acquired seat
|
|
523
|
-
* a daemon restart
|
|
524
|
-
*
|
|
525
|
-
*
|
|
525
|
+
* event-stream's membership snapshot picks up the just-acquired EXPLICIT seat
|
|
526
|
+
* without a daemon restart. A throwing hook never breaks the tool reply.
|
|
527
|
+
* (There is no leave hook: auto-join removal deleted the room-memory it fed,
|
|
528
|
+
* and a leave needs no proxy action — the stream stops hearing the room on the
|
|
529
|
+
* next reconnect.)
|
|
526
530
|
*/
|
|
527
531
|
onTandemJoin?: (tandemId: string | null) => void;
|
|
528
|
-
/**
|
|
529
|
-
* Fired after a SUCCESSFUL tandem_leave through this proxy, with the left
|
|
530
|
-
* tandem id (#28). The proxy drops the room from this instance's local
|
|
531
|
-
* room-memory so a restart doesn't rejoin a room the agent deliberately
|
|
532
|
-
* left. A throwing hook never breaks the tool reply.
|
|
533
|
-
*/
|
|
534
|
-
onTandemLeave?: (tandemId: string | null) => void;
|
|
535
532
|
}
|
|
536
533
|
|
|
537
534
|
/**
|
|
@@ -567,13 +564,14 @@ interface ToolCallHooks {
|
|
|
567
564
|
/**
|
|
568
565
|
* Everything a {@link WakeDelivery.start} needs from the proxy to build its
|
|
569
566
|
* sinks/surfaces and wire the SSE stream. Assembled once in `startProxy` after
|
|
570
|
-
* auth
|
|
567
|
+
* auth, before the stream connects.
|
|
571
568
|
*/
|
|
572
569
|
interface WakeDeliveryContext {
|
|
573
570
|
/**
|
|
574
|
-
* This instance's stable key (#28): the
|
|
575
|
-
* KOJEE_INSTANCE
|
|
576
|
-
* event-log filename and other per-session
|
|
571
|
+
* This instance's stable key (#28): the runtime's own-env session id, else
|
|
572
|
+
* `inst-<KOJEE_INSTANCE>`, else a minted `mint-<uuid>` (see `resolveInstanceKey`).
|
|
573
|
+
* Used to derive the per-runtime event-log filename and other per-session
|
|
574
|
+
* artifacts.
|
|
577
575
|
*/
|
|
578
576
|
instanceKey: string;
|
|
579
577
|
/** The detected runtime label (drives capability + which delivery is built). */
|
|
@@ -594,7 +592,7 @@ interface WakeDeliveryContext {
|
|
|
594
592
|
tandemMembershipCount: number;
|
|
595
593
|
/** tandem_id lister for the resubscribe-on-connect touch. */
|
|
596
594
|
listTandemIds: () => Promise<string[] | null>;
|
|
597
|
-
/** join
|
|
595
|
+
/** join side-channel hook (stream reconnect on an explicit join). */
|
|
598
596
|
toolCallHooks: ToolCallHooks;
|
|
599
597
|
/** Called once the stream handle is armed, to flush a boot-race reconnect. */
|
|
600
598
|
onStreamReady?: (handle: StreamLike) => void;
|
package/dist/lib.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
GatewayClient,
|
|
15
15
|
applyStableSessionId
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-U4MNICQI.js";
|
|
17
17
|
import {
|
|
18
18
|
AuthModule
|
|
19
19
|
} from "./chunk-JXMVZEQ7.js";
|
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
} from "./chunk-CH32ELFX.js";
|
|
31
31
|
import "./chunk-BLEGIR35.js";
|
|
32
32
|
import "./chunk-2OLXXOT3.js";
|
|
33
|
-
import "./chunk-VHKPWUX7.js";
|
|
34
33
|
export {
|
|
35
34
|
AuthModule,
|
|
36
35
|
GatewayClient,
|
|
@@ -4,7 +4,7 @@ import "./chunk-LSUB6QMP.js";
|
|
|
4
4
|
import {
|
|
5
5
|
claudeCodeAdapter
|
|
6
6
|
} from "./chunk-XXFVWP6H.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-XIDCYUTK.js";
|
|
8
8
|
import "./chunk-X672ZN7V.js";
|
|
9
9
|
import "./chunk-2OLXXOT3.js";
|
|
10
10
|
|
|
@@ -97,12 +97,12 @@ function createClaudeCodeDelivery() {
|
|
|
97
97
|
cleanupDiscoveryByKey,
|
|
98
98
|
sweepStaleDiscovery
|
|
99
99
|
} = await import("./session-discovery-FNMJGFPM.js");
|
|
100
|
-
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-
|
|
100
|
+
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-HOBO3ECB.js");
|
|
101
101
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
102
102
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
103
103
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
104
104
|
const { startEventStream } = await import("./event-stream-XX5EZ6HN.js");
|
|
105
|
-
const { createMcpServer } = await import("./server-
|
|
105
|
+
const { createMcpServer } = await import("./server-LTFG4GMV.js");
|
|
106
106
|
const { deriveDiscoveryKey } = await import("./ancestry-ONFBQEP5.js");
|
|
107
107
|
sweepStaleDiscovery();
|
|
108
108
|
sweepStaleEventLogs();
|
|
@@ -274,12 +274,12 @@ function createWebhookDelivery(name) {
|
|
|
274
274
|
return {
|
|
275
275
|
name,
|
|
276
276
|
async start(ctx) {
|
|
277
|
-
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-
|
|
277
|
+
const { startEventLog, sweepStaleEventLogs } = await import("./event-log-HOBO3ECB.js");
|
|
278
278
|
const { resolveWebhookConfig } = await import("./webhook-config-O4WMQ532.js");
|
|
279
279
|
const { createWebhookSink } = await import("./webhook-sink-N6AUTFL3.js");
|
|
280
280
|
const { resubscribeMemberships } = await import("./resubscribe-G5OGDZJD.js");
|
|
281
281
|
const { startEventStream } = await import("./event-stream-XX5EZ6HN.js");
|
|
282
|
-
const { createMcpServer } = await import("./server-
|
|
282
|
+
const { createMcpServer } = await import("./server-LTFG4GMV.js");
|
|
283
283
|
sweepStaleEventLogs();
|
|
284
284
|
eventLog = startEventLog({
|
|
285
285
|
key: ctx.instanceKey,
|
|
@@ -10,14 +10,13 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
GatewayClient,
|
|
12
12
|
applyStableSessionId
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-U4MNICQI.js";
|
|
14
14
|
import "./chunk-NR4Y54OL.js";
|
|
15
15
|
import {
|
|
16
16
|
loadKeystore
|
|
17
17
|
} from "./chunk-CH32ELFX.js";
|
|
18
18
|
import "./chunk-BLEGIR35.js";
|
|
19
19
|
import "./chunk-2OLXXOT3.js";
|
|
20
|
-
import "./chunk-VHKPWUX7.js";
|
|
21
20
|
|
|
22
21
|
// src/tandem/send-cli.ts
|
|
23
22
|
import os from "os";
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import "./chunk-XLKGPGZT.js";
|
|
2
|
-
import {
|
|
3
|
-
controlTokenAuthHeaders
|
|
4
|
-
} from "./chunk-GI2CKKBL.js";
|
|
5
2
|
import {
|
|
6
3
|
formatChannelEvents
|
|
7
4
|
} from "./chunk-PHXO5P25.js";
|
|
@@ -11,10 +8,13 @@ import {
|
|
|
11
8
|
import {
|
|
12
9
|
monitorHeartbeatPath,
|
|
13
10
|
nudgeSentinelPath
|
|
14
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DURRQDYI.js";
|
|
15
12
|
import {
|
|
16
13
|
readSessionDiscoveryByKey
|
|
17
14
|
} from "./chunk-DO42NPNR.js";
|
|
15
|
+
import {
|
|
16
|
+
controlTokenAuthHeaders
|
|
17
|
+
} from "./chunk-GI2CKKBL.js";
|
|
18
18
|
import "./chunk-BLEGIR35.js";
|
|
19
19
|
import {
|
|
20
20
|
buildMonitorNudge
|
package/package.json
CHANGED
package/dist/chunk-YKW54DKF.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
// src/tandem/ensure-join.ts
|
|
2
|
-
var OBJECT_ID_RE = /^[0-9a-f]{24}$/i;
|
|
3
|
-
var DEFAULT_PER_CALL_TIMEOUT_MS = 1e4;
|
|
4
|
-
function parseTandemsConfig(raw) {
|
|
5
|
-
const trimmed = (raw ?? "").trim();
|
|
6
|
-
if (trimmed.length === 0) return { mode: "auto-local", ids: [], invalid: [] };
|
|
7
|
-
if (trimmed.toLowerCase() === "none") return { mode: "disabled", ids: [], invalid: [] };
|
|
8
|
-
if (trimmed.toLowerCase() === "auto-agent") return { mode: "auto-agent", ids: [], invalid: [] };
|
|
9
|
-
const ids = [];
|
|
10
|
-
const invalid = [];
|
|
11
|
-
for (const entry of trimmed.split(/[\s,]+/)) {
|
|
12
|
-
if (entry.length === 0) continue;
|
|
13
|
-
(OBJECT_ID_RE.test(entry) ? ids : invalid).push(entry);
|
|
14
|
-
}
|
|
15
|
-
return { mode: "explicit", ids, invalid };
|
|
16
|
-
}
|
|
17
|
-
async function ensureJoinTandems(opts) {
|
|
18
|
-
const log = opts.log ?? ((line) => console.error(line));
|
|
19
|
-
const perCallTimeoutMs = opts.perCallTimeoutMs ?? DEFAULT_PER_CALL_TIMEOUT_MS;
|
|
20
|
-
const config = parseTandemsConfig(opts.env);
|
|
21
|
-
const result = { mode: config.mode, joined: [], already: [], failed: [] };
|
|
22
|
-
if (config.mode === "disabled") {
|
|
23
|
-
log("[ensure-join] disabled (KOJEE_TANDEMS=none)");
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
for (const bad of config.invalid) {
|
|
27
|
-
log(`[ensure-join] skipping invalid tandem id "${bad}" (not a 24-hex ObjectId)`);
|
|
28
|
-
}
|
|
29
|
-
const listAgentTandems = async () => {
|
|
30
|
-
try {
|
|
31
|
-
return opts.listTandems ? await opts.listTandems() : null;
|
|
32
|
-
} catch (err) {
|
|
33
|
-
log(`[ensure-join] tandem_list threw: ${err.message}`);
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const warnListFailed = () => {
|
|
38
|
-
log(
|
|
39
|
-
"[ensure-join] tandem_list failed \u2014 cannot re-seat this session (set KOJEE_TANDEMS=<ids> to pin, or KOJEE_TANDEMS=none to silence)"
|
|
40
|
-
);
|
|
41
|
-
};
|
|
42
|
-
let ids;
|
|
43
|
-
if (config.mode === "explicit") {
|
|
44
|
-
ids = config.ids;
|
|
45
|
-
log(`[ensure-join] mode=explicit n=${ids.length} (KOJEE_TANDEMS)`);
|
|
46
|
-
} else if (config.mode === "auto-agent") {
|
|
47
|
-
const listed = await listAgentTandems();
|
|
48
|
-
if (listed === null) {
|
|
49
|
-
warnListFailed();
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
ids = listed;
|
|
53
|
-
log(`[ensure-join] mode=auto-agent n=${ids.length} (legacy \u2014 re-seating every room this agent holds a seat)`);
|
|
54
|
-
} else {
|
|
55
|
-
const mem = opts.roomMemory;
|
|
56
|
-
if (mem && mem.hasMemory()) {
|
|
57
|
-
ids = mem.read().filter((id) => OBJECT_ID_RE.test(id));
|
|
58
|
-
log(`[ensure-join] mode=auto-local n=${ids.length} (rejoining this session's own rooms from local memory)`);
|
|
59
|
-
} else {
|
|
60
|
-
const listed = await listAgentTandems();
|
|
61
|
-
if (listed === null) {
|
|
62
|
-
warnListFailed();
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
ids = listed;
|
|
66
|
-
if (mem) {
|
|
67
|
-
try {
|
|
68
|
-
mem.seed(ids);
|
|
69
|
-
log(`[ensure-join] mode=auto-local n=${ids.length} (first run \u2014 seeded local memory from membership)`);
|
|
70
|
-
} catch (err) {
|
|
71
|
-
log(`[ensure-join] mode=auto-local n=${ids.length} (first run \u2014 seed write FAILED: ${err.message}; joining without persisting)`);
|
|
72
|
-
}
|
|
73
|
-
} else {
|
|
74
|
-
log(`[ensure-join] mode=auto-local n=${ids.length} (no room-memory port \u2014 falling back to agent-scoped list)`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
for (const id of ids) {
|
|
79
|
-
const outcome = await joinOne(opts.gateway, id, perCallTimeoutMs);
|
|
80
|
-
if (outcome.kind === "failed") {
|
|
81
|
-
result.failed.push(id);
|
|
82
|
-
log(`[ensure-join] ${id}: FAILED \u2014 ${outcome.detail} (continuing)`);
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (outcome.kind === "already") {
|
|
86
|
-
result.already.push(id);
|
|
87
|
-
log(`[ensure-join] ${id}: already seated`);
|
|
88
|
-
} else {
|
|
89
|
-
result.joined.push(id);
|
|
90
|
-
log(`[ensure-join] ${id}: joined fresh`);
|
|
91
|
-
}
|
|
92
|
-
try {
|
|
93
|
-
opts.onJoined?.(id);
|
|
94
|
-
} catch {
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return result;
|
|
98
|
-
}
|
|
99
|
-
async function joinOne(gateway, tandemId, perCallTimeoutMs) {
|
|
100
|
-
const ac = new AbortController();
|
|
101
|
-
const timer = setTimeout(() => ac.abort(), perCallTimeoutMs);
|
|
102
|
-
try {
|
|
103
|
-
const result = await gateway.sendRpc(
|
|
104
|
-
"tools/call",
|
|
105
|
-
{ name: "tandem_join", arguments: { tandem_id: tandemId } },
|
|
106
|
-
ac.signal
|
|
107
|
-
);
|
|
108
|
-
const text = (result.content ?? []).map((c) => typeof c?.text === "string" ? c.text : "").filter(Boolean).join("\n");
|
|
109
|
-
if (result.isError) {
|
|
110
|
-
return { kind: "failed", detail: text || "tandem_join returned an error with no text" };
|
|
111
|
-
}
|
|
112
|
-
if (/already[\s_-]?(a[\s_-]?)?(member|seated|joined)/i.test(text)) {
|
|
113
|
-
return { kind: "already" };
|
|
114
|
-
}
|
|
115
|
-
return { kind: "joined" };
|
|
116
|
-
} catch (err) {
|
|
117
|
-
return { kind: "failed", detail: err?.message ?? String(err) };
|
|
118
|
-
} finally {
|
|
119
|
-
clearTimeout(timer);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export {
|
|
124
|
-
parseTandemsConfig,
|
|
125
|
-
ensureJoinTandems
|
|
126
|
-
};
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
controlTokenAuthHeaders
|
|
3
|
-
} from "./chunk-GI2CKKBL.js";
|
|
4
1
|
import {
|
|
5
2
|
formatChannelEvents
|
|
6
3
|
} from "./chunk-PHXO5P25.js";
|
|
@@ -10,6 +7,9 @@ import {
|
|
|
10
7
|
import {
|
|
11
8
|
readSessionDiscoveryByKey
|
|
12
9
|
} from "./chunk-DO42NPNR.js";
|
|
10
|
+
import {
|
|
11
|
+
controlTokenAuthHeaders
|
|
12
|
+
} from "./chunk-GI2CKKBL.js";
|
|
13
13
|
import "./chunk-BLEGIR35.js";
|
|
14
14
|
import {
|
|
15
15
|
deriveDiscoveryKey,
|
|
File without changes
|