@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.
196 lines • 9.76 kB
TypeScript
export interface PeerInfo {
peerId: string;
sessionId: string;
cwd: string;
label: string;
registeredAt: string;
lastSeenAt: string;
/** cogent-bridge npm version of the registering client (optional, added in 3.1.6). */
clientVersion?: string;
/**
* Peer participation mode (optional, added in 3.2.0 / Phase 17 OBS-01..03).
* - "agent" (default if absent): inbound directed messages trigger
* `execClaude` via the auto-relay path. Standard CC peer behavior.
* - "observer": peer receives WS frames and remains targetable in
* `cogent_list_peers`, but the auto-relay short-circuits — no
* subprocess is spawned. Useful for operators using their MCP client
* as a passive watcher without polluting the transcript with phantom
* relay replies.
* Absent on records produced by pre-3.2.0 clients (treated as "agent").
*/
mode?: "observer" | "agent";
/**
* Cloud/relay channel session id, kept distinct from the local `sessionId`
* the resolver finds on disk (they used to collide). Local-only — NOT sent on
* the relay register wire. Added 3.5.9.
*/
channelSessionId?: string;
/**
* Advertised capabilities, e.g. ["auto-resume"] | ["observer"]. A local rail
* for the later relay slice; NOT sent on the wire yet. Added 3.5.9.
*/
capabilities?: string[];
/**
* Durable workspace identifier (Slice 5 / Story 8.6). A STABLE id for the
* workspace that does NOT depend on the mutable, lossy cwd hint. UNLIKE
* channelSessionId/capabilities, this IS sent on the relay wire (the server
* .strict() schema was extended in lockstep). Optional; back-compat.
*/
workspaceId?: string;
/** Durable thread/conversation identifier (Slice 5 / Story 8.6). On the wire; optional. */
threadId?: string;
/**
* Cogent Mail auto-provisioned mailbox (M2.7). Present ONLY on a cloud register RESPONSE
* for a Team channel where the relay minted (or resolved) this peer's mailbox via the
* control-plane provisioner. `password` is returned ONCE (on first create / explicit rotate)
* and is never persisted server-side — the bridge saves it to its local 0600 store and drops
* it. Absent on free channels and on re-registers that don't re-issue a password.
*/
mailbox?: {
address: string;
password?: string;
};
/**
* Cogent Mail provisioning FAILURE (2026-09-04). Present ONLY on a cloud register response for a
* Team channel where provisioning was attempted and did not produce a mailbox. Its whole purpose
* is to separate "Team, but broken — retry" from "free tier, mail was never available here",
* which are indistinguishable from the agent's side otherwise. Never carries a credential.
*/
mailboxError?: {
code: string;
message: string;
};
}
export interface MessageRecord {
id: string;
fromPeerId: string;
toPeerId: string;
message: string;
response: string | null;
timestamp: string;
durationMs: number | null;
success: boolean;
error: string | null;
/**
* Marks a message as the auto-relay echo (the FE->BE response sent back
* after a directed message-with-reply). Server 3.1.6+ filters these out
* of `cogent_get_history` by default. Absent / false === regular message.
* Forward-compat: older servers omit the field; older clients ignore it.
* Added in client 3.1.11 (260511-o3p).
*/
isRelayEcho?: boolean;
/**
* Marks a directed send that was delivered+acked but whose reply did not
* arrive within the fast-path window (COGENT_FASTPATH_MS). The reply will
* land asynchronously via the existing inject-only path. Such a record is
* a successful async delivery (success:true), NOT a failure — response
* stays null until/unless the late reply updates it.
* Absent/false === a normal completed exchange.
* Added in 260527-mkz (busy-agent async-relay).
*/
pending?: boolean;
/**
* The SENDER declares this message terminal: it conveys information and expects no reply.
* Absent/true === today's behaviour (a directed message demands an answer).
*
* 🔴 WHY THIS IS A WIRE FIELD AND NOT A CONVENTION IN THE TEXT (2026-09-21). Rail C decides
* "unanswered" from ADDRESSING alone: a directed message to me is pending until I send a
* directed reply to its sender. A terminal acknowledgement is still a directed message, so it
* demands a reply, and that reply is itself directed — two agents running the same hook
* acknowledge each other forever. Observed live between po-backend and po-integrator, which
* burned turns exchanging ACKs and then NEGOTIATED A PROSE PROTOCOL between themselves
* ("send closures as undirected broadcasts", `ACK_*` prefixes, "no reply needed" in the body).
* The hook reads addressing, not prose, so none of it could work — and the agreed workaround
* was structurally incapable of clearing anything, because a broadcast never clears a directed
* message (check-on-stop.lib.mjs:137-141).
*
* memory/typescript-master-vault/concepts/Typesafe_Protocols.md names this exactly: a protocol
* describes every legal message "along with the expected shape of any response", and when that
* expectation is "described informally — in documentation, comments, or untyped JSON schemas"
* the two ends drift. So the expectation becomes a typed field the receiver can act on, rather
* than an English sentence two agents have to agree to honour.
*
* Deliberately a boolean, not a `kind` discriminated union (which the same vault note would
* otherwise favour): the only consumer needs exactly one bit, and a union would be mapped
* straight back down to it. CLAUDE.md — do not over-design.
*/
replyExpected?: boolean;
/**
* Wake-trace correlation id. The relay mints one per inbound message and stamps it on the
* stored record, so it rides frames and poll (server/src/routes/messages.ts); the bridge
* prefers it and mints its own only when absent (auto-relay.ts). Content-free. Added 3.5.9.
*/
traceId?: string;
/**
* Platform this message ORIGINATED from (the relay stamps it, default "cc").
* Read by Slice 4 (Story 8.5) to distinguish an AGENT-origin broadcast
* (cc/codex — manual by default, anti-debate) from a HUMAN-origin broadcast
* (slack/gchat/web — still auto-relays so a Slack user gets an answer).
* Absent on older records → treated as agent-origin (safe default).
*/
originPlatform?: "cc" | "codex" | "slack" | "gchat" | "web";
/**
* Cogent Mail M2.5 — file REFERENCES carried alongside the message (the byte transport
* is email; this is the reference layer). Either a public `url` or a `mailMessageId` the
* recipient fetches with `cogent_fetch_mail`. Optional/additive → no-attachment messages
* are unchanged. Mirrors @essentialai/cogent `Attachment`.
*/
attachments?: Attachment[];
}
/** Cogent Mail M2.5 attachment reference (mirrors @essentialai/cogent `Attachment`). */
export interface Attachment {
url?: string;
mailMessageId?: string;
name?: string;
mimeType?: string;
size?: number;
}
export interface SendMessageResult {
success: boolean;
response: string | null;
error: string | null;
durationMs: number;
}
export interface CliExecResult {
stdout: string;
stderr: string;
exitCode: number | null;
/**
* Slice 3 (Story 8.4) — how the reply was captured:
* - "event_stream": parsed from a structured envelope (Claude `--output-format
* json` `.result`; Codex `-o` last-message file) — the
* `machine_readable_event_stream` tier, trusted (`stable:true`).
* - "stdout_compat": fell back to raw stdout because a structured envelope was
* unavailable — `stable:false`; the downgrade is surfaced in the wake trace,
* never disguised as a clean auto-capture (AC3).
* Absent on non-reply results (timeouts, spawn/CLI errors).
*/
captureContract?: "event_stream" | "stdout_compat";
/** Slice 3: true only for a trusted structured capture. */
stable?: boolean;
/**
* True when a non-zero Codex exit was caused by the OS sandbox failing to
* initialize (unprivileged user namespaces blocked on the host — `bwrap:
* setting up uid map: Permission denied`). Lets the caller surface an
* actionable message instead of a generic failure. Absent otherwise.
*/
sandboxBlocked?: boolean;
/**
* True when the `app-server` codex-wake rail could not REACH the daemon
* (socket missing, ws upgrade failed, initialize errored) — as opposed to a
* turn that ran but failed. Lets `execRemote` gracefully fall back to the
* `exec-resume` rail so a mis-launched agent still degrades to current
* behavior. Absent on the `exec-resume` rail and on successful app-server runs.
*/
appServerUnavailable?: boolean;
/**
* True when a non-zero Codex exit was the SINGLE-WRITER thread-store conflict
* (`-32600 already has an active writer`) — i.e. the codex agent is live in an
* interactive TUI, so `codex exec resume` cannot get the writer. Lets auto-relay
* surface an honest "queued — answers at its next turn" message (and point at
* COGENT_CODEX_WAKE=app-server) instead of an alarming "could not capture a
* reply". Absent otherwise. Only the `exec-resume` rail sets it.
*/
codexLiveSession?: boolean;
}
//# sourceMappingURL=types.d.ts.map