@essentialai/cogent-bridge-bundled 3.21.9 → 3.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cogent-bridge.mjs +547 -186
- package/package.json +1 -1
package/cogent-bridge.mjs
CHANGED
|
@@ -1259,8 +1259,8 @@ var init_parseUtil = __esm({
|
|
|
1259
1259
|
init_errors();
|
|
1260
1260
|
init_en();
|
|
1261
1261
|
makeIssue = (params) => {
|
|
1262
|
-
const { data, path:
|
|
1263
|
-
const fullPath = [...
|
|
1262
|
+
const { data, path: path21, errorMaps, issueData } = params;
|
|
1263
|
+
const fullPath = [...path21, ...issueData.path || []];
|
|
1264
1264
|
const fullIssue = {
|
|
1265
1265
|
...issueData,
|
|
1266
1266
|
path: fullPath
|
|
@@ -1540,11 +1540,11 @@ var init_types = __esm({
|
|
|
1540
1540
|
init_parseUtil();
|
|
1541
1541
|
init_util();
|
|
1542
1542
|
ParseInputLazyPath = class {
|
|
1543
|
-
constructor(parent, value,
|
|
1543
|
+
constructor(parent, value, path21, key) {
|
|
1544
1544
|
this._cachedPath = [];
|
|
1545
1545
|
this.parent = parent;
|
|
1546
1546
|
this.data = value;
|
|
1547
|
-
this._path =
|
|
1547
|
+
this._path = path21;
|
|
1548
1548
|
this._key = key;
|
|
1549
1549
|
}
|
|
1550
1550
|
get path() {
|
|
@@ -4996,10 +4996,10 @@ function assignProp(target, prop, value) {
|
|
|
4996
4996
|
configurable: true
|
|
4997
4997
|
});
|
|
4998
4998
|
}
|
|
4999
|
-
function getElementAtPath(obj,
|
|
5000
|
-
if (!
|
|
4999
|
+
function getElementAtPath(obj, path21) {
|
|
5000
|
+
if (!path21)
|
|
5001
5001
|
return obj;
|
|
5002
|
-
return
|
|
5002
|
+
return path21.reduce((acc, key) => acc?.[key], obj);
|
|
5003
5003
|
}
|
|
5004
5004
|
function promiseAllObject(promisesObj) {
|
|
5005
5005
|
const keys = Object.keys(promisesObj);
|
|
@@ -5248,11 +5248,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5248
5248
|
}
|
|
5249
5249
|
return false;
|
|
5250
5250
|
}
|
|
5251
|
-
function prefixIssues(
|
|
5251
|
+
function prefixIssues(path21, issues) {
|
|
5252
5252
|
return issues.map((iss) => {
|
|
5253
5253
|
var _a;
|
|
5254
5254
|
(_a = iss).path ?? (_a.path = []);
|
|
5255
|
-
iss.path.unshift(
|
|
5255
|
+
iss.path.unshift(path21);
|
|
5256
5256
|
return iss;
|
|
5257
5257
|
});
|
|
5258
5258
|
}
|
|
@@ -17058,8 +17058,8 @@ var require_utils = __commonJS({
|
|
|
17058
17058
|
}
|
|
17059
17059
|
return ind;
|
|
17060
17060
|
}
|
|
17061
|
-
function removeDotSegments(
|
|
17062
|
-
let input =
|
|
17061
|
+
function removeDotSegments(path21) {
|
|
17062
|
+
let input = path21;
|
|
17063
17063
|
const output = [];
|
|
17064
17064
|
let nextSlash = -1;
|
|
17065
17065
|
let len = 0;
|
|
@@ -17258,8 +17258,8 @@ var require_schemes = __commonJS({
|
|
|
17258
17258
|
wsComponent.secure = void 0;
|
|
17259
17259
|
}
|
|
17260
17260
|
if (wsComponent.resourceName) {
|
|
17261
|
-
const [
|
|
17262
|
-
wsComponent.path =
|
|
17261
|
+
const [path21, query] = wsComponent.resourceName.split("?");
|
|
17262
|
+
wsComponent.path = path21 && path21 !== "/" ? path21 : void 0;
|
|
17263
17263
|
wsComponent.query = query;
|
|
17264
17264
|
wsComponent.resourceName = void 0;
|
|
17265
17265
|
}
|
|
@@ -20612,12 +20612,12 @@ var require_dist = __commonJS({
|
|
|
20612
20612
|
throw new Error(`Unknown format "${name}"`);
|
|
20613
20613
|
return f;
|
|
20614
20614
|
};
|
|
20615
|
-
function addFormats(ajv, list,
|
|
20615
|
+
function addFormats(ajv, list, fs19, exportName) {
|
|
20616
20616
|
var _a;
|
|
20617
20617
|
var _b;
|
|
20618
20618
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
20619
20619
|
for (const f of list)
|
|
20620
|
-
ajv.addFormat(f,
|
|
20620
|
+
ajv.addFormat(f, fs19[f]);
|
|
20621
20621
|
}
|
|
20622
20622
|
module.exports = exports = formatsPlugin;
|
|
20623
20623
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -21729,8 +21729,8 @@ var require_parseUtil = __commonJS({
|
|
|
21729
21729
|
var errors_js_1 = require_errors2();
|
|
21730
21730
|
var en_js_1 = __importDefault(require_en());
|
|
21731
21731
|
var makeIssue2 = (params) => {
|
|
21732
|
-
const { data, path:
|
|
21733
|
-
const fullPath = [...
|
|
21732
|
+
const { data, path: path21, errorMaps, issueData } = params;
|
|
21733
|
+
const fullPath = [...path21, ...issueData.path || []];
|
|
21734
21734
|
const fullIssue = {
|
|
21735
21735
|
...issueData,
|
|
21736
21736
|
path: fullPath
|
|
@@ -21884,11 +21884,11 @@ var require_types2 = __commonJS({
|
|
|
21884
21884
|
var parseUtil_js_1 = require_parseUtil();
|
|
21885
21885
|
var util_js_1 = require_util2();
|
|
21886
21886
|
var ParseInputLazyPath2 = class {
|
|
21887
|
-
constructor(parent, value,
|
|
21887
|
+
constructor(parent, value, path21, key) {
|
|
21888
21888
|
this._cachedPath = [];
|
|
21889
21889
|
this.parent = parent;
|
|
21890
21890
|
this.data = value;
|
|
21891
|
-
this._path =
|
|
21891
|
+
this._path = path21;
|
|
21892
21892
|
this._key = key;
|
|
21893
21893
|
}
|
|
21894
21894
|
get path() {
|
|
@@ -25477,10 +25477,10 @@ var require_zod = __commonJS({
|
|
|
25477
25477
|
};
|
|
25478
25478
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25479
25479
|
exports.z = void 0;
|
|
25480
|
-
var
|
|
25481
|
-
exports.z =
|
|
25480
|
+
var z19 = __importStar(require_external());
|
|
25481
|
+
exports.z = z19;
|
|
25482
25482
|
__exportStar(require_external(), exports);
|
|
25483
|
-
exports.default =
|
|
25483
|
+
exports.default = z19;
|
|
25484
25484
|
}
|
|
25485
25485
|
});
|
|
25486
25486
|
|
|
@@ -26389,8 +26389,8 @@ var init_stdio2 = __esm({
|
|
|
26389
26389
|
// src/constants.ts
|
|
26390
26390
|
import { createRequire } from "node:module";
|
|
26391
26391
|
function resolveVersion() {
|
|
26392
|
-
if ("3.
|
|
26393
|
-
return "3.
|
|
26392
|
+
if ("3.23.0") {
|
|
26393
|
+
return "3.23.0";
|
|
26394
26394
|
}
|
|
26395
26395
|
try {
|
|
26396
26396
|
const require2 = createRequire(import.meta.url);
|
|
@@ -27747,8 +27747,13 @@ async function getRelayVersion(http, nowFn = Date.now) {
|
|
|
27747
27747
|
if (cached2 !== null && now - cached2.fetchedAt < TTL_MS) {
|
|
27748
27748
|
return cached2.version;
|
|
27749
27749
|
}
|
|
27750
|
+
return (await _refresh(http, now))?.version ?? null;
|
|
27751
|
+
}
|
|
27752
|
+
async function _refresh(http, now) {
|
|
27750
27753
|
try {
|
|
27751
|
-
const resp = await http.get(
|
|
27754
|
+
const resp = await http.get(
|
|
27755
|
+
"/api/health"
|
|
27756
|
+
);
|
|
27752
27757
|
const version2 = resp?.version;
|
|
27753
27758
|
if (typeof version2 !== "string" || version2.length === 0) {
|
|
27754
27759
|
logger.debug(
|
|
@@ -27756,14 +27761,26 @@ async function getRelayVersion(http, nowFn = Date.now) {
|
|
|
27756
27761
|
);
|
|
27757
27762
|
return null;
|
|
27758
27763
|
}
|
|
27759
|
-
|
|
27760
|
-
|
|
27764
|
+
const raw = resp?.capabilities;
|
|
27765
|
+
const capabilities = Array.isArray(raw) ? raw.filter((c2) => typeof c2 === "string") : [];
|
|
27766
|
+
cached2 = { version: version2, capabilities, fetchedAt: now };
|
|
27767
|
+
return cached2;
|
|
27761
27768
|
} catch (err) {
|
|
27762
27769
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27763
27770
|
logger.debug(`relay-version-cache: /api/health fetch failed: ${msg}`);
|
|
27764
27771
|
return null;
|
|
27765
27772
|
}
|
|
27766
27773
|
}
|
|
27774
|
+
async function getRelayCapabilities(http, nowFn = Date.now) {
|
|
27775
|
+
const now = nowFn();
|
|
27776
|
+
if (cached2 !== null && now - cached2.fetchedAt < TTL_MS) {
|
|
27777
|
+
return cached2.capabilities;
|
|
27778
|
+
}
|
|
27779
|
+
return (await _refresh(http, now))?.capabilities ?? null;
|
|
27780
|
+
}
|
|
27781
|
+
async function relaySupports(http, capability, nowFn = Date.now) {
|
|
27782
|
+
return (await getRelayCapabilities(http, nowFn))?.includes(capability) ?? false;
|
|
27783
|
+
}
|
|
27767
27784
|
var TTL_MS, cached2;
|
|
27768
27785
|
var init_relay_version_cache = __esm({
|
|
27769
27786
|
"src/backend/relay-version-cache.ts"() {
|
|
@@ -27779,6 +27796,8 @@ var MAILBOX_DEPROVISIONED_HEADER, PATHS, HttpBackend;
|
|
|
27779
27796
|
var init_http_backend = __esm({
|
|
27780
27797
|
"src/backend/http-backend.ts"() {
|
|
27781
27798
|
"use strict";
|
|
27799
|
+
init_relay_version_cache();
|
|
27800
|
+
init_errors4();
|
|
27782
27801
|
init_health_check();
|
|
27783
27802
|
init_constants();
|
|
27784
27803
|
init_config();
|
|
@@ -27806,8 +27825,8 @@ var init_http_backend = __esm({
|
|
|
27806
27825
|
* The sessionId parameter from StorageBackend is the local CC sessionId;
|
|
27807
27826
|
* in cloud mode we ignore it and use this.sessionId (the cloud session).
|
|
27808
27827
|
*/
|
|
27809
|
-
async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId) {
|
|
27810
|
-
const
|
|
27828
|
+
async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId, peerSecret) {
|
|
27829
|
+
const path21 = PATHS.peers.replace(":sessionId", this.sessionId);
|
|
27811
27830
|
const body = {
|
|
27812
27831
|
peerId,
|
|
27813
27832
|
cwd,
|
|
@@ -27835,7 +27854,10 @@ var init_http_backend = __esm({
|
|
|
27835
27854
|
body.role = roleCap.slice("role:".length);
|
|
27836
27855
|
}
|
|
27837
27856
|
}
|
|
27838
|
-
|
|
27857
|
+
if (peerSecret !== void 0 && await relaySupports(this.http, "peer-ownership")) {
|
|
27858
|
+
body.peerSecret = peerSecret;
|
|
27859
|
+
}
|
|
27860
|
+
return this.http.post(path21, body);
|
|
27839
27861
|
}
|
|
27840
27862
|
/**
|
|
27841
27863
|
* Deregister a peer from the cloud session.
|
|
@@ -27847,13 +27869,16 @@ var init_http_backend = __esm({
|
|
|
27847
27869
|
* local mail creds. A 404 (peer not found) resolves to `{ removed: false, ... }`, not a throw.
|
|
27848
27870
|
*/
|
|
27849
27871
|
async deregisterPeer(peerId) {
|
|
27850
|
-
const
|
|
27872
|
+
const path21 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
|
|
27851
27873
|
try {
|
|
27852
|
-
const headers = await this.http.delete(
|
|
27874
|
+
const headers = await this.http.delete(path21, {});
|
|
27853
27875
|
const mailboxDeprovisioned = headers.get(MAILBOX_DEPROVISIONED_HEADER) === "true";
|
|
27854
27876
|
return { removed: true, mailboxDeprovisioned };
|
|
27855
|
-
} catch {
|
|
27856
|
-
|
|
27877
|
+
} catch (err) {
|
|
27878
|
+
if (err instanceof BridgeError && err.code === "PEER_NOT_FOUND" /* PEER_NOT_FOUND */) {
|
|
27879
|
+
return { removed: false, mailboxDeprovisioned: false };
|
|
27880
|
+
}
|
|
27881
|
+
throw err;
|
|
27857
27882
|
}
|
|
27858
27883
|
}
|
|
27859
27884
|
/**
|
|
@@ -27870,8 +27895,8 @@ var init_http_backend = __esm({
|
|
|
27870
27895
|
* GET /api/sessions/:sessionId/peers
|
|
27871
27896
|
*/
|
|
27872
27897
|
async listPeers() {
|
|
27873
|
-
const
|
|
27874
|
-
const result = await this.http.get(
|
|
27898
|
+
const path21 = PATHS.peers.replace(":sessionId", this.sessionId);
|
|
27899
|
+
const result = await this.http.get(path21);
|
|
27875
27900
|
return result.peers;
|
|
27876
27901
|
}
|
|
27877
27902
|
/**
|
|
@@ -27879,8 +27904,8 @@ var init_http_backend = __esm({
|
|
|
27879
27904
|
* Calls the lightweight heartbeat endpoint on the cloud relay server.
|
|
27880
27905
|
*/
|
|
27881
27906
|
async updateLastSeen(peerId) {
|
|
27882
|
-
const
|
|
27883
|
-
await this.http.post(
|
|
27907
|
+
const path21 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
|
|
27908
|
+
await this.http.post(path21, { peerId });
|
|
27884
27909
|
}
|
|
27885
27910
|
/**
|
|
27886
27911
|
* Record a message in the cloud session.
|
|
@@ -27891,7 +27916,7 @@ var init_http_backend = __esm({
|
|
|
27891
27916
|
* the input record to construct a full MessageRecord.
|
|
27892
27917
|
*/
|
|
27893
27918
|
async recordMessage(record2) {
|
|
27894
|
-
const
|
|
27919
|
+
const path21 = PATHS.messages.replace(":sessionId", this.sessionId);
|
|
27895
27920
|
const body = {
|
|
27896
27921
|
fromPeerId: record2.fromPeerId,
|
|
27897
27922
|
toPeerId: record2.toPeerId,
|
|
@@ -27900,10 +27925,21 @@ var init_http_backend = __esm({
|
|
|
27900
27925
|
if (record2.isRelayEcho === true) {
|
|
27901
27926
|
body.isRelayEcho = true;
|
|
27902
27927
|
}
|
|
27928
|
+
if (await relaySupports(this.http, "message-delivery-fields")) {
|
|
27929
|
+
if (record2.success === false) {
|
|
27930
|
+
body.success = false;
|
|
27931
|
+
}
|
|
27932
|
+
if (record2.error != null) {
|
|
27933
|
+
body.error = record2.error;
|
|
27934
|
+
}
|
|
27935
|
+
if (record2.durationMs != null) {
|
|
27936
|
+
body.durationMs = record2.durationMs;
|
|
27937
|
+
}
|
|
27938
|
+
}
|
|
27903
27939
|
if (record2.attachments && record2.attachments.length > 0) {
|
|
27904
27940
|
body.attachments = record2.attachments;
|
|
27905
27941
|
}
|
|
27906
|
-
const resp = await this.http.post(
|
|
27942
|
+
const resp = await this.http.post(path21, body);
|
|
27907
27943
|
return {
|
|
27908
27944
|
...record2,
|
|
27909
27945
|
id: resp.id,
|
|
@@ -27917,7 +27953,7 @@ var init_http_backend = __esm({
|
|
|
27917
27953
|
* Supports optional peerId filter and limit (default 300).
|
|
27918
27954
|
*/
|
|
27919
27955
|
async getHistory(peerId, limit, includeRelayEchoes, metadataOnly, dedupInferredEchoes) {
|
|
27920
|
-
const
|
|
27956
|
+
const path21 = PATHS.messages.replace(":sessionId", this.sessionId);
|
|
27921
27957
|
const query = {};
|
|
27922
27958
|
if (peerId) {
|
|
27923
27959
|
query.peerId = peerId;
|
|
@@ -27932,7 +27968,7 @@ var init_http_backend = __esm({
|
|
|
27932
27968
|
if (dedupInferredEchoes) {
|
|
27933
27969
|
query.dedupInferredEchoes = "true";
|
|
27934
27970
|
}
|
|
27935
|
-
const result = await this.http.get(
|
|
27971
|
+
const result = await this.http.get(path21, query);
|
|
27936
27972
|
return result.messages;
|
|
27937
27973
|
}
|
|
27938
27974
|
/**
|
|
@@ -27943,12 +27979,12 @@ var init_http_backend = __esm({
|
|
|
27943
27979
|
* so that get-history shows the actual response (not null).
|
|
27944
27980
|
*/
|
|
27945
27981
|
async updateMessageResponse(messageId, response, durationMs, pending) {
|
|
27946
|
-
const
|
|
27982
|
+
const path21 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
|
|
27947
27983
|
const body = { response, durationMs };
|
|
27948
27984
|
if (pending === true) {
|
|
27949
27985
|
body.pending = true;
|
|
27950
27986
|
}
|
|
27951
|
-
await this.http.patch(
|
|
27987
|
+
await this.http.patch(path21, body);
|
|
27952
27988
|
}
|
|
27953
27989
|
/**
|
|
27954
27990
|
* Validate a session.
|
|
@@ -28090,8 +28126,8 @@ var init_http_client = __esm({
|
|
|
28090
28126
|
* Perform an authenticated GET request.
|
|
28091
28127
|
* Appends query parameters to the URL if provided.
|
|
28092
28128
|
*/
|
|
28093
|
-
async get(
|
|
28094
|
-
const url = new URL(
|
|
28129
|
+
async get(path21, query) {
|
|
28130
|
+
const url = new URL(path21, this.baseUrl);
|
|
28095
28131
|
if (query) {
|
|
28096
28132
|
for (const [k, v] of Object.entries(query)) {
|
|
28097
28133
|
url.searchParams.set(k, v);
|
|
@@ -28106,8 +28142,8 @@ var init_http_client = __esm({
|
|
|
28106
28142
|
/**
|
|
28107
28143
|
* Perform an authenticated POST request with a JSON body.
|
|
28108
28144
|
*/
|
|
28109
|
-
async post(
|
|
28110
|
-
const url = new URL(
|
|
28145
|
+
async post(path21, body) {
|
|
28146
|
+
const url = new URL(path21, this.baseUrl);
|
|
28111
28147
|
const resp = await this.fetchWithTimeout(url, {
|
|
28112
28148
|
method: "POST",
|
|
28113
28149
|
headers: this.headers(),
|
|
@@ -28125,8 +28161,8 @@ var init_http_client = __esm({
|
|
|
28125
28161
|
* Perform an authenticated PATCH request with a JSON body.
|
|
28126
28162
|
* Used to update existing resources (e.g., message response fields).
|
|
28127
28163
|
*/
|
|
28128
|
-
async patch(
|
|
28129
|
-
const url = new URL(
|
|
28164
|
+
async patch(path21, body) {
|
|
28165
|
+
const url = new URL(path21, this.baseUrl);
|
|
28130
28166
|
const resp = await this.fetchWithTimeout(url, {
|
|
28131
28167
|
method: "PATCH",
|
|
28132
28168
|
headers: this.headers(),
|
|
@@ -28151,8 +28187,8 @@ var init_http_client = __esm({
|
|
|
28151
28187
|
* "Malformed JSON in request body" 400 from cogent-server <=3.1.2 when
|
|
28152
28188
|
* any client (including third-party tools) DELETEd a peer without a body.
|
|
28153
28189
|
*/
|
|
28154
|
-
async delete(
|
|
28155
|
-
const url = new URL(
|
|
28190
|
+
async delete(path21, body) {
|
|
28191
|
+
const url = new URL(path21, this.baseUrl);
|
|
28156
28192
|
const headers = {
|
|
28157
28193
|
"Authorization": `Bearer ${this.token}`
|
|
28158
28194
|
};
|
|
@@ -32335,14 +32371,14 @@ var init_ws_client = __esm({
|
|
|
32335
32371
|
if (this.pollTimer) return;
|
|
32336
32372
|
const poll = async () => {
|
|
32337
32373
|
try {
|
|
32338
|
-
const
|
|
32374
|
+
const path21 = `/api/sessions/${this.opts.sessionId}/poll`;
|
|
32339
32375
|
const query = {
|
|
32340
32376
|
peerId: this.opts.peerId
|
|
32341
32377
|
};
|
|
32342
32378
|
if (this.lastMessageId) {
|
|
32343
32379
|
query.lastMessageId = this.lastMessageId;
|
|
32344
32380
|
}
|
|
32345
|
-
const result = await this.opts.http.get(
|
|
32381
|
+
const result = await this.opts.http.get(path21, query);
|
|
32346
32382
|
if (result.messages && result.messages.length > 0) {
|
|
32347
32383
|
this.opts.onMessages(result.messages);
|
|
32348
32384
|
this.lastMessageId = result.messages[result.messages.length - 1].id;
|
|
@@ -33061,6 +33097,112 @@ var init_exec_remote = __esm({
|
|
|
33061
33097
|
}
|
|
33062
33098
|
});
|
|
33063
33099
|
|
|
33100
|
+
// src/services/wake-log.ts
|
|
33101
|
+
import { execFileSync } from "node:child_process";
|
|
33102
|
+
import crypto4 from "node:crypto";
|
|
33103
|
+
import fs13 from "node:fs";
|
|
33104
|
+
import os9 from "node:os";
|
|
33105
|
+
import path14 from "node:path";
|
|
33106
|
+
function hashCwd2(cwd) {
|
|
33107
|
+
return crypto4.createHash("sha256").update(path14.resolve(cwd)).digest("hex").slice(0, 16);
|
|
33108
|
+
}
|
|
33109
|
+
function wakeLogPath(cwd) {
|
|
33110
|
+
return path14.join(os9.homedir(), ".cogent", "wake-log", `${hashCwd2(cwd)}.jsonl`);
|
|
33111
|
+
}
|
|
33112
|
+
function git(cwd, args) {
|
|
33113
|
+
return execFileSync("git", ["-C", cwd, ...args], {
|
|
33114
|
+
encoding: "utf-8",
|
|
33115
|
+
timeout: 15e3,
|
|
33116
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
33117
|
+
});
|
|
33118
|
+
}
|
|
33119
|
+
function captureTreeState(cwd) {
|
|
33120
|
+
try {
|
|
33121
|
+
const head = git(cwd, ["rev-parse", "HEAD"]).trim();
|
|
33122
|
+
const entries = {};
|
|
33123
|
+
const raw = git(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
33124
|
+
const fields = raw.split("\0").filter((f) => f.length > 0);
|
|
33125
|
+
for (let i = 0; i < fields.length; i++) {
|
|
33126
|
+
const field = fields[i];
|
|
33127
|
+
const status = field.slice(0, 2);
|
|
33128
|
+
entries[field.slice(3)] = status;
|
|
33129
|
+
if (status[0] === "R" || status[0] === "C") i++;
|
|
33130
|
+
}
|
|
33131
|
+
return { head, entries };
|
|
33132
|
+
} catch {
|
|
33133
|
+
return null;
|
|
33134
|
+
}
|
|
33135
|
+
}
|
|
33136
|
+
function diffTreeState(before, after) {
|
|
33137
|
+
if (!before || !after) return null;
|
|
33138
|
+
const changed = /* @__PURE__ */ new Set();
|
|
33139
|
+
if (before.head !== after.head) changed.add("HEAD");
|
|
33140
|
+
for (const [file, status] of Object.entries(after.entries)) {
|
|
33141
|
+
if (before.entries[file] !== status) changed.add(file);
|
|
33142
|
+
}
|
|
33143
|
+
for (const file of Object.keys(before.entries)) {
|
|
33144
|
+
if (!(file in after.entries)) changed.add(file);
|
|
33145
|
+
}
|
|
33146
|
+
return [...changed].sort();
|
|
33147
|
+
}
|
|
33148
|
+
function preview(message) {
|
|
33149
|
+
return message.length > PREVIEW_LIMIT ? `${message.slice(0, PREVIEW_LIMIT)}\u2026` : message;
|
|
33150
|
+
}
|
|
33151
|
+
function appendWakeRecord(record2, cwd) {
|
|
33152
|
+
const target = wakeLogPath(cwd);
|
|
33153
|
+
try {
|
|
33154
|
+
fs13.mkdirSync(path14.dirname(target), { recursive: true });
|
|
33155
|
+
const row = {
|
|
33156
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33157
|
+
...record2,
|
|
33158
|
+
messagePreview: preview(record2.messagePreview ?? "")
|
|
33159
|
+
};
|
|
33160
|
+
fs13.appendFileSync(target, `${JSON.stringify(row)}
|
|
33161
|
+
`);
|
|
33162
|
+
pruneIfNeeded(target);
|
|
33163
|
+
} catch {
|
|
33164
|
+
}
|
|
33165
|
+
}
|
|
33166
|
+
function pruneIfNeeded(target) {
|
|
33167
|
+
try {
|
|
33168
|
+
const lines = fs13.readFileSync(target, "utf-8").split("\n").filter((l) => l.length > 0);
|
|
33169
|
+
if (lines.length <= WAKE_LOG_MAX_RECORDS) return;
|
|
33170
|
+
const kept = lines.slice(lines.length - WAKE_LOG_MAX_RECORDS);
|
|
33171
|
+
const tmp = `${target}.tmp-${process.pid}`;
|
|
33172
|
+
fs13.writeFileSync(tmp, `${kept.join("\n")}
|
|
33173
|
+
`);
|
|
33174
|
+
fs13.renameSync(tmp, target);
|
|
33175
|
+
} catch {
|
|
33176
|
+
}
|
|
33177
|
+
}
|
|
33178
|
+
function readWakeLog(cwd, limit) {
|
|
33179
|
+
let lines;
|
|
33180
|
+
try {
|
|
33181
|
+
lines = fs13.readFileSync(wakeLogPath(cwd), "utf-8").split("\n").filter((l) => l.length > 0);
|
|
33182
|
+
} catch {
|
|
33183
|
+
return [];
|
|
33184
|
+
}
|
|
33185
|
+
const rows = [];
|
|
33186
|
+
for (const line of lines) {
|
|
33187
|
+
try {
|
|
33188
|
+
const parsed = JSON.parse(line);
|
|
33189
|
+
if (parsed && typeof parsed === "object" && typeof parsed.messageId === "string") {
|
|
33190
|
+
rows.push(parsed);
|
|
33191
|
+
}
|
|
33192
|
+
} catch {
|
|
33193
|
+
}
|
|
33194
|
+
}
|
|
33195
|
+
return typeof limit === "number" && limit > 0 ? rows.slice(-limit) : rows;
|
|
33196
|
+
}
|
|
33197
|
+
var WAKE_LOG_MAX_RECORDS, PREVIEW_LIMIT;
|
|
33198
|
+
var init_wake_log = __esm({
|
|
33199
|
+
"src/services/wake-log.ts"() {
|
|
33200
|
+
"use strict";
|
|
33201
|
+
WAKE_LOG_MAX_RECORDS = 500;
|
|
33202
|
+
PREVIEW_LIMIT = 500;
|
|
33203
|
+
}
|
|
33204
|
+
});
|
|
33205
|
+
|
|
33064
33206
|
// src/services/session-resolver.ts
|
|
33065
33207
|
function decideResolution(candidates, pinnedSessionId, pinnedExists, marginMs) {
|
|
33066
33208
|
const sorted = [...candidates].sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
@@ -33148,15 +33290,15 @@ var init_session_resolver = __esm({
|
|
|
33148
33290
|
});
|
|
33149
33291
|
|
|
33150
33292
|
// src/services/wake-inflight.ts
|
|
33151
|
-
import
|
|
33152
|
-
import
|
|
33153
|
-
import
|
|
33154
|
-
import
|
|
33155
|
-
function
|
|
33156
|
-
return
|
|
33293
|
+
import crypto5 from "node:crypto";
|
|
33294
|
+
import fs14 from "node:fs";
|
|
33295
|
+
import os10 from "node:os";
|
|
33296
|
+
import path15 from "node:path";
|
|
33297
|
+
function hashCwd3(cwd) {
|
|
33298
|
+
return crypto5.createHash("sha256").update(path15.resolve(cwd)).digest("hex").slice(0, 16);
|
|
33157
33299
|
}
|
|
33158
33300
|
function wakeInflightPath(peerId, cwd = process.cwd()) {
|
|
33159
|
-
return
|
|
33301
|
+
return path15.join(os10.homedir(), ".cogent", "wake-inflight", `${hashCwd3(cwd)}.${safe(peerId)}.json`);
|
|
33160
33302
|
}
|
|
33161
33303
|
function ttlMs() {
|
|
33162
33304
|
let timeout = 3e5;
|
|
@@ -33169,17 +33311,17 @@ function ttlMs() {
|
|
|
33169
33311
|
}
|
|
33170
33312
|
function readFileSafe(p) {
|
|
33171
33313
|
try {
|
|
33172
|
-
const parsed = JSON.parse(
|
|
33314
|
+
const parsed = JSON.parse(fs14.readFileSync(p, "utf-8"));
|
|
33173
33315
|
return parsed && typeof parsed === "object" && parsed.messages ? parsed : { messages: {} };
|
|
33174
33316
|
} catch {
|
|
33175
33317
|
return { messages: {} };
|
|
33176
33318
|
}
|
|
33177
33319
|
}
|
|
33178
33320
|
function writeAtomic(p, data) {
|
|
33179
|
-
|
|
33321
|
+
fs14.mkdirSync(path15.dirname(p), { recursive: true });
|
|
33180
33322
|
const tmp = `${p}.${process.pid}.tmp`;
|
|
33181
|
-
|
|
33182
|
-
|
|
33323
|
+
fs14.writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
33324
|
+
fs14.renameSync(tmp, p);
|
|
33183
33325
|
}
|
|
33184
33326
|
function prune(f, now) {
|
|
33185
33327
|
for (const [id, rec] of Object.entries(f.messages)) {
|
|
@@ -33199,6 +33341,15 @@ function markWakeInFlight(peerId, messageId, traceId, cwd = process.cwd()) {
|
|
|
33199
33341
|
logger.debug(`wake-inflight: could not mark ${messageId}: ${err}`);
|
|
33200
33342
|
}
|
|
33201
33343
|
}
|
|
33344
|
+
function listWakeInFlight(peerId, cwd = process.cwd()) {
|
|
33345
|
+
try {
|
|
33346
|
+
const cur = readFileSafe(wakeInflightPath(peerId, cwd));
|
|
33347
|
+
const now = Date.now();
|
|
33348
|
+
return Object.entries(cur.messages).filter(([, e]) => e.expiresAt > now).map(([messageId, e]) => ({ messageId, ...e })).sort((a, b) => a.startedAt - b.startedAt);
|
|
33349
|
+
} catch {
|
|
33350
|
+
return [];
|
|
33351
|
+
}
|
|
33352
|
+
}
|
|
33202
33353
|
function clearWakeInFlight(peerId, messageId, cwd = process.cwd()) {
|
|
33203
33354
|
try {
|
|
33204
33355
|
const p = wakeInflightPath(peerId, cwd);
|
|
@@ -33350,11 +33501,12 @@ function truncate(s) {
|
|
|
33350
33501
|
const flat = s.replace(/\s+/g, " ").trim();
|
|
33351
33502
|
return flat.length > DIAGNOSTIC_MAX ? `${flat.slice(0, DIAGNOSTIC_MAX - 1)}\u2026` : flat;
|
|
33352
33503
|
}
|
|
33353
|
-
var RELAY_STATUS, STATUS_MARKER_RE, DIAGNOSTIC_MAX, AutoRelayService, autoRelay;
|
|
33504
|
+
var RELAY_STATUS, STATUS_MARKER_RE, DIAGNOSTIC_MAX, EARLY_ACK_TTL_MS, EARLY_ACK_MAX, AutoRelayService, autoRelay;
|
|
33354
33505
|
var init_auto_relay = __esm({
|
|
33355
33506
|
"src/services/auto-relay.ts"() {
|
|
33356
33507
|
"use strict";
|
|
33357
33508
|
init_exec_remote();
|
|
33509
|
+
init_wake_log();
|
|
33358
33510
|
init_session_resolver();
|
|
33359
33511
|
init_config();
|
|
33360
33512
|
init_backend();
|
|
@@ -33371,6 +33523,8 @@ var init_auto_relay = __esm({
|
|
|
33371
33523
|
};
|
|
33372
33524
|
STATUS_MARKER_RE = /^\[cogent:(queued|processing|delivered|needs-manual|failed)\]/;
|
|
33373
33525
|
DIAGNOSTIC_MAX = 220;
|
|
33526
|
+
EARLY_ACK_TTL_MS = 12e4;
|
|
33527
|
+
EARLY_ACK_MAX = 256;
|
|
33374
33528
|
AutoRelayService = class {
|
|
33375
33529
|
localPeerId = null;
|
|
33376
33530
|
localSessionId = null;
|
|
@@ -33417,7 +33571,24 @@ var init_auto_relay = __esm({
|
|
|
33417
33571
|
// so the first send-message tool call returned literal "Superseded by
|
|
33418
33572
|
// newer send_message call" instead of the actual peer response.
|
|
33419
33573
|
responseWaiters = /* @__PURE__ */ new Map();
|
|
33574
|
+
/**
|
|
33575
|
+
* Pending delivery-ack waiters, keyed by MESSAGE ID.
|
|
33576
|
+
*
|
|
33577
|
+
* 🔴 AUD-002. This used to be keyed by PEER ID, so a peer could have only one outstanding
|
|
33578
|
+
* waiter and a second concurrent send displaced the first — resolving it, so the first send
|
|
33579
|
+
* reported "delivered" with no acknowledgment from anyone. Delivery acknowledgment is the one
|
|
33580
|
+
* claim this system must never fabricate.
|
|
33581
|
+
*/
|
|
33420
33582
|
deliveryAckWaiters = /* @__PURE__ */ new Map();
|
|
33583
|
+
/**
|
|
33584
|
+
* Acks that arrived before their waiter existed, keyed by message id.
|
|
33585
|
+
*
|
|
33586
|
+
* The recipient acknowledges over WS as soon as it receives the message, which can beat our own
|
|
33587
|
+
* POST response. Waiters used to be registered BEFORE the send to dodge that race, which is what
|
|
33588
|
+
* forced peer-keying. Buffering the early ack instead lets registration happen after the message
|
|
33589
|
+
* id is known, so correlation stays exact. Entries are consumed once and expire.
|
|
33590
|
+
*/
|
|
33591
|
+
recentAcks = /* @__PURE__ */ new Map();
|
|
33421
33592
|
sendDeliveryAckFn = null;
|
|
33422
33593
|
/**
|
|
33423
33594
|
* Set the local peer info. Called from register-peer tool after
|
|
@@ -33586,12 +33757,6 @@ var init_auto_relay = __esm({
|
|
|
33586
33757
|
cancelResponseWaiter(fromPeerId) {
|
|
33587
33758
|
this._cancelWaiter(fromPeerId);
|
|
33588
33759
|
}
|
|
33589
|
-
/**
|
|
33590
|
-
* Cancel only the active delivery ack waiter for a peer.
|
|
33591
|
-
*/
|
|
33592
|
-
cancelDeliveryAckWaiter(fromPeerId) {
|
|
33593
|
-
this._cancelDeliveryAckWaiter(fromPeerId);
|
|
33594
|
-
}
|
|
33595
33760
|
/**
|
|
33596
33761
|
* Cancel ALL pending response waiters for a peer by rejecting them with
|
|
33597
33762
|
* a clear error. Used by clearExpectation()/cancelResponseWaiter() to
|
|
@@ -33614,39 +33779,73 @@ var init_auto_relay = __esm({
|
|
|
33614
33779
|
* resolves when the target peer's MCP sends a delivery_ack frame, or
|
|
33615
33780
|
* rejects on timeout (recipient likely offline).
|
|
33616
33781
|
*/
|
|
33617
|
-
waitForDeliveryAck(fromPeerId, timeoutMs) {
|
|
33782
|
+
waitForDeliveryAck(fromPeerId, messageId, timeoutMs) {
|
|
33783
|
+
if (this.recentAcks.delete(messageId)) {
|
|
33784
|
+
logger.debug(`Auto-relay: delivery ack for ${messageId} was already buffered`);
|
|
33785
|
+
return Promise.resolve();
|
|
33786
|
+
}
|
|
33618
33787
|
return new Promise((resolve, reject) => {
|
|
33619
|
-
this._cancelDeliveryAckWaiter(fromPeerId);
|
|
33620
33788
|
const timer = setTimeout(() => {
|
|
33621
|
-
this.deliveryAckWaiters.delete(
|
|
33622
|
-
reject(
|
|
33789
|
+
this.deliveryAckWaiters.delete(messageId);
|
|
33790
|
+
reject(
|
|
33791
|
+
new Error(
|
|
33792
|
+
`Delivery ack timeout from ${fromPeerId} for ${messageId} after ${timeoutMs}ms`
|
|
33793
|
+
)
|
|
33794
|
+
);
|
|
33623
33795
|
}, timeoutMs);
|
|
33624
|
-
this.deliveryAckWaiters.set(
|
|
33796
|
+
this.deliveryAckWaiters.set(messageId, { peerId: fromPeerId, resolve, reject, timer });
|
|
33625
33797
|
});
|
|
33626
33798
|
}
|
|
33627
33799
|
/**
|
|
33628
|
-
*
|
|
33629
|
-
*
|
|
33800
|
+
* Settle the waiter for exactly this message.
|
|
33801
|
+
*
|
|
33802
|
+
* 🔴 Correlation is by MESSAGE ID, and the acknowledging peer must match the peer we sent to.
|
|
33803
|
+
* The frame has carried `messageId` all along (src/cloud/ws-client.ts:52); startup.ts used to
|
|
33804
|
+
* drop it and resolve by peer, so any ack settled any outstanding send to that peer — and an ack
|
|
33805
|
+
* naming a peer the sender never messaged could settle a waiter outright (AUD-003).
|
|
33806
|
+
*
|
|
33807
|
+
* An ack with no matching waiter is BUFFERED rather than discarded: the recipient can legitimately
|
|
33808
|
+
* acknowledge before our POST response returns.
|
|
33630
33809
|
*/
|
|
33631
|
-
resolveDeliveryAck(fromPeerId) {
|
|
33632
|
-
const waiter = this.deliveryAckWaiters.get(
|
|
33810
|
+
resolveDeliveryAck(fromPeerId, messageId) {
|
|
33811
|
+
const waiter = this.deliveryAckWaiters.get(messageId);
|
|
33633
33812
|
if (waiter) {
|
|
33813
|
+
if (waiter.peerId !== fromPeerId) {
|
|
33814
|
+
logger.warn(
|
|
33815
|
+
`Auto-relay: IGNORED delivery ack for ${messageId} \u2014 claimed by '${fromPeerId}' but that message was sent to '${waiter.peerId}'`
|
|
33816
|
+
);
|
|
33817
|
+
return;
|
|
33818
|
+
}
|
|
33634
33819
|
clearTimeout(waiter.timer);
|
|
33635
|
-
this.deliveryAckWaiters.delete(
|
|
33820
|
+
this.deliveryAckWaiters.delete(messageId);
|
|
33636
33821
|
waiter.resolve();
|
|
33637
|
-
logger.debug(`Auto-relay: delivery ack received from ${fromPeerId}`);
|
|
33822
|
+
logger.debug(`Auto-relay: delivery ack received from ${fromPeerId} for ${messageId}`);
|
|
33823
|
+
return;
|
|
33638
33824
|
}
|
|
33825
|
+
this._bufferEarlyAck(messageId);
|
|
33826
|
+
}
|
|
33827
|
+
/** Remember an ack whose waiter does not exist yet, with bounded retention. */
|
|
33828
|
+
_bufferEarlyAck(messageId) {
|
|
33829
|
+
const now = Date.now();
|
|
33830
|
+
for (const [id, at] of this.recentAcks) {
|
|
33831
|
+
if (now - at > EARLY_ACK_TTL_MS) this.recentAcks.delete(id);
|
|
33832
|
+
}
|
|
33833
|
+
while (this.recentAcks.size >= EARLY_ACK_MAX) {
|
|
33834
|
+
const oldest = this.recentAcks.keys().next().value;
|
|
33835
|
+
if (oldest === void 0) break;
|
|
33836
|
+
this.recentAcks.delete(oldest);
|
|
33837
|
+
}
|
|
33838
|
+
this.recentAcks.set(messageId, now);
|
|
33839
|
+
logger.debug(`Auto-relay: buffered early delivery ack for ${messageId}`);
|
|
33639
33840
|
}
|
|
33640
|
-
/**
|
|
33641
|
-
* Cancel a pending delivery ack waiter for a peer.
|
|
33642
|
-
* Resolves the old promise so callers awaiting it don't hang forever.
|
|
33643
|
-
*/
|
|
33644
33841
|
_cancelDeliveryAckWaiter(fromPeerId) {
|
|
33645
|
-
const
|
|
33646
|
-
|
|
33647
|
-
clearTimeout(
|
|
33648
|
-
this.deliveryAckWaiters.delete(
|
|
33649
|
-
|
|
33842
|
+
for (const [messageId, waiter] of this.deliveryAckWaiters) {
|
|
33843
|
+
if (waiter.peerId !== fromPeerId) continue;
|
|
33844
|
+
clearTimeout(waiter.timer);
|
|
33845
|
+
this.deliveryAckWaiters.delete(messageId);
|
|
33846
|
+
waiter.reject(
|
|
33847
|
+
new Error(`Delivery ack wait for ${messageId} cancelled (${fromPeerId})`)
|
|
33848
|
+
);
|
|
33650
33849
|
}
|
|
33651
33850
|
}
|
|
33652
33851
|
/**
|
|
@@ -33903,6 +34102,10 @@ var init_auto_relay = __esm({
|
|
|
33903
34102
|
const traceId = msg.traceId ?? randomUUID2();
|
|
33904
34103
|
const startMs = Date.now();
|
|
33905
34104
|
let replied = false;
|
|
34105
|
+
let treeBefore = null;
|
|
34106
|
+
let wakeExitCode = null;
|
|
34107
|
+
let refused = false;
|
|
34108
|
+
let threw = false;
|
|
33906
34109
|
try {
|
|
33907
34110
|
const res = await this.refreshSessionId();
|
|
33908
34111
|
this._trace(traceId, "resolved", {
|
|
@@ -33912,6 +34115,7 @@ var init_auto_relay = __esm({
|
|
|
33912
34115
|
mtimeGapMs: res.mtimeGapMs
|
|
33913
34116
|
});
|
|
33914
34117
|
if (!res.sessionId) {
|
|
34118
|
+
refused = true;
|
|
33915
34119
|
this._trace(traceId, "refused", {
|
|
33916
34120
|
decision: res.decision,
|
|
33917
34121
|
candidateCount: res.candidateCount
|
|
@@ -33940,6 +34144,7 @@ var init_auto_relay = __esm({
|
|
|
33940
34144
|
decision: res.decision,
|
|
33941
34145
|
confidence: res.confidence
|
|
33942
34146
|
});
|
|
34147
|
+
treeBefore = captureTreeState(this.localCwd);
|
|
33943
34148
|
const result = await execRemote(
|
|
33944
34149
|
getConfig().COGENT_PLATFORM,
|
|
33945
34150
|
this.localSessionId,
|
|
@@ -33952,6 +34157,7 @@ var init_auto_relay = __esm({
|
|
|
33952
34157
|
{ idempotencyKey: msg.id }
|
|
33953
34158
|
);
|
|
33954
34159
|
const durationMs = Date.now() - startMs;
|
|
34160
|
+
wakeExitCode = result.exitCode;
|
|
33955
34161
|
if (result.exitCode === 0 && result.stdout) {
|
|
33956
34162
|
await this._relayCapturedReply(msg, result, traceId, durationMs, false);
|
|
33957
34163
|
replied = true;
|
|
@@ -34017,6 +34223,7 @@ var init_auto_relay = __esm({
|
|
|
34017
34223
|
}
|
|
34018
34224
|
} catch (err) {
|
|
34019
34225
|
const durationMs = Date.now() - startMs;
|
|
34226
|
+
threw = true;
|
|
34020
34227
|
this._trace(traceId, "failed", { threw: true, durationMs });
|
|
34021
34228
|
logger.error(`Auto-relay: execRemote threw after ${durationMs}ms: ${err}`);
|
|
34022
34229
|
if (!replied) {
|
|
@@ -34024,6 +34231,48 @@ var init_auto_relay = __esm({
|
|
|
34024
34231
|
stderr: `EXEC_THREW: ${err}`
|
|
34025
34232
|
});
|
|
34026
34233
|
}
|
|
34234
|
+
} finally {
|
|
34235
|
+
this._recordWake(msg, traceId, {
|
|
34236
|
+
outcome: threw ? "threw" : refused ? "refused" : replied ? "replied" : "failed",
|
|
34237
|
+
exitCode: wakeExitCode,
|
|
34238
|
+
durationMs: Date.now() - startMs,
|
|
34239
|
+
treeBefore
|
|
34240
|
+
});
|
|
34241
|
+
}
|
|
34242
|
+
}
|
|
34243
|
+
/**
|
|
34244
|
+
* Write the durable wake record. Wrapped whole: visibility must never be able to fail a
|
|
34245
|
+
* wake that would otherwise have succeeded, so every error here is swallowed and logged.
|
|
34246
|
+
*/
|
|
34247
|
+
_recordWake(msg, traceId, outcome) {
|
|
34248
|
+
try {
|
|
34249
|
+
const cwd = this.localCwd;
|
|
34250
|
+
if (!cwd) return;
|
|
34251
|
+
const filesChanged = outcome.treeBefore ? diffTreeState(outcome.treeBefore, captureTreeState(cwd)) : null;
|
|
34252
|
+
appendWakeRecord(
|
|
34253
|
+
{
|
|
34254
|
+
traceId,
|
|
34255
|
+
messageId: msg.id,
|
|
34256
|
+
fromPeerId: msg.fromPeerId,
|
|
34257
|
+
toPeerId: this.localPeerId ?? "",
|
|
34258
|
+
cwd,
|
|
34259
|
+
sessionId: this.localSessionId ?? "",
|
|
34260
|
+
platform: getConfig().COGENT_PLATFORM,
|
|
34261
|
+
outcome: outcome.outcome,
|
|
34262
|
+
exitCode: outcome.exitCode,
|
|
34263
|
+
durationMs: outcome.durationMs,
|
|
34264
|
+
filesChanged,
|
|
34265
|
+
messagePreview: msg.message ?? ""
|
|
34266
|
+
},
|
|
34267
|
+
cwd
|
|
34268
|
+
);
|
|
34269
|
+
if (filesChanged && filesChanged.length > 0) {
|
|
34270
|
+
logger.info(
|
|
34271
|
+
`Auto-relay: wake for message ${msg.id} from ${msg.fromPeerId} changed ${filesChanged.length} path(s): ${filesChanged.slice(0, 10).join(", ")}`
|
|
34272
|
+
);
|
|
34273
|
+
}
|
|
34274
|
+
} catch (err) {
|
|
34275
|
+
logger.warn(`Auto-relay: wake-log record failed (wake itself unaffected): ${err}`);
|
|
34027
34276
|
}
|
|
34028
34277
|
}
|
|
34029
34278
|
/**
|
|
@@ -34149,15 +34398,18 @@ var init_auto_relay = __esm({
|
|
|
34149
34398
|
for (const [, queue] of this.responseWaiters) {
|
|
34150
34399
|
for (const waiter of queue) {
|
|
34151
34400
|
clearTimeout(waiter.timer);
|
|
34152
|
-
waiter.
|
|
34401
|
+
waiter.reject(new Error("Auto-relay shut down while waiting for a response"));
|
|
34153
34402
|
}
|
|
34154
34403
|
}
|
|
34155
34404
|
this.responseWaiters.clear();
|
|
34156
|
-
for (const [, waiter] of this.deliveryAckWaiters) {
|
|
34405
|
+
for (const [messageId, waiter] of this.deliveryAckWaiters) {
|
|
34157
34406
|
clearTimeout(waiter.timer);
|
|
34158
|
-
waiter.
|
|
34407
|
+
waiter.reject(
|
|
34408
|
+
new Error(`Auto-relay shut down while waiting for delivery ack (${messageId})`)
|
|
34409
|
+
);
|
|
34159
34410
|
}
|
|
34160
34411
|
this.deliveryAckWaiters.clear();
|
|
34412
|
+
this.recentAcks.clear();
|
|
34161
34413
|
this.queue = [];
|
|
34162
34414
|
this.awarenessOnly.clear();
|
|
34163
34415
|
this.localPeerId = null;
|
|
@@ -34413,9 +34665,9 @@ __export(startup_exports, {
|
|
|
34413
34665
|
setInboxNotifier: () => setInboxNotifier,
|
|
34414
34666
|
triggerReRegistration: () => triggerReRegistration
|
|
34415
34667
|
});
|
|
34416
|
-
import
|
|
34417
|
-
import
|
|
34418
|
-
import
|
|
34668
|
+
import os11 from "node:os";
|
|
34669
|
+
import path16 from "node:path";
|
|
34670
|
+
import fs15 from "node:fs/promises";
|
|
34419
34671
|
import readline2 from "node:readline";
|
|
34420
34672
|
import { execFile as execFile5 } from "node:child_process";
|
|
34421
34673
|
import { promisify as promisify4 } from "node:util";
|
|
@@ -34433,7 +34685,7 @@ function bindInboxNotifier() {
|
|
|
34433
34685
|
}
|
|
34434
34686
|
async function loadPersistedConfig() {
|
|
34435
34687
|
try {
|
|
34436
|
-
const raw = await
|
|
34688
|
+
const raw = await fs15.readFile(PERSIST_PATH, "utf-8");
|
|
34437
34689
|
return JSON.parse(raw);
|
|
34438
34690
|
} catch {
|
|
34439
34691
|
return {};
|
|
@@ -34441,7 +34693,7 @@ async function loadPersistedConfig() {
|
|
|
34441
34693
|
}
|
|
34442
34694
|
async function savePersistedConfig(config2) {
|
|
34443
34695
|
try {
|
|
34444
|
-
await
|
|
34696
|
+
await fs15.writeFile(PERSIST_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
34445
34697
|
} catch (err) {
|
|
34446
34698
|
const msg = err instanceof Error ? err.message : String(err);
|
|
34447
34699
|
logger.warn(`Could not save config to ${PERSIST_PATH}: ${msg}`);
|
|
@@ -34476,11 +34728,11 @@ First run detected. Where should cogent-bridge store its data?
|
|
|
34476
34728
|
});
|
|
34477
34729
|
}
|
|
34478
34730
|
async function validateStateDir(statePath) {
|
|
34479
|
-
await
|
|
34480
|
-
const testFile =
|
|
34731
|
+
await fs15.mkdir(statePath, { recursive: true });
|
|
34732
|
+
const testFile = path16.join(statePath, ".write-test");
|
|
34481
34733
|
try {
|
|
34482
|
-
await
|
|
34483
|
-
await
|
|
34734
|
+
await fs15.writeFile(testFile, "test", "utf-8");
|
|
34735
|
+
await fs15.unlink(testFile);
|
|
34484
34736
|
} catch {
|
|
34485
34737
|
throw new BridgeError(
|
|
34486
34738
|
"STARTUP_FAILED" /* STARTUP_FAILED */,
|
|
@@ -34580,7 +34832,7 @@ function createCloudWsClient(endpoint, sessionId, token, http, inbox, pollInterv
|
|
|
34580
34832
|
logger.warn(`WebSocket error frame: ${err.code}: ${err.message}`);
|
|
34581
34833
|
},
|
|
34582
34834
|
onDeliveryAck: (payload) => {
|
|
34583
|
-
autoRelay.resolveDeliveryAck(payload.peerId);
|
|
34835
|
+
autoRelay.resolveDeliveryAck(payload.peerId, payload.messageId);
|
|
34584
34836
|
},
|
|
34585
34837
|
onPeerEvicted: () => {
|
|
34586
34838
|
triggerReRegistration().catch((err) => {
|
|
@@ -34667,10 +34919,10 @@ async function runStartup() {
|
|
|
34667
34919
|
if (envStatePath) {
|
|
34668
34920
|
statePath = envStatePath;
|
|
34669
34921
|
} else {
|
|
34670
|
-
const defaultPath =
|
|
34922
|
+
const defaultPath = path16.join(os11.homedir(), ".cogent");
|
|
34671
34923
|
statePath = await firstRunPrompt(defaultPath);
|
|
34672
34924
|
}
|
|
34673
|
-
if (!envStatePath && statePath !==
|
|
34925
|
+
if (!envStatePath && statePath !== path16.join(os11.homedir(), ".cogent")) {
|
|
34674
34926
|
process.env.COGENT_STATE_PATH = statePath;
|
|
34675
34927
|
} else if (!envStatePath) {
|
|
34676
34928
|
process.env.COGENT_STATE_PATH = statePath;
|
|
@@ -34734,7 +34986,7 @@ async function runStartup() {
|
|
|
34734
34986
|
}
|
|
34735
34987
|
initLogger(
|
|
34736
34988
|
config2.COGENT_LOG_LEVEL,
|
|
34737
|
-
|
|
34989
|
+
path16.join(config2.COGENT_STATE_PATH, "logs")
|
|
34738
34990
|
);
|
|
34739
34991
|
if (isCloud && effectiveSessionId && effectiveToken) {
|
|
34740
34992
|
cloudInbox = new MessageInbox();
|
|
@@ -34796,7 +35048,7 @@ var init_startup = __esm({
|
|
|
34796
35048
|
init_auto_relay();
|
|
34797
35049
|
UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
34798
35050
|
execFileAsync3 = promisify4(execFile5);
|
|
34799
|
-
PERSIST_PATH =
|
|
35051
|
+
PERSIST_PATH = path16.join(os11.homedir(), ".cogent-config.json");
|
|
34800
35052
|
legacyWarnEmitted = false;
|
|
34801
35053
|
cloudWsClient = null;
|
|
34802
35054
|
cloudInbox = null;
|
|
@@ -34810,13 +35062,13 @@ var init_startup = __esm({
|
|
|
34810
35062
|
});
|
|
34811
35063
|
|
|
34812
35064
|
// src/cloud/mail-credential-store.ts
|
|
34813
|
-
import
|
|
34814
|
-
import
|
|
34815
|
-
import
|
|
34816
|
-
import
|
|
35065
|
+
import crypto6 from "node:crypto";
|
|
35066
|
+
import fs16 from "node:fs/promises";
|
|
35067
|
+
import path17 from "node:path";
|
|
35068
|
+
import os12 from "node:os";
|
|
34817
35069
|
function defaultMailCredentialPath(cwd = process.cwd()) {
|
|
34818
|
-
const hash =
|
|
34819
|
-
return
|
|
35070
|
+
const hash = crypto6.createHash("sha256").update(path17.resolve(cwd)).digest("hex").slice(0, 16);
|
|
35071
|
+
return path17.join(os12.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
|
|
34820
35072
|
}
|
|
34821
35073
|
function resolveMailCredentialPath(credentialPath) {
|
|
34822
35074
|
if (credentialPath) return credentialPath;
|
|
@@ -34827,16 +35079,16 @@ function resolveMailCredentialPath(credentialPath) {
|
|
|
34827
35079
|
async function loadMailCredentials(credentialPath) {
|
|
34828
35080
|
const filePath = resolveMailCredentialPath(credentialPath);
|
|
34829
35081
|
try {
|
|
34830
|
-
return JSON.parse(await
|
|
35082
|
+
return JSON.parse(await fs16.readFile(filePath, "utf-8"));
|
|
34831
35083
|
} catch {
|
|
34832
35084
|
return null;
|
|
34833
35085
|
}
|
|
34834
35086
|
}
|
|
34835
35087
|
async function saveMailCredentials(creds, credentialPath) {
|
|
34836
35088
|
const filePath = resolveMailCredentialPath(credentialPath);
|
|
34837
|
-
await
|
|
34838
|
-
await
|
|
34839
|
-
await
|
|
35089
|
+
await fs16.mkdir(path17.dirname(filePath), { recursive: true, mode: 448 });
|
|
35090
|
+
await fs16.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
35091
|
+
await fs16.chmod(filePath, 384);
|
|
34840
35092
|
}
|
|
34841
35093
|
async function persistProvisionedMailbox(mailbox, credentialPath) {
|
|
34842
35094
|
if (!mailbox?.address || !mailbox.password) return false;
|
|
@@ -34857,7 +35109,7 @@ async function persistProvisionedMailbox(mailbox, credentialPath) {
|
|
|
34857
35109
|
async function clearMailCredentials(credentialPath) {
|
|
34858
35110
|
const filePath = resolveMailCredentialPath(credentialPath);
|
|
34859
35111
|
try {
|
|
34860
|
-
await
|
|
35112
|
+
await fs16.unlink(filePath);
|
|
34861
35113
|
} catch (err) {
|
|
34862
35114
|
if (err.code !== "ENOENT") throw err;
|
|
34863
35115
|
}
|
|
@@ -34871,7 +35123,11 @@ var init_mail_credential_store = __esm({
|
|
|
34871
35123
|
});
|
|
34872
35124
|
|
|
34873
35125
|
// src/tools/register-peer.ts
|
|
34874
|
-
import
|
|
35126
|
+
import crypto7 from "node:crypto";
|
|
35127
|
+
function unresolvedSessionReason(r) {
|
|
35128
|
+
if (r.sessionId) return null;
|
|
35129
|
+
return r.candidateCount === 0 ? "no-candidates" : "ambiguous";
|
|
35130
|
+
}
|
|
34875
35131
|
function registerRegisterPeerTool(server) {
|
|
34876
35132
|
server.registerTool(
|
|
34877
35133
|
"cogent_register_peer",
|
|
@@ -34883,6 +35139,9 @@ function registerRegisterPeerTool(server) {
|
|
|
34883
35139
|
sessionId: import_zod3.z.string().describe("Claude Code session ID (used with --resume)"),
|
|
34884
35140
|
cwd: import_zod3.z.string().describe("Absolute path to the project working directory"),
|
|
34885
35141
|
label: import_zod3.z.string().describe("Human-readable label, e.g. 'Cogent_Backend' or 'Cogent_Frontend'"),
|
|
35142
|
+
peerSecret: import_zod3.z.string().optional().describe(
|
|
35143
|
+
"AUD-003 ownership proof. Only needed to RECLAIM a peerId whose registration was made from a different connection \u2014 e.g. this agent re-joined the channel and was issued a new token. Normally omitted: the stored credential is replayed automatically."
|
|
35144
|
+
),
|
|
34886
35145
|
mode: import_zod3.z.enum(["observer", "agent"]).optional().default("agent").describe(
|
|
34887
35146
|
'Peer participation mode. "agent" (default) auto-responds via execClaude on inbound directed messages. "observer" registers a passive watcher that receives WS frames and remains targetable but never spawns a subprocess. Useful for operators using their MCP client to monitor a channel without polluting transcript with phantom replies.'
|
|
34888
35147
|
),
|
|
@@ -34897,7 +35156,7 @@ function registerRegisterPeerTool(server) {
|
|
|
34897
35156
|
openWorldHint: false
|
|
34898
35157
|
}
|
|
34899
35158
|
},
|
|
34900
|
-
async ({ peerId, sessionId, cwd, label, mode, role }) => {
|
|
35159
|
+
async ({ peerId, sessionId, cwd, label, mode, role, peerSecret: argPeerSecret }) => {
|
|
34901
35160
|
try {
|
|
34902
35161
|
const config2 = getConfig();
|
|
34903
35162
|
const hasExplicitCloudSession = !!config2.COGENT_SESSION_ID || process.env.COGENT_TRUST_PERSISTED_CREDENTIALS === "1";
|
|
@@ -34930,7 +35189,7 @@ function registerRegisterPeerTool(server) {
|
|
|
34930
35189
|
}
|
|
34931
35190
|
}
|
|
34932
35191
|
if (!cloudSessionId) {
|
|
34933
|
-
autoSecret =
|
|
35192
|
+
autoSecret = crypto7.randomBytes(16).toString("hex");
|
|
34934
35193
|
const resp = await fetch(
|
|
34935
35194
|
`${endpoint}/api/sessions`,
|
|
34936
35195
|
{
|
|
@@ -34990,6 +35249,14 @@ function registerRegisterPeerTool(server) {
|
|
|
34990
35249
|
}
|
|
34991
35250
|
const workspaceId = deriveWorkspaceId(cwd);
|
|
34992
35251
|
const threadId = sessionId;
|
|
35252
|
+
let storedPeerSecret = argPeerSecret;
|
|
35253
|
+
if (storedPeerSecret === void 0 && isCloudMode()) {
|
|
35254
|
+
try {
|
|
35255
|
+
storedPeerSecret = (await loadCredentials())?.peerSecret;
|
|
35256
|
+
} catch {
|
|
35257
|
+
storedPeerSecret = void 0;
|
|
35258
|
+
}
|
|
35259
|
+
}
|
|
34993
35260
|
const peer = await backend.registerPeer(
|
|
34994
35261
|
peerId,
|
|
34995
35262
|
sessionId,
|
|
@@ -35000,15 +35267,18 @@ function registerRegisterPeerTool(server) {
|
|
|
35000
35267
|
channelSessionId,
|
|
35001
35268
|
capabilities,
|
|
35002
35269
|
workspaceId,
|
|
35003
|
-
threadId
|
|
35270
|
+
threadId,
|
|
35271
|
+
storedPeerSecret
|
|
35004
35272
|
);
|
|
35005
35273
|
if (isCloudMode()) {
|
|
35006
35274
|
try {
|
|
35007
35275
|
const creds = await loadCredentials();
|
|
35008
|
-
|
|
35276
|
+
const issuedSecret = peer?.peerSecret;
|
|
35277
|
+
if (creds && (creds.peerId !== peerId || issuedSecret && creds.peerSecret !== issuedSecret)) {
|
|
35009
35278
|
await saveCredentials({
|
|
35010
35279
|
...creds,
|
|
35011
35280
|
peerId,
|
|
35281
|
+
...issuedSecret ? { peerSecret: issuedSecret } : {},
|
|
35012
35282
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35013
35283
|
});
|
|
35014
35284
|
}
|
|
@@ -35036,16 +35306,17 @@ function registerRegisterPeerTool(server) {
|
|
|
35036
35306
|
logger.info(
|
|
35037
35307
|
`Register: session resolved ${resolution.decision}/${resolution.confidence} (${resolution.candidateCount} candidate[s]) -> ${localSessionId}`
|
|
35038
35308
|
);
|
|
35039
|
-
const
|
|
35040
|
-
if (
|
|
35309
|
+
const unresolved = unresolvedSessionReason(resolution);
|
|
35310
|
+
if (unresolved) {
|
|
35041
35311
|
try {
|
|
35042
35312
|
const base = getConfig().COGENT_PLATFORM === "cc" ? await transcriptBaseSummary() : "(codex: scans CODEX_HOME/sessions)";
|
|
35313
|
+
const cause = unresolved === "ambiguous" ? `${resolution.candidateCount} candidate sessions matched and NONE could be chosen, so the id you supplied was pinned UNCHECKED \u2014 if that is a channel UUID rather than a local session id, every wake will fail in ~2s` : `NO local ${getConfig().COGENT_PLATFORM} session was found for this cwd`;
|
|
35043
35314
|
logger.warn(
|
|
35044
|
-
`Register:
|
|
35315
|
+
`Register: ${cause} \u2014 auto-wake WILL be refused. cwd=${cwd}; scanned ${(resolution.probedPaths ?? []).join(", ") || "(unknown)"}; base ${base}. If the base is missing or empty, this agent's config dir is elsewhere \u2014 set CLAUDE_CONFIG_DIR / CODEX_HOME so the BRIDGE process sees it too. If the base has project dirs but none match, the registered cwd is not where this agent runs.`
|
|
35045
35316
|
);
|
|
35046
35317
|
} catch (err) {
|
|
35047
35318
|
logger.warn(
|
|
35048
|
-
`Register:
|
|
35319
|
+
`Register: unresolved session (${unresolved}) for cwd=${cwd} (auto-wake WILL be refused); could not describe the transcript base: ${err.message}`
|
|
35049
35320
|
);
|
|
35050
35321
|
}
|
|
35051
35322
|
}
|
|
@@ -35059,8 +35330,8 @@ function registerRegisterPeerTool(server) {
|
|
|
35059
35330
|
confidence: resolution.confidence,
|
|
35060
35331
|
candidateCount: resolution.candidateCount,
|
|
35061
35332
|
// Only on the broken path — keeps the happy-path result terse.
|
|
35062
|
-
...
|
|
35063
|
-
warning: "No local session transcript found for this cwd \u2014 auto-wake will be REFUSED. Verify the cwd is where this agent actually runs and that CLAUDE_CONFIG_DIR / CODEX_HOME are visible to the bridge process.",
|
|
35333
|
+
...unresolved ? {
|
|
35334
|
+
warning: (unresolved === "ambiguous" ? `${resolution.candidateCount} local session transcripts matched this cwd and none could be chosen, so the sessionId you supplied was pinned UNCHECKED \u2014 if it is a channel UUID rather than a local session id, auto-wake will be REFUSED. ` : "No local session transcript found for this cwd \u2014 auto-wake will be REFUSED. ") + "Verify the cwd is where this agent actually runs and that CLAUDE_CONFIG_DIR / CODEX_HOME are visible to the bridge process.",
|
|
35064
35335
|
scannedPaths: resolution.probedPaths ?? []
|
|
35065
35336
|
} : {}
|
|
35066
35337
|
}
|
|
@@ -35276,8 +35547,9 @@ function registerSendMessageTool(server) {
|
|
|
35276
35547
|
if (isCloud && success2) {
|
|
35277
35548
|
const effectiveAckTimeout = Math.min(ACK_TIMEOUT_MS, config2.COGENT_TIMEOUT_MS);
|
|
35278
35549
|
const fastPathMs = Math.min(config2.COGENT_FASTPATH_MS, config2.COGENT_TIMEOUT_MS);
|
|
35279
|
-
const ackPromise = autoRelay.waitForDeliveryAck(toPeerId, effectiveAckTimeout);
|
|
35280
35550
|
const responsePromise = autoRelay.waitForResponse(toPeerId, fastPathMs);
|
|
35551
|
+
responsePromise.catch(() => {
|
|
35552
|
+
});
|
|
35281
35553
|
let outboundRecord;
|
|
35282
35554
|
try {
|
|
35283
35555
|
outboundRecord = await backend.recordMessage({
|
|
@@ -35293,6 +35565,11 @@ function registerSendMessageTool(server) {
|
|
|
35293
35565
|
autoRelay.clearExpectation(toPeerId);
|
|
35294
35566
|
throw recordErr;
|
|
35295
35567
|
}
|
|
35568
|
+
const ackPromise = autoRelay.waitForDeliveryAck(
|
|
35569
|
+
toPeerId,
|
|
35570
|
+
outboundRecord.id,
|
|
35571
|
+
effectiveAckTimeout
|
|
35572
|
+
);
|
|
35296
35573
|
let ackReceived = false;
|
|
35297
35574
|
try {
|
|
35298
35575
|
await ackPromise;
|
|
@@ -35473,6 +35750,7 @@ var init_message = __esm({
|
|
|
35473
35750
|
timestamp: import_zod6.z.string().datetime().describe("ISO 8601 message timestamp"),
|
|
35474
35751
|
durationMs: import_zod6.z.number().nullable().describe("Round-trip duration in ms"),
|
|
35475
35752
|
success: import_zod6.z.boolean().describe("Whether delivery succeeded"),
|
|
35753
|
+
pending: import_zod6.z.boolean().optional().describe("Delivered+acked; the reply is still expected"),
|
|
35476
35754
|
error: import_zod6.z.string().nullable().describe("Error message if delivery failed"),
|
|
35477
35755
|
originPlatform: import_zod6.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini", "whatsapp", "telegram", "discord"]).optional().describe("Platform that originated this message"),
|
|
35478
35756
|
attachments: import_zod6.z.array(AttachmentSchema).optional().describe("Cogent Mail M2.5 \u2014 file references (byte transport = email)")
|
|
@@ -35562,15 +35840,31 @@ var init_errors5 = __esm({
|
|
|
35562
35840
|
});
|
|
35563
35841
|
|
|
35564
35842
|
// cogent/dist/schemas/api-requests.js
|
|
35565
|
-
var import_zod10, CreateSessionRequestSchema, JoinSessionRequestSchema, ResolveSessionRequestSchema, RegisterPeerRequestSchema, DeregisterPeerRequestSchema, SendMessageRequestSchema, GetHistoryQuerySchema, PollQuerySchema;
|
|
35843
|
+
var import_zod10, FIELD_LIMITS, CreateSessionRequestSchema, JoinSessionRequestSchema, ResolveSessionRequestSchema, RegisterPeerRequestSchema, DeregisterPeerRequestSchema, SendMessageRequestSchema, GetHistoryQuerySchema, PollQuerySchema;
|
|
35566
35844
|
var init_api_requests = __esm({
|
|
35567
35845
|
"cogent/dist/schemas/api-requests.js"() {
|
|
35568
35846
|
"use strict";
|
|
35569
35847
|
import_zod10 = __toESM(require_zod(), 1);
|
|
35848
|
+
FIELD_LIMITS = {
|
|
35849
|
+
/** A shared channel secret. Long passphrases are fine; novels are not. */
|
|
35850
|
+
SECRET_MAX: 1024,
|
|
35851
|
+
/** Org_ID. */
|
|
35852
|
+
ORG_ID_MAX: 256,
|
|
35853
|
+
/** Peer identifier. */
|
|
35854
|
+
PEER_ID_MAX: 128,
|
|
35855
|
+
/** Human-readable peer label. */
|
|
35856
|
+
PEER_LABEL_MAX: 256,
|
|
35857
|
+
/** Working directory — comfortably above PATH_MAX (4096) on every platform we support. */
|
|
35858
|
+
CWD_MAX: 8192,
|
|
35859
|
+
/** A single message body. */
|
|
35860
|
+
MESSAGE_MAX: 1048576,
|
|
35861
|
+
/** One platform-identity value (e.g. a Slack user id). */
|
|
35862
|
+
PLATFORM_IDENTITY_VALUE_MAX: 512
|
|
35863
|
+
};
|
|
35570
35864
|
CreateSessionRequestSchema = import_zod10.z.object({
|
|
35571
35865
|
label: import_zod10.z.string().regex(/^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$/).optional().describe("Optional human-readable session label (3-32 lowercase alphanumeric + hyphens)"),
|
|
35572
|
-
secret: import_zod10.z.string().min(8).describe("Shared secret for joining the session (min 8 chars)"),
|
|
35573
|
-
orgId: import_zod10.z.string().min(1).optional().describe("Optional Org_ID -- gates the channel to Business Edition orgs (multi-tenant relay only)")
|
|
35866
|
+
secret: import_zod10.z.string().min(8).max(FIELD_LIMITS.SECRET_MAX).describe("Shared secret for joining the session (min 8 chars)"),
|
|
35867
|
+
orgId: import_zod10.z.string().min(1).max(FIELD_LIMITS.ORG_ID_MAX).optional().describe("Optional Org_ID -- gates the channel to Business Edition orgs (multi-tenant relay only)")
|
|
35574
35868
|
}).strict();
|
|
35575
35869
|
JoinSessionRequestSchema = import_zod10.z.object({
|
|
35576
35870
|
secret: import_zod10.z.string().min(1).describe("Shared secret for the session"),
|
|
@@ -35581,13 +35875,14 @@ var init_api_requests = __esm({
|
|
|
35581
35875
|
orgId: import_zod10.z.string().min(1).optional().describe("Optional Org_ID -- required to resolve an Org_ID-gated business channel")
|
|
35582
35876
|
}).strict();
|
|
35583
35877
|
RegisterPeerRequestSchema = import_zod10.z.object({
|
|
35584
|
-
peerId: import_zod10.z.string().min(1).describe("Unique peer identifier"),
|
|
35585
|
-
|
|
35586
|
-
|
|
35878
|
+
peerId: import_zod10.z.string().min(1).max(FIELD_LIMITS.PEER_ID_MAX).describe("Unique peer identifier"),
|
|
35879
|
+
peerSecret: import_zod10.z.string().min(1).max(256).optional().describe("AUD-003: proof of ownership when reclaiming a peer from a different token"),
|
|
35880
|
+
cwd: import_zod10.z.string().max(FIELD_LIMITS.CWD_MAX).describe("Peer working directory path"),
|
|
35881
|
+
label: import_zod10.z.string().max(FIELD_LIMITS.PEER_LABEL_MAX).describe("Human-readable peer label"),
|
|
35587
35882
|
platform: import_zod10.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini"]).optional().describe("Platform this peer belongs to"),
|
|
35588
35883
|
type: import_zod10.z.enum(["agent", "human"]).optional().describe("Peer type"),
|
|
35589
35884
|
transport: import_zod10.z.enum(["ws", "slack-bot", "gchat-bot", "websocket"]).optional().describe("Transport mechanism"),
|
|
35590
|
-
platformIdentity: import_zod10.z.record(import_zod10.z.string(), import_zod10.z.string()).optional().describe("Platform-specific identity fields"),
|
|
35885
|
+
platformIdentity: import_zod10.z.record(import_zod10.z.string().max(64), import_zod10.z.string().max(FIELD_LIMITS.PLATFORM_IDENTITY_VALUE_MAX)).optional().describe("Platform-specific identity fields"),
|
|
35591
35886
|
role: import_zod10.z.string().max(64).optional().describe("A2A role advertised by this peer (Agent Card)"),
|
|
35592
35887
|
capabilities: import_zod10.z.array(import_zod10.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]")
|
|
35593
35888
|
}).strict();
|
|
@@ -35595,8 +35890,11 @@ var init_api_requests = __esm({
|
|
|
35595
35890
|
SendMessageRequestSchema = import_zod10.z.object({
|
|
35596
35891
|
fromPeerId: import_zod10.z.string().min(1).describe("Sender peer ID"),
|
|
35597
35892
|
toPeerId: import_zod10.z.string().min(1).describe("Recipient peer ID (use '*' for broadcast)"),
|
|
35598
|
-
message: import_zod10.z.string().min(1).describe("Message content"),
|
|
35599
|
-
originPlatform: import_zod10.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini"]).optional().describe("Platform originating this message")
|
|
35893
|
+
message: import_zod10.z.string().min(1).max(FIELD_LIMITS.MESSAGE_MAX).describe("Message content"),
|
|
35894
|
+
originPlatform: import_zod10.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini"]).optional().describe("Platform originating this message"),
|
|
35895
|
+
success: import_zod10.z.boolean().optional().describe("Delivery outcome as reported by the sender"),
|
|
35896
|
+
error: import_zod10.z.string().nullable().optional().describe("Failure code as reported by the sender"),
|
|
35897
|
+
durationMs: import_zod10.z.number().int().nonnegative().nullable().optional().describe("Sender-measured duration")
|
|
35600
35898
|
}).strict();
|
|
35601
35899
|
GetHistoryQuerySchema = import_zod10.z.object({
|
|
35602
35900
|
peerId: import_zod10.z.string().optional().describe("Filter messages by peer ID"),
|
|
@@ -36206,8 +36504,69 @@ var init_health_check2 = __esm({
|
|
|
36206
36504
|
}
|
|
36207
36505
|
});
|
|
36208
36506
|
|
|
36507
|
+
// src/tools/wake-log.ts
|
|
36508
|
+
function registerWakeLogTool(server) {
|
|
36509
|
+
server.registerTool(
|
|
36510
|
+
"cogent_wake_log",
|
|
36511
|
+
{
|
|
36512
|
+
title: "Wake Log",
|
|
36513
|
+
description: "Show the auto-resume (wake) sessions Cogent has run in this working directory: which peer's message triggered each one, when, how long it took, the outcome, and WHICH FILES IT CHANGED. Use this when files changed with no corresponding edit in your session, when you need to establish who authored a change before committing it, or to check whether a wake is running now.",
|
|
36514
|
+
inputSchema: {
|
|
36515
|
+
limit: import_zod14.z.number().int().positive().optional().describe("Return only the most recent N wakes (default: all retained)."),
|
|
36516
|
+
peerId: import_zod14.z.string().optional().describe(
|
|
36517
|
+
"Your local peer id. Required to list wakes running RIGHT NOW \u2014 in-flight markers are keyed per peer, and two peers can share one directory."
|
|
36518
|
+
),
|
|
36519
|
+
cwd: import_zod14.z.string().optional().describe("Directory to inspect (default: this bridge's working directory).")
|
|
36520
|
+
},
|
|
36521
|
+
annotations: {
|
|
36522
|
+
readOnlyHint: true,
|
|
36523
|
+
destructiveHint: false,
|
|
36524
|
+
idempotentHint: true,
|
|
36525
|
+
openWorldHint: false
|
|
36526
|
+
}
|
|
36527
|
+
},
|
|
36528
|
+
async ({ limit, peerId, cwd }) => {
|
|
36529
|
+
try {
|
|
36530
|
+
const target = cwd ?? process.cwd();
|
|
36531
|
+
const wakes = readWakeLog(target, limit);
|
|
36532
|
+
const mutatingWakeCount = wakes.filter(
|
|
36533
|
+
(w) => Array.isArray(w.filesChanged) && w.filesChanged.length > 0
|
|
36534
|
+
).length;
|
|
36535
|
+
const active = peerId ? listWakeInFlight(peerId, target) : [];
|
|
36536
|
+
return successResult({
|
|
36537
|
+
cwd: target,
|
|
36538
|
+
count: wakes.length,
|
|
36539
|
+
mutatingWakeCount,
|
|
36540
|
+
wakes,
|
|
36541
|
+
active,
|
|
36542
|
+
...peerId ? {} : { activeHint: "Pass peerId to see wakes running right now (markers are per-peer)." },
|
|
36543
|
+
note: "filesChanged is a git snapshot diff across each wake's window. If you were editing the same tree at the same time, your edits appear here too \u2014 it is what changed while the wake ran, not proof of authorship. null means the directory is not a git worktree."
|
|
36544
|
+
});
|
|
36545
|
+
} catch (err) {
|
|
36546
|
+
return errorResult(
|
|
36547
|
+
new BridgeError(
|
|
36548
|
+
"STATE_CORRUPT" /* STATE_CORRUPT */,
|
|
36549
|
+
`Could not read the wake log: ${err instanceof Error ? err.message : String(err)}`,
|
|
36550
|
+
"The log lives at ~/.cogent/wake-log/<sha256(cwd)[..16]>.jsonl \u2014 check it is readable."
|
|
36551
|
+
)
|
|
36552
|
+
);
|
|
36553
|
+
}
|
|
36554
|
+
}
|
|
36555
|
+
);
|
|
36556
|
+
}
|
|
36557
|
+
var import_zod14;
|
|
36558
|
+
var init_wake_log2 = __esm({
|
|
36559
|
+
"src/tools/wake-log.ts"() {
|
|
36560
|
+
"use strict";
|
|
36561
|
+
import_zod14 = __toESM(require_zod(), 1);
|
|
36562
|
+
init_errors4();
|
|
36563
|
+
init_wake_log();
|
|
36564
|
+
init_wake_inflight();
|
|
36565
|
+
}
|
|
36566
|
+
});
|
|
36567
|
+
|
|
36209
36568
|
// src/tools/create-session.ts
|
|
36210
|
-
import
|
|
36569
|
+
import crypto8 from "node:crypto";
|
|
36211
36570
|
function registerCreateSessionTool(server) {
|
|
36212
36571
|
server.registerTool(
|
|
36213
36572
|
"cogent_create_session",
|
|
@@ -36215,8 +36574,8 @@ function registerCreateSessionTool(server) {
|
|
|
36215
36574
|
title: "Create Cloud Session",
|
|
36216
36575
|
description: "Create a new cloud bridge session on the relay server. Returns session ID and secret for other peers to join. Only available when COGENT_ENDPOINT is configured.",
|
|
36217
36576
|
inputSchema: {
|
|
36218
|
-
label:
|
|
36219
|
-
secret:
|
|
36577
|
+
label: import_zod15.z.string().optional().describe("Human-readable label for the session"),
|
|
36578
|
+
secret: import_zod15.z.string().min(8).optional().describe(
|
|
36220
36579
|
"Shared secret for session authentication (min 8 chars). Auto-generated if omitted."
|
|
36221
36580
|
)
|
|
36222
36581
|
},
|
|
@@ -36238,7 +36597,7 @@ function registerCreateSessionTool(server) {
|
|
|
36238
36597
|
)
|
|
36239
36598
|
);
|
|
36240
36599
|
}
|
|
36241
|
-
const sessionSecret = secret ??
|
|
36600
|
+
const sessionSecret = secret ?? crypto8.randomBytes(16).toString("hex");
|
|
36242
36601
|
const resp = await fetch(
|
|
36243
36602
|
`${config2.COGENT_ENDPOINT}/api/sessions`,
|
|
36244
36603
|
{
|
|
@@ -36309,11 +36668,11 @@ COGENT_PLATFORM=${config2.COGENT_PLATFORM}`,
|
|
|
36309
36668
|
}
|
|
36310
36669
|
);
|
|
36311
36670
|
}
|
|
36312
|
-
var
|
|
36671
|
+
var import_zod15;
|
|
36313
36672
|
var init_create_session = __esm({
|
|
36314
36673
|
"src/tools/create-session.ts"() {
|
|
36315
36674
|
"use strict";
|
|
36316
|
-
|
|
36675
|
+
import_zod15 = __toESM(require_zod(), 1);
|
|
36317
36676
|
init_config();
|
|
36318
36677
|
init_errors4();
|
|
36319
36678
|
init_credential_store();
|
|
@@ -36329,11 +36688,11 @@ function registerJoinSessionTool(server) {
|
|
|
36329
36688
|
title: "Join Cloud Channel",
|
|
36330
36689
|
description: 'Join an existing cloud bridge channel by its channel name (e.g. "mt-space") and shared secret. The channel name is the human-readable label provided by the user, NOT a UUID. Do NOT pass a Claude Code session ID here \u2014 use the channel/space name the user gave you.',
|
|
36331
36690
|
inputSchema: {
|
|
36332
|
-
channel:
|
|
36691
|
+
channel: import_zod16.z.string().describe(
|
|
36333
36692
|
'The channel name to join (e.g. "mt-space"). This is the human-readable name the user provided, NOT a UUID. Do NOT use a Claude Code session ID here.'
|
|
36334
36693
|
),
|
|
36335
|
-
secret:
|
|
36336
|
-
orgId:
|
|
36694
|
+
secret: import_zod16.z.string().min(1).describe("The shared secret / password for the channel"),
|
|
36695
|
+
orgId: import_zod16.z.string().min(1).optional().describe(
|
|
36337
36696
|
"Org_ID \u2014 the 3rd credential for a Team (business) channel. Provide it for team channels; omit for free channels."
|
|
36338
36697
|
)
|
|
36339
36698
|
},
|
|
@@ -36452,11 +36811,11 @@ function registerJoinSessionTool(server) {
|
|
|
36452
36811
|
}
|
|
36453
36812
|
);
|
|
36454
36813
|
}
|
|
36455
|
-
var
|
|
36814
|
+
var import_zod16, UUID_V4_RE2;
|
|
36456
36815
|
var init_join_session = __esm({
|
|
36457
36816
|
"src/tools/join-session.ts"() {
|
|
36458
36817
|
"use strict";
|
|
36459
|
-
|
|
36818
|
+
import_zod16 = __toESM(require_zod(), 1);
|
|
36460
36819
|
init_config();
|
|
36461
36820
|
init_errors4();
|
|
36462
36821
|
init_credential_store();
|
|
@@ -36520,12 +36879,12 @@ function registerSetupMailTool(server) {
|
|
|
36520
36879
|
title: "Configure this agent's mailbox",
|
|
36521
36880
|
description: "Store the agent's mailbox address + password (from the admin Mail panel) locally so cogent_send_mail and cogent_fetch_mail can use them. Hosts default to mail.cogent.tools (IMAP 993 / SMTP 465).",
|
|
36522
36881
|
inputSchema: {
|
|
36523
|
-
address:
|
|
36524
|
-
password:
|
|
36525
|
-
imapHost:
|
|
36526
|
-
imapPort:
|
|
36527
|
-
smtpHost:
|
|
36528
|
-
smtpPort:
|
|
36882
|
+
address: import_zod17.z.string().describe("Full mailbox address, e.g. claude-agent-backend@mail.cogent.tools"),
|
|
36883
|
+
password: import_zod17.z.string().describe("Mailbox password (shown once in the admin Mail panel on create/rotate)"),
|
|
36884
|
+
imapHost: import_zod17.z.string().optional().describe(`IMAP host (default ${DEFAULT_MAIL_HOST})`),
|
|
36885
|
+
imapPort: import_zod17.z.number().int().positive().optional().describe("IMAP port (default 993)"),
|
|
36886
|
+
smtpHost: import_zod17.z.string().optional().describe(`SMTP host (default ${DEFAULT_MAIL_HOST})`),
|
|
36887
|
+
smtpPort: import_zod17.z.number().int().positive().optional().describe("SMTP submission port (default 465)")
|
|
36529
36888
|
},
|
|
36530
36889
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
36531
36890
|
},
|
|
@@ -36549,11 +36908,11 @@ function registerSetupMailTool(server) {
|
|
|
36549
36908
|
}
|
|
36550
36909
|
);
|
|
36551
36910
|
}
|
|
36552
|
-
var
|
|
36911
|
+
var import_zod17, DEFAULT_MAIL_HOST;
|
|
36553
36912
|
var init_setup_mail = __esm({
|
|
36554
36913
|
"src/tools/setup-mail.ts"() {
|
|
36555
36914
|
"use strict";
|
|
36556
|
-
|
|
36915
|
+
import_zod17 = __toESM(require_zod(), 1);
|
|
36557
36916
|
init_errors4();
|
|
36558
36917
|
init_mail_credential_store();
|
|
36559
36918
|
init_validate();
|
|
@@ -36562,13 +36921,13 @@ var init_setup_mail = __esm({
|
|
|
36562
36921
|
});
|
|
36563
36922
|
|
|
36564
36923
|
// src/mail/mail-sender.ts
|
|
36565
|
-
import
|
|
36566
|
-
import
|
|
36924
|
+
import fs17 from "node:fs/promises";
|
|
36925
|
+
import path18 from "node:path";
|
|
36567
36926
|
async function buildAndSendMail(sender, creds, params) {
|
|
36568
36927
|
const attachments = [];
|
|
36569
36928
|
for (const p of params.attachmentPaths ?? []) {
|
|
36570
|
-
const content = await
|
|
36571
|
-
attachments.push({ filename:
|
|
36929
|
+
const content = await fs17.readFile(p);
|
|
36930
|
+
attachments.push({ filename: path18.basename(p), content });
|
|
36572
36931
|
}
|
|
36573
36932
|
const { messageId } = await sender.send({
|
|
36574
36933
|
from: creds.address,
|
|
@@ -36644,10 +37003,10 @@ function registerSendMailTool(server, deps = {}) {
|
|
|
36644
37003
|
title: "Send an email from this agent's mailbox",
|
|
36645
37004
|
description: "Send an email (with optional file attachments) from this agent's Cogent mailbox to another address. Requires cogent_setup_mail to have stored the mailbox credentials first.",
|
|
36646
37005
|
inputSchema: {
|
|
36647
|
-
to:
|
|
36648
|
-
subject:
|
|
36649
|
-
body:
|
|
36650
|
-
attachments:
|
|
37006
|
+
to: import_zod18.z.string().describe("Recipient email address, e.g. other-agent-backend@mail.cogent.tools"),
|
|
37007
|
+
subject: import_zod18.z.string().describe("Email subject"),
|
|
37008
|
+
body: import_zod18.z.string().describe("Plain-text email body"),
|
|
37009
|
+
attachments: import_zod18.z.array(import_zod18.z.string()).optional().describe("Optional local file paths to attach (each is read and attached by basename)")
|
|
36651
37010
|
},
|
|
36652
37011
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
36653
37012
|
},
|
|
@@ -36682,11 +37041,11 @@ function registerSendMailTool(server, deps = {}) {
|
|
|
36682
37041
|
}
|
|
36683
37042
|
);
|
|
36684
37043
|
}
|
|
36685
|
-
var
|
|
37044
|
+
var import_zod18;
|
|
36686
37045
|
var init_send_mail = __esm({
|
|
36687
37046
|
"src/tools/send-mail.ts"() {
|
|
36688
37047
|
"use strict";
|
|
36689
|
-
|
|
37048
|
+
import_zod18 = __toESM(require_zod(), 1);
|
|
36690
37049
|
init_errors4();
|
|
36691
37050
|
init_mail_credential_store();
|
|
36692
37051
|
init_mail_sender();
|
|
@@ -36696,16 +37055,16 @@ var init_send_mail = __esm({
|
|
|
36696
37055
|
});
|
|
36697
37056
|
|
|
36698
37057
|
// src/mail/mail-fetcher.ts
|
|
36699
|
-
import
|
|
36700
|
-
import
|
|
37058
|
+
import fs18 from "node:fs/promises";
|
|
37059
|
+
import path19 from "node:path";
|
|
36701
37060
|
async function saveAttachments(attachments, downloadDir) {
|
|
36702
37061
|
if (attachments.length === 0) return [];
|
|
36703
|
-
await
|
|
37062
|
+
await fs18.mkdir(downloadDir, { recursive: true });
|
|
36704
37063
|
const saved = [];
|
|
36705
37064
|
for (const a of attachments) {
|
|
36706
|
-
const name =
|
|
36707
|
-
const savedPath =
|
|
36708
|
-
await
|
|
37065
|
+
const name = path19.basename(a.filename) || "attachment";
|
|
37066
|
+
const savedPath = path19.join(downloadDir, name);
|
|
37067
|
+
await fs18.writeFile(savedPath, a.content);
|
|
36709
37068
|
saved.push({ filename: name, savedPath, size: a.content.length });
|
|
36710
37069
|
}
|
|
36711
37070
|
return saved;
|
|
@@ -36836,8 +37195,8 @@ var init_mail_fetcher = __esm({
|
|
|
36836
37195
|
});
|
|
36837
37196
|
|
|
36838
37197
|
// src/tools/fetch-mail.ts
|
|
36839
|
-
import
|
|
36840
|
-
import
|
|
37198
|
+
import os13 from "node:os";
|
|
37199
|
+
import path20 from "node:path";
|
|
36841
37200
|
function registerFetchMailTool(server, deps = {}) {
|
|
36842
37201
|
const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
|
|
36843
37202
|
server.registerTool(
|
|
@@ -36846,11 +37205,11 @@ function registerFetchMailTool(server, deps = {}) {
|
|
|
36846
37205
|
title: "Read this agent's mailbox",
|
|
36847
37206
|
description: "List messages in this agent's Cogent mailbox, or fetch one message (text + attachments saved locally) by uid. Requires cogent_setup_mail to have stored the mailbox credentials.",
|
|
36848
37207
|
inputSchema: {
|
|
36849
|
-
action:
|
|
36850
|
-
uid:
|
|
36851
|
-
unreadOnly:
|
|
36852
|
-
limit:
|
|
36853
|
-
downloadDir:
|
|
37208
|
+
action: import_zod19.z.enum(["list", "fetch"]).optional().describe("'list' (default) or 'fetch' one message"),
|
|
37209
|
+
uid: import_zod19.z.number().int().positive().optional().describe("Message uid to fetch (required when action='fetch')"),
|
|
37210
|
+
unreadOnly: import_zod19.z.boolean().optional().describe("List only unread messages (default false)"),
|
|
37211
|
+
limit: import_zod19.z.number().int().positive().optional().describe("Max messages to list (default 25)"),
|
|
37212
|
+
downloadDir: import_zod19.z.string().optional().describe(`Directory to save fetched attachments (default ${DEFAULT_DOWNLOAD_DIR})`)
|
|
36854
37213
|
},
|
|
36855
37214
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
36856
37215
|
},
|
|
@@ -36889,17 +37248,17 @@ function registerFetchMailTool(server, deps = {}) {
|
|
|
36889
37248
|
}
|
|
36890
37249
|
);
|
|
36891
37250
|
}
|
|
36892
|
-
var
|
|
37251
|
+
var import_zod19, DEFAULT_DOWNLOAD_DIR;
|
|
36893
37252
|
var init_fetch_mail = __esm({
|
|
36894
37253
|
"src/tools/fetch-mail.ts"() {
|
|
36895
37254
|
"use strict";
|
|
36896
|
-
|
|
37255
|
+
import_zod19 = __toESM(require_zod(), 1);
|
|
36897
37256
|
init_errors4();
|
|
36898
37257
|
init_mail_credential_store();
|
|
36899
37258
|
init_mail_fetcher();
|
|
36900
37259
|
init_auth_error();
|
|
36901
37260
|
init_validate();
|
|
36902
|
-
DEFAULT_DOWNLOAD_DIR =
|
|
37261
|
+
DEFAULT_DOWNLOAD_DIR = path20.join(os13.homedir(), ".cogent", "mail-downloads");
|
|
36903
37262
|
}
|
|
36904
37263
|
});
|
|
36905
37264
|
|
|
@@ -36999,6 +37358,7 @@ async function main() {
|
|
|
36999
37358
|
registerListPeersTool(server);
|
|
37000
37359
|
registerGetHistoryTool(server);
|
|
37001
37360
|
registerHealthCheckTool(server);
|
|
37361
|
+
registerWakeLogTool(server);
|
|
37002
37362
|
registerCreateSessionTool(server);
|
|
37003
37363
|
registerJoinSessionTool(server);
|
|
37004
37364
|
registerSetupMailTool(server);
|
|
@@ -37071,6 +37431,7 @@ var init_index = __esm({
|
|
|
37071
37431
|
init_get_history();
|
|
37072
37432
|
init_deregister_peer();
|
|
37073
37433
|
init_health_check2();
|
|
37434
|
+
init_wake_log2();
|
|
37074
37435
|
init_create_session();
|
|
37075
37436
|
init_join_session();
|
|
37076
37437
|
init_setup_mail();
|