@essentialai/cogent-bridge 3.23.9 → 3.24.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.
- package/CHANGELOG.md +109 -0
- package/README.md +15 -3
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/services/peer-presence.d.ts +77 -0
- package/dist/services/peer-presence.d.ts.map +1 -0
- package/dist/services/peer-presence.js +181 -0
- package/dist/services/peer-presence.js.map +1 -0
- package/dist/startup.d.ts +28 -0
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +77 -0
- package/dist/startup.js.map +1 -1
- package/dist/tools/deregister-peer.d.ts.map +1 -1
- package/dist/tools/deregister-peer.js +22 -0
- package/dist/tools/deregister-peer.js.map +1 -1
- package/dist/tools/health-check.d.ts.map +1 -1
- package/dist/tools/health-check.js +20 -0
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/register-peer.d.ts.map +1 -1
- package/dist/tools/register-peer.js +20 -4
- package/dist/tools/register-peer.js.map +1 -1
- package/package.json +4 -3
- package/server.json +2 -2
- package/dist/e2e/helpers.d.ts +0 -101
- package/dist/e2e/helpers.d.ts.map +0 -1
- package/dist/e2e/helpers.js +0 -228
- package/dist/e2e/helpers.js.map +0 -1
- package/dist/services/session-wake-lock.d.ts +0 -26
- package/dist/services/session-wake-lock.d.ts.map +0 -1
- package/dist/services/session-wake-lock.js +0 -198
- package/dist/services/session-wake-lock.js.map +0 -1
package/dist/e2e/helpers.js
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E test helpers for cloud mode integration testing.
|
|
3
|
-
*
|
|
4
|
-
* Provides utilities to stand up a real Hono server (with WebSocket support)
|
|
5
|
-
* and create MCP client instances whose HttpBackend talks to that server.
|
|
6
|
-
* This enables full-stack testing: MCP tool call -> HttpBackend -> REST API
|
|
7
|
-
* -> server -> file persistence -> WebSocket push.
|
|
8
|
-
*/
|
|
9
|
-
import fs from "node:fs/promises";
|
|
10
|
-
import os from "node:os";
|
|
11
|
-
import path from "node:path";
|
|
12
|
-
import WebSocket from "ws";
|
|
13
|
-
import { createAdaptorServer } from "@hono/node-server";
|
|
14
|
-
import { SessionStore } from "../../server/src/services/session-store.js";
|
|
15
|
-
import { AuthService } from "../../server/src/services/auth-service.js";
|
|
16
|
-
import { ConnectionManager } from "../../server/src/services/connection-manager.js";
|
|
17
|
-
import { MessageQueueService } from "../../server/src/services/message-queue.js";
|
|
18
|
-
import { createApp } from "../../server/src/app.js";
|
|
19
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
21
|
-
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
22
|
-
import { HttpClient } from "../cloud/http-client.js";
|
|
23
|
-
import { HttpBackend } from "../backend/http-backend.js";
|
|
24
|
-
import { initBackend, resetBackend } from "../backend/backend-provider.js";
|
|
25
|
-
import { resetConfig, loadConfig } from "../config.js";
|
|
26
|
-
import { registerRegisterPeerTool } from "../tools/register-peer.js";
|
|
27
|
-
import { registerDeregisterPeerTool } from "../tools/deregister-peer.js";
|
|
28
|
-
import { registerSendMessageTool } from "../tools/send-message.js";
|
|
29
|
-
import { registerListPeersTool } from "../tools/list-peers.js";
|
|
30
|
-
import { registerGetHistoryTool } from "../tools/get-history.js";
|
|
31
|
-
import { registerHealthCheckTool } from "../tools/health-check.js";
|
|
32
|
-
/**
|
|
33
|
-
* Create a real Hono server with WebSocket support on an ephemeral port.
|
|
34
|
-
*
|
|
35
|
-
* Follows the same pattern as `server/src/__tests__/helpers.ts` createTestWsApp
|
|
36
|
-
* but tailored for E2E testing with separate MCP clients.
|
|
37
|
-
*
|
|
38
|
-
* @param existingStateDir - Optional state directory for server restart tests.
|
|
39
|
-
* When provided, skips mkdtemp and uses the given dir; cleanup() only
|
|
40
|
-
* removes the dir if it was created by this call.
|
|
41
|
-
*/
|
|
42
|
-
export async function createE2EServer(existingStateDir) {
|
|
43
|
-
const ownsDir = !existingStateDir;
|
|
44
|
-
const stateDir = existingStateDir ?? await fs.mkdtemp(path.join(os.tmpdir(), "e2e-test-"));
|
|
45
|
-
const sessionStore = new SessionStore(stateDir, 500);
|
|
46
|
-
const authService = new AuthService(4); // Low rounds for fast tests
|
|
47
|
-
const connectionManager = new ConnectionManager(30_000);
|
|
48
|
-
const messageQueue = new MessageQueueService(sessionStore);
|
|
49
|
-
await sessionStore.init();
|
|
50
|
-
const { app, injectWebSocket } = createApp({
|
|
51
|
-
sessionStore,
|
|
52
|
-
authService,
|
|
53
|
-
startTime: Date.now(),
|
|
54
|
-
maxMessages: 500,
|
|
55
|
-
connectionManager,
|
|
56
|
-
messageQueue,
|
|
57
|
-
heartbeatIntervalMs: 30_000,
|
|
58
|
-
heartbeatMaxMissed: 3,
|
|
59
|
-
});
|
|
60
|
-
const server = createAdaptorServer(app);
|
|
61
|
-
injectWebSocket(server);
|
|
62
|
-
return new Promise((resolve) => {
|
|
63
|
-
server.listen(0, () => {
|
|
64
|
-
const addr = server.address();
|
|
65
|
-
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
66
|
-
resolve({
|
|
67
|
-
port,
|
|
68
|
-
server,
|
|
69
|
-
sessionStore,
|
|
70
|
-
connectionManager,
|
|
71
|
-
messageQueue,
|
|
72
|
-
stateDir,
|
|
73
|
-
wsUrl: (sessionId, token, peerId) => `ws://localhost:${port}/ws/${sessionId}?token=${encodeURIComponent(token)}&peerId=${encodeURIComponent(peerId)}`,
|
|
74
|
-
shutdown: async () => {
|
|
75
|
-
connectionManager.closeAll(1012, "Test shutdown");
|
|
76
|
-
return new Promise((res, rej) => {
|
|
77
|
-
server.close((err) => (err ? rej(err) : res()));
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
cleanup: async () => {
|
|
81
|
-
if (ownsDir) {
|
|
82
|
-
await fs.rm(stateDir, { recursive: true, force: true });
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Create an MCP Server + Client pair connected via InMemoryTransport,
|
|
91
|
-
* backed by an HttpBackend pointed at the given E2E server.
|
|
92
|
-
*
|
|
93
|
-
* Since tool handlers call getBackend() at runtime (module-level singleton),
|
|
94
|
-
* call activate() before each tool invocation to swap the backend.
|
|
95
|
-
*/
|
|
96
|
-
export async function createE2EClient(port, sessionId, token, tempDir) {
|
|
97
|
-
const httpClient = new HttpClient(`http://localhost:${port}`, token);
|
|
98
|
-
const backend = new HttpBackend(httpClient, sessionId);
|
|
99
|
-
const mcpServer = new McpServer({ name: "e2e-test", version: "1.0.0" });
|
|
100
|
-
// Register all 6 standard tools
|
|
101
|
-
registerRegisterPeerTool(mcpServer);
|
|
102
|
-
registerDeregisterPeerTool(mcpServer);
|
|
103
|
-
registerSendMessageTool(mcpServer);
|
|
104
|
-
registerListPeersTool(mcpServer);
|
|
105
|
-
registerGetHistoryTool(mcpServer);
|
|
106
|
-
registerHealthCheckTool(mcpServer);
|
|
107
|
-
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
108
|
-
const client = new Client({ name: "e2e-client", version: "1.0.0" });
|
|
109
|
-
await Promise.all([
|
|
110
|
-
mcpServer.connect(serverTransport),
|
|
111
|
-
client.connect(clientTransport),
|
|
112
|
-
]);
|
|
113
|
-
return {
|
|
114
|
-
client,
|
|
115
|
-
mcpServer,
|
|
116
|
-
backend,
|
|
117
|
-
httpClient,
|
|
118
|
-
activate: () => {
|
|
119
|
-
// Swap the module-level backend singleton to this client's backend
|
|
120
|
-
resetBackend();
|
|
121
|
-
initBackend(backend);
|
|
122
|
-
// Ensure config is loaded with cloud endpoint pointing at the E2E server
|
|
123
|
-
resetConfig();
|
|
124
|
-
loadConfig({
|
|
125
|
-
CC_BRIDGE_STATE_PATH: tempDir,
|
|
126
|
-
CC_BRIDGE_ENDPOINT: `http://localhost:${port}`,
|
|
127
|
-
CC_BRIDGE_SESSION_ID: sessionId,
|
|
128
|
-
CC_BRIDGE_TIMEOUT_MS: "10000",
|
|
129
|
-
CC_BRIDGE_LOG_LEVEL: "error",
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
close: async () => {
|
|
133
|
-
await client.close();
|
|
134
|
-
await mcpServer.close();
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
// ---------------------------------------------------------------------------
|
|
139
|
-
// Cloud session REST helpers
|
|
140
|
-
// ---------------------------------------------------------------------------
|
|
141
|
-
/**
|
|
142
|
-
* Create a new cloud session via direct fetch.
|
|
143
|
-
* Returns the sessionId and bearer token.
|
|
144
|
-
*/
|
|
145
|
-
export async function createCloudSession(port, secret) {
|
|
146
|
-
const res = await fetch(`http://localhost:${port}/api/sessions`, {
|
|
147
|
-
method: "POST",
|
|
148
|
-
headers: { "Content-Type": "application/json" },
|
|
149
|
-
body: JSON.stringify({ secret }),
|
|
150
|
-
});
|
|
151
|
-
if (!res.ok) {
|
|
152
|
-
throw new Error(`createCloudSession failed: HTTP ${res.status}`);
|
|
153
|
-
}
|
|
154
|
-
return (await res.json());
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Join an existing cloud session via direct fetch.
|
|
158
|
-
* Returns a bearer token for the joining client.
|
|
159
|
-
*/
|
|
160
|
-
export async function joinCloudSession(port, sessionId, secret) {
|
|
161
|
-
const res = await fetch(`http://localhost:${port}/api/sessions/${sessionId}/join`, {
|
|
162
|
-
method: "POST",
|
|
163
|
-
headers: { "Content-Type": "application/json" },
|
|
164
|
-
body: JSON.stringify({ secret }),
|
|
165
|
-
});
|
|
166
|
-
if (!res.ok) {
|
|
167
|
-
throw new Error(`joinCloudSession failed: HTTP ${res.status}`);
|
|
168
|
-
}
|
|
169
|
-
return (await res.json());
|
|
170
|
-
}
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
// MCP result parser
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
174
|
-
/**
|
|
175
|
-
* Extract and JSON.parse the text from an MCP tool result content array.
|
|
176
|
-
* Copied from src/integration.test.ts.
|
|
177
|
-
*/
|
|
178
|
-
export function parseResult(result) {
|
|
179
|
-
return JSON.parse(result.content[0].text);
|
|
180
|
-
}
|
|
181
|
-
// ---------------------------------------------------------------------------
|
|
182
|
-
// Async utilities
|
|
183
|
-
// ---------------------------------------------------------------------------
|
|
184
|
-
/** Small delay helper for async settling. */
|
|
185
|
-
export const tick = (ms = 100) => new Promise((r) => setTimeout(r, ms));
|
|
186
|
-
/**
|
|
187
|
-
* Wait for the next parsed JSON message from a WebSocket connection.
|
|
188
|
-
* Optionally filters by frame type (ignoring non-matching frames).
|
|
189
|
-
* Times out after the specified duration.
|
|
190
|
-
*/
|
|
191
|
-
export function waitForWsMessage(ws, type, timeoutMs = 5000) {
|
|
192
|
-
return new Promise((resolve, reject) => {
|
|
193
|
-
const timeout = setTimeout(() => {
|
|
194
|
-
ws.off("message", handler);
|
|
195
|
-
reject(new Error(`Timed out waiting for WS message${type ? ` of type "${type}"` : ""}`));
|
|
196
|
-
}, timeoutMs);
|
|
197
|
-
const handler = (data) => {
|
|
198
|
-
const frame = JSON.parse(data.toString());
|
|
199
|
-
if (!type || frame.type === type) {
|
|
200
|
-
clearTimeout(timeout);
|
|
201
|
-
ws.off("message", handler);
|
|
202
|
-
resolve(frame);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
ws.on("message", handler);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Open a WebSocket connection and wait until it's fully open.
|
|
210
|
-
* Rejects on connection error.
|
|
211
|
-
*/
|
|
212
|
-
export function connectWs(url) {
|
|
213
|
-
return new Promise((resolve, reject) => {
|
|
214
|
-
const ws = new WebSocket(url);
|
|
215
|
-
const onClose = (code) => {
|
|
216
|
-
if (ws.readyState !== WebSocket.OPEN) {
|
|
217
|
-
reject(new Error(`WebSocket closed during connect with code ${code}`));
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
ws.on("open", () => {
|
|
221
|
-
ws.off("close", onClose);
|
|
222
|
-
resolve(ws);
|
|
223
|
-
});
|
|
224
|
-
ws.on("error", (err) => reject(err));
|
|
225
|
-
ws.on("close", onClose);
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
//# sourceMappingURL=helpers.js.map
|
package/dist/e2e/helpers.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/e2e/helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Hard ceiling on how long one lease may exclude others. Mirrors wake-claim's CLAIM_MAX_MS. */
|
|
2
|
-
export declare const SESSION_WAKE_MAX_MS = 1800000;
|
|
3
|
-
/** `~/.cogent/wake-sessions/<sha256(cwd)[..16]>.<sessionId>.json` */
|
|
4
|
-
export declare function sessionWakeLockPath(sessionId: string, cwd?: string): string;
|
|
5
|
-
export interface SessionWakeLease {
|
|
6
|
-
/** True whenever the caller may wake — including every fail-open path. */
|
|
7
|
-
acquired: boolean;
|
|
8
|
-
/** Milliseconds spent waiting for another process. 0 when uncontended. */
|
|
9
|
-
waitedMs: number;
|
|
10
|
-
/** True when another live process held the lease and we had to wait for it. */
|
|
11
|
-
contended: boolean;
|
|
12
|
-
/** Idempotent. Safe to call even when the lease was never really taken. */
|
|
13
|
-
release: () => void;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Become the single process resuming `sessionId` in `cwd`, waiting out any other holder.
|
|
17
|
-
*
|
|
18
|
-
* Never returns `acquired: false` today — every path either takes the lease or fails open — but the
|
|
19
|
-
* field is explicit so a caller reads the intent rather than assuming it.
|
|
20
|
-
*/
|
|
21
|
-
export declare function acquireSessionWake(sessionId: string, cwd?: string, opts?: {
|
|
22
|
-
maxWaitMs?: number;
|
|
23
|
-
}): Promise<SessionWakeLease>;
|
|
24
|
-
/** Release the lease. Best-effort; an orphan expires or is stolen when its holder dies. */
|
|
25
|
-
export declare function releaseSessionWake(sessionId: string, cwd?: string): void;
|
|
26
|
-
//# sourceMappingURL=session-wake-lock.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session-wake-lock.d.ts","sourceRoot":"","sources":["../../src/services/session-wake-lock.ts"],"names":[],"mappings":"AAwCA,gGAAgG;AAChG,eAAO,MAAM,mBAAmB,UAAY,CAAC;AAqB7C,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAE1F;AAsDD,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAsB,EAC3B,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAChC,OAAO,CAAC,gBAAgB,CAAC,CA8E3B;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,IAAI,CAMvF"}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CROSS-PROCESS SESSION WAKE LOCK — never two `claude --resume <S>` at once in one checkout.
|
|
3
|
-
*
|
|
4
|
-
* ── THE DEFECT THIS FIXES, AND HOW IT DIFFERS FROM wake-claim.ts ─────────────────────────
|
|
5
|
-
* `wake-claim.ts` excludes on the MESSAGE id: it stops one message, fanned by the relay to N
|
|
6
|
-
* sockets of the same peer, from being answered N times. That is a real defect and it is fixed.
|
|
7
|
-
*
|
|
8
|
-
* It cannot help here. Measured on a real user (peer `marvin-coder`, 2026-09-05): the SAME peer
|
|
9
|
-
* was registered on the FREE relay at plugin v3.20.0 AND on the TEAM relay at v3.23.1 — same cwd,
|
|
10
|
-
* same thread id, two live bridge processes. Reachable because credentials are keyed on cwd ALONE
|
|
11
|
-
* (`~/.cogent/credentials/<sha256(cwd)>.json`, credential-store.ts:64-71); the endpoint is not part
|
|
12
|
-
* of the key, so a second join overwrites the file while the first process keeps running from its
|
|
13
|
-
* in-memory token and live WebSocket. Nothing on disk then records that the first relay is in play,
|
|
14
|
-
* which is why nothing reaps it.
|
|
15
|
-
*
|
|
16
|
-
* Two relays mint two DIFFERENT message ids, so both per-message claims succeed and both processes
|
|
17
|
-
* resume the SAME session concurrently. `scripts/wake-dedup-matrix-test.mjs --topology two-relay`
|
|
18
|
-
* reproduces it offline against two local relays: two wakes, one session id, 4001ms of overlap.
|
|
19
|
-
*
|
|
20
|
-
* So the exclusion has to key on the RESUME TARGET — (cwd, sessionId) — not on the message.
|
|
21
|
-
*
|
|
22
|
-
* ── WHY IT WAITS INSTEAD OF DROPPING ─────────────────────────────────────────────────────
|
|
23
|
-
* Two relays carry two genuinely distinct questions; answering both is CORRECT. What is never
|
|
24
|
-
* correct is running both turns at once against one worktree. So a loser waits for the holder and
|
|
25
|
-
* then proceeds, which serialises the turns without losing a message. Dropping would trade a
|
|
26
|
-
* corruption bug for a silence bug, and silence is the worse failure mode in this codebase.
|
|
27
|
-
*
|
|
28
|
-
* ── FAILS OPEN, DELIBERATELY ─────────────────────────────────────────────────────────────
|
|
29
|
-
* Every uncertain path — unreadable lock dir, corrupt holder record, a holder that outlives the
|
|
30
|
-
* wait budget — resolves to "go ahead and wake". A lock that can mute an agent for good is worse
|
|
31
|
-
* than the overlap it prevents. That is the 3.21.5 lesson: a fix whose blast radius is "auto-wake
|
|
32
|
-
* stops working" is not a safe fix, however correct it looks.
|
|
33
|
-
*/
|
|
34
|
-
import crypto from "node:crypto";
|
|
35
|
-
import fs from "node:fs";
|
|
36
|
-
import os from "node:os";
|
|
37
|
-
import path from "node:path";
|
|
38
|
-
import { getConfig } from "../config.js";
|
|
39
|
-
import { logger } from "../logger.js";
|
|
40
|
-
/** Hard ceiling on how long one lease may exclude others. Mirrors wake-claim's CLAIM_MAX_MS. */
|
|
41
|
-
export const SESSION_WAKE_MAX_MS = 1_800_000; // 30 min
|
|
42
|
-
/** Multiplier over COGENT_TIMEOUT_MS covering primary + rotated-session retry, as wake-claim does. */
|
|
43
|
-
const LEASE_TIMEOUT_FACTOR = 3;
|
|
44
|
-
/** How often a waiter re-checks. Short enough to be invisible, long enough not to spin. */
|
|
45
|
-
const POLL_MS = 250;
|
|
46
|
-
/** MUST match wake-claim / wake-inflight / credential-store — one cwd, one key, everywhere. */
|
|
47
|
-
function hashCwd(cwd) {
|
|
48
|
-
return crypto.createHash("sha256").update(path.resolve(cwd)).digest("hex").slice(0, 16);
|
|
49
|
-
}
|
|
50
|
-
const safe = (s) => String(s).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
51
|
-
/** `~/.cogent/wake-sessions/<sha256(cwd)[..16]>.<sessionId>.json` */
|
|
52
|
-
export function sessionWakeLockPath(sessionId, cwd = process.cwd()) {
|
|
53
|
-
return path.join(os.homedir(), ".cogent", "wake-sessions", `${hashCwd(cwd)}.${safe(sessionId)}.json`);
|
|
54
|
-
}
|
|
55
|
-
function leaseTtlMs() {
|
|
56
|
-
let timeout = 300_000;
|
|
57
|
-
try {
|
|
58
|
-
const c = getConfig()?.COGENT_TIMEOUT_MS;
|
|
59
|
-
if (typeof c === "number" && Number.isFinite(c) && c > 0)
|
|
60
|
-
timeout = c;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
/* config not loaded — documented default */
|
|
64
|
-
}
|
|
65
|
-
return Math.min(timeout * LEASE_TIMEOUT_FACTOR, SESSION_WAKE_MAX_MS);
|
|
66
|
-
}
|
|
67
|
-
/** Default budget for waiting out another process's turn: one full turn. */
|
|
68
|
-
function defaultMaxWaitMs() {
|
|
69
|
-
try {
|
|
70
|
-
const c = getConfig()?.COGENT_TIMEOUT_MS;
|
|
71
|
-
if (typeof c === "number" && Number.isFinite(c) && c > 0)
|
|
72
|
-
return c;
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
/* config not loaded */
|
|
76
|
-
}
|
|
77
|
-
return 300_000;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Is the recorded holder still a live process?
|
|
81
|
-
*
|
|
82
|
-
* Our OWN pid counts as DEAD here, unlike wake-claim: in-process wakes are already serialised by
|
|
83
|
-
* AutoRelayService's `processing` flag, so a lease still bearing this pid is a leak from an earlier
|
|
84
|
-
* turn, never a concurrent one. Blocking on ourselves would deadlock the agent for the full budget.
|
|
85
|
-
*/
|
|
86
|
-
function holderAlive(pid) {
|
|
87
|
-
if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0)
|
|
88
|
-
return false;
|
|
89
|
-
if (pid === process.pid)
|
|
90
|
-
return false;
|
|
91
|
-
try {
|
|
92
|
-
process.kill(pid, 0); // signal 0 = existence check only
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
// EPERM means it EXISTS but belongs to another user — alive for our purposes.
|
|
97
|
-
return err?.code === "EPERM";
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/** Read the current holder, or null when absent/corrupt (both mean "free"). */
|
|
101
|
-
function readHolder(p) {
|
|
102
|
-
try {
|
|
103
|
-
const held = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
104
|
-
if (!held || typeof held.expiresAt !== "number")
|
|
105
|
-
return null;
|
|
106
|
-
return held;
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
113
|
-
/**
|
|
114
|
-
* Become the single process resuming `sessionId` in `cwd`, waiting out any other holder.
|
|
115
|
-
*
|
|
116
|
-
* Never returns `acquired: false` today — every path either takes the lease or fails open — but the
|
|
117
|
-
* field is explicit so a caller reads the intent rather than assuming it.
|
|
118
|
-
*/
|
|
119
|
-
export async function acquireSessionWake(sessionId, cwd = process.cwd(), opts = {}) {
|
|
120
|
-
const p = sessionWakeLockPath(sessionId, cwd);
|
|
121
|
-
const maxWaitMs = opts.maxWaitMs ?? defaultMaxWaitMs();
|
|
122
|
-
const started = Date.now();
|
|
123
|
-
let contended = false;
|
|
124
|
-
const lease = () => ({
|
|
125
|
-
acquired: true,
|
|
126
|
-
waitedMs: Date.now() - started,
|
|
127
|
-
contended,
|
|
128
|
-
release: () => releaseSessionWake(sessionId, cwd),
|
|
129
|
-
});
|
|
130
|
-
/** Fail open: the caller wakes, and nothing is left behind that could mute the next turn. */
|
|
131
|
-
const failOpen = (why) => {
|
|
132
|
-
logger.warn(`session-wake-lock: ${why} — waking anyway (fail-open)`);
|
|
133
|
-
return { acquired: true, waitedMs: Date.now() - started, contended, release: () => { } };
|
|
134
|
-
};
|
|
135
|
-
try {
|
|
136
|
-
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
137
|
-
}
|
|
138
|
-
catch (err) {
|
|
139
|
-
return failOpen(`cannot create lock dir (${err.message})`);
|
|
140
|
-
}
|
|
141
|
-
for (;;) {
|
|
142
|
-
const now = Date.now();
|
|
143
|
-
const body = JSON.stringify({
|
|
144
|
-
pid: process.pid,
|
|
145
|
-
sessionId,
|
|
146
|
-
startedAt: now,
|
|
147
|
-
expiresAt: now + leaseTtlMs(),
|
|
148
|
-
});
|
|
149
|
-
try {
|
|
150
|
-
fs.writeFileSync(p, body, { flag: "wx" }); // atomic: exactly one winner
|
|
151
|
-
if (contended) {
|
|
152
|
-
logger.info(`Auto-relay: waited ${Date.now() - started}ms for another bridge process to finish its ` +
|
|
153
|
-
`turn on session ${sessionId} before resuming — turns are now serialised, not overlapping.`);
|
|
154
|
-
}
|
|
155
|
-
return lease();
|
|
156
|
-
}
|
|
157
|
-
catch (err) {
|
|
158
|
-
if (err?.code !== "EEXIST") {
|
|
159
|
-
return failOpen(`cannot write lock (${err.message})`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Held. Take it over when the holder is gone, expired, or unreadable — a lease that outlives
|
|
163
|
-
// its holder would mute this agent until the writer-stamped expiry.
|
|
164
|
-
const held = readHolder(p);
|
|
165
|
-
if (!held || held.expiresAt <= now || !holderAlive(held.pid)) {
|
|
166
|
-
try {
|
|
167
|
-
fs.writeFileSync(p, body); // steal
|
|
168
|
-
}
|
|
169
|
-
catch (err) {
|
|
170
|
-
return failOpen(`cannot steal abandoned lock (${err.message})`);
|
|
171
|
-
}
|
|
172
|
-
logger.debug(`session-wake-lock: took over an abandoned lease for session ${sessionId}`);
|
|
173
|
-
return lease();
|
|
174
|
-
}
|
|
175
|
-
if (!contended) {
|
|
176
|
-
contended = true;
|
|
177
|
-
logger.info(`Auto-relay: pid ${held.pid} is already resuming session ${sessionId} in this checkout — ` +
|
|
178
|
-
`waiting for it rather than running a second '--resume' over the same worktree. ` +
|
|
179
|
-
`Two bridge processes are registered for this cwd (check for a stale one).`);
|
|
180
|
-
}
|
|
181
|
-
if (Date.now() - started >= maxWaitMs) {
|
|
182
|
-
// Budget spent and the holder is STILL alive. Proceeding risks the overlap this lock
|
|
183
|
-
// exists to prevent; refusing risks silence. Silence is worse — but say so loudly.
|
|
184
|
-
return failOpen(`pid ${held.pid} still holds session ${sessionId} after ${Date.now() - started}ms`);
|
|
185
|
-
}
|
|
186
|
-
await sleep(POLL_MS);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
/** Release the lease. Best-effort; an orphan expires or is stolen when its holder dies. */
|
|
190
|
-
export function releaseSessionWake(sessionId, cwd = process.cwd()) {
|
|
191
|
-
try {
|
|
192
|
-
fs.unlinkSync(sessionWakeLockPath(sessionId, cwd));
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
/* already gone, or never ours */
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
//# sourceMappingURL=session-wake-lock.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session-wake-lock.js","sourceRoot":"","sources":["../../src/services/session-wake-lock.ts"],"names":[],"mappings":"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"}
|