UNPKG

@essentialai/cogent-bridge

Version:

Cogent Bridge — cross-agent comms for Claude Code, OpenAI Codex and Slack. Codex users: install with `curl -fsSL https://cogent.tools/install.sh | sh`, then start with `cogent-codex` for real-time peer wake.

99 lines • 5.06 kB
import { z } from "zod"; import { getBackend, isCloudDeferred } from "../backend/index.js"; import { getConfig } from "../config.js"; import { successResult, errorResult } from "../errors.js"; import { logger } from "../logger.js"; import { heartbeat } from "../services/heartbeat.js"; import { clearMailCredentials } from "../cloud/mail-credential-store.js"; import { loadCredentials, saveCredentials } from "../cloud/credential-store.js"; export function registerDeregisterPeerTool(server) { server.registerTool("cogent_deregister_peer", { title: "Deregister Peer", description: "Remove a previously registered peer from the bridge. " + "The peer will no longer be reachable for messaging.", inputSchema: { peerId: z .string() .describe("Peer ID to deregister, e.g. 'backend'"), }, annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false, }, }, async ({ peerId }) => { try { // SECURITY: an unjoined cloud agent (deferred FileBackend placeholder) // must not touch the host-global cogent-state.json — deregisterPeer there // is both an existence oracle (the `removed` boolean) and a destructive // DELETE of ANOTHER channel's peer. Refuse; it belongs to no channel. if (isCloudDeferred(getConfig().COGENT_ENDPOINT)) { return successResult({ success: false, notJoined: true, removed: false, message: "Not connected to a channel yet — call cogent_join_session (or cogent_register_peer) first. Nothing to deregister.", }); } const backend = getBackend(); // Read the ownership proof BEFORE deleting — the block below clears it from the stored // credential on success, so loading it afterwards would always come back empty. let ownershipProof; try { ownershipProof = (await loadCredentials())?.peerSecret; } catch { /* no stored credential — the current token must prove ownership on its own */ } const { removed, mailboxDeprovisioned } = await backend.deregisterPeer(peerId, ownershipProof); if (removed) heartbeat.stop(); // Cogent Mail F3: if the relay confirmed our Team mailbox was reaped, the locally-stored // creds are now dead — clear them so a stale password never lingers (a returning agent // re-provisions fresh on the next register). Best-effort: a cleanup failure must not fail // the deregister the user asked for. if (mailboxDeprovisioned) { try { await clearMailCredentials(); logger.info("cleared local mail credentials after mailbox deprovision", { peerId }); } catch (err) { logger.warn("failed to clear local mail credentials after deprovision", { error: err }); } } // 🔴 CLEAR THE peerId FROM THE STORED CREDENTIAL — the CONSENT SIGNAL. // Cold-start rejoin (services/cold-start-rejoin.ts) restores a registration at // startup when the credential file names a peer. Leaving the peerId here after a // deliberate deregister would make the next bridge start put this agent straight // back into the channel the operator just removed it from. The rest of the // credential (endpoint, sessionId, token) is KEPT: the channel membership is // still valid and cogent_register_peer must keep working without a re-join. // Best-effort — a cleanup failure must not fail the deregister that succeeded. if (removed) { try { const creds = await loadCredentials(); if (creds?.peerId === peerId) { const { peerId: _dropped, peerSecret: _secret, ...rest } = creds; await saveCredentials({ ...rest, savedAt: new Date().toISOString() }); logger.info("cleared stored peerId after deregister", { peerId }); } } catch (err) { logger.warn("failed to clear stored peerId after deregister", { error: err }); } } return successResult({ success: removed, message: removed ? `Peer '${peerId}' deregistered` : `Peer '${peerId}' was not registered`, }); } catch (err) { logger.error("deregister-peer failed", { error: err }); return errorResult(err); } }); } //# sourceMappingURL=deregister-peer.js.map