@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.
134 lines • 7.25 kB
TypeScript
import { CliExecResult } from "../types.js";
/**
* Every transcript directory that could hold this cwd's sessions.
*
* 🔴 WHY THIS IS NOT ONE PATH. Claude Code names a project dir by replacing each
* non-alphanumeric char of the cwd with "-", so EVERY representation of the same
* directory encodes to a DIFFERENT name: a trailing slash, a "." segment, or a
* symlinked path (macOS `/tmp` -> `/private/tmp`, `/var` -> `/private/var`) each
* produce a dir that does not exist. The cwd here is a string the AGENT supplied
* to cogent_register_peer — it is not guaranteed to be the canonical form.
*
* That mismatch is how a fresh dev machine got ZERO candidates from its very
* first register (2026-08-31) and refused every wake thereafter. The Codex rail
* had already canonicalised for this reason (codex-cli.ts `cwdCanonicals`); this
* is the Claude Code rail reaching parity.
*
* Exported so callers can LOG what was probed — a miss used to be indistinguishable
* from an empty dir, which is what made the failure undiagnosable from the logs.
*/
export declare function projectDirsFor(cwd: string): Promise<string[]>;
/**
* Every config root that could hold this machine's Claude Code transcripts,
* best-first.
*
* 🔴 WHY THIS IS A LIST. `claudeConfigDir()` returns ONE guess — the env var if
* this process happens to see it, otherwise `~/.claude`. A single guess has no
* way to be wrong loudly: it just scans a directory that does not exist and
* reports "no sessions". Users relocate their agent state (small system disks,
* synced or encrypted home dirs, multi-account machines, containers), and the
* env var does not always reach the process that needs it.
*
* Order is deliberate:
* 1. `CLAUDE_CONFIG_DIR` — an explicit instruction always wins.
* 2. LEARNED roots — recovered from transcript paths the agent itself reported
* (agent-sessions.ts). These are the only entries PROVEN to have held a real
* transcript, which is why they outrank the conventional defaults.
* 3. `~/.claude`, then `~/.config/claude` — the conventional locations.
*
* Probing a few extra directories costs one failed `readdir` each; missing the
* user's actual directory costs them the entire auto-wake feature.
*/
export declare function claudeConfigRoots(): Promise<string[]>;
/**
* The transcript this cwd's agent REPORTED for itself, if the record is still
* valid on disk. This is layer 1 — the authoritative path — and it bypasses both
* the root guess and the cwd-encoding guess entirely.
*
* Returns null when nothing was ever recorded (an agent running without our
* plugin) or when the recorded transcript has since been removed, in which case
* the caller falls back to discovery.
*/
export declare function recordedCandidate(cwd: string): Promise<{
sessionId: string;
mtimeMs: number;
} | null>;
/**
* One-line description of the transcript BASE dir and how many project dirs it
* holds. Used only when a resolve finds zero candidates, because it separates the
* two causes that look identical in the logs:
* • base absent / 0 project dirs → the bridge is looking at the WRONG config dir
* entirely (Claude Code's environment lives elsewhere and CLAUDE_CONFIG_DIR did
* not reach this process).
* • base holds N project dirs but none match → the CWD is the wrong one (or a
* representation this build cannot map — see {@link projectDirsFor}).
* Never throws.
*/
export declare function transcriptBaseSummary(): Promise<string>;
/**
* List all Claude Code session candidates for a cwd as {sessionId, mtimeMs}.
* Scans every {@link projectDirsFor} dir for *.jsonl — honoring CLAUDE_CONFIG_DIR
* (default ~/.claude). Returns [] on any error or an empty/absent dir (the
* resolver treats [] as "no candidates"). Never throws — Vault: Returning-null/
* Option error pattern (typescript-master-vault concepts/Error_Handling). Dirs
* and stats are probed in parallel (Vault: Parallelism); results are deduped by
* sessionId so two cwd forms resolving to one dir cannot inflate the count (a
* false ">1" would make the resolver call it "ambiguous" and refuse).
*/
export declare function listCandidates(cwd: string): Promise<{
sessionId: string;
mtimeMs: number;
}[]>;
/**
* Detect the most recently active Claude Code session for a given cwd (newest
* mtime wins). Returns null if none. Preserved for existing callers; new code
* should use the confidence-aware resolveSession() (session-resolver.ts).
*/
export declare function detectCurrentSession(cwd: string): Promise<string | null>;
/**
* Check whether a Claude Code session file exists on disk.
* Returns false if the file is missing (session ended or ID is wrong).
*
* Probes every {@link projectDirsFor} form — a pin validated under the canonical
* cwd must still validate when the agent registered a symlinked/trailing-slash
* representation of the same directory, or the resolver silently drops the pin
* and falls back to guessing by mtime.
*/
export declare function validateSession(sessionId: string, cwd: string): Promise<boolean>;
/**
* The config root that actually HOLDS this session's transcript, or null.
*
* 🔴 WHY DISCOVERY ALONE WAS NOT ENOUGH. {@link claudeConfigRoots} lets us FIND a
* session under any of several roots, but `claude --resume <id>` is a separate
* process that searches exactly ONE root — whatever `CLAUDE_CONFIG_DIR` says, or
* `~/.claude`. Finding a session in root B and then resuming against root A fails
* with an empty stdout and "No conversation found with session ID" on stderr,
* which upstream reported as the useless "busy, or produced no output" (shipped
* 3.21.0–3.21.3; measured against a real Claude Code on 2026-09-01).
*
* So resume must be told where discovery looked. The invariant: anything that can
* FIND a session must be able to say WHERE it found it.
*
* Layer 1 (the agent's own reported transcript) wins, exactly as in
* {@link listCandidates} — its root is a recorded fact rather than a search. Only
* then do we probe roots × cwd forms for `<root>/projects/<enc>/<id>.jsonl`.
* Returns null when no root holds it, in which case the caller must NOT invent a
* value — an inherited `CLAUDE_CONFIG_DIR` is still better than a guess.
*/
export declare function rootForSession(sessionId: string, cwd: string): Promise<string | null>;
/** COG-86: the only tools a default (`COGENT_WAKE_TOOLS=read`) wake gets. */
export declare const READ_ONLY_WAKE_TOOLS = "Read,Glob,Grep";
export declare const READ_ONLY_WAKE_NOTICE: string;
export declare function execClaude(sessionId: string, message: string, cwd: string, timeoutMs?: number): Promise<CliExecResult>;
/**
* Parse a Claude Code `--output-format json` result envelope. Returns the final
* assistant message (`.result`, or `null` when absent/non-string) plus whether
* the run reported an error — or `null` ONLY when the text is not a
* `type:"result"` envelope at all (→ caller uses the raw-stdout fallback).
* Slice 3 (Story 8.4).
*/
export declare function parseClaudeJsonReply(raw: string): {
result: string | null;
isError: boolean;
} | null;
//# sourceMappingURL=cc-cli.d.ts.map