/** * whoami Command - which session am I? (Issue #2376) * * commandmate whoami [--json] * * ## Why an agent cannot answer this for itself * * Every delegation command needs a worktree id and an instance id, and an agent * running INSIDE one of those sessions had no way to produce either. `ls` lists * every worktree on the machine without saying which row is the caller; * `instances` needs the worktree id you are trying to find out. So the first * step of "ask that other session something" was a human pasting two ids in. * * ## Where the answer comes from, in order * * 1. **The environment.** `CM_WORKTREE_ID` / `CM_INSTANCE_ID` / `CM_CLI_TOOL` * first, then the `CM_AGENT_*` correlation variables CommandMate already * puts on the launch line of every tool whose hooks ride there (codex, * copilot, gemini, antigravity, opencode, command-code — see * `AGENT_CORRELATION_ENV_VARS` in `lib/hooks/sources/launch-command.ts`). * Authoritative when present: the server wrote them when it started the * session. * 2. **The tmux session name.** claude is configured through `--settings` and * carries no launch-line environment at all, so for the most common agent * of the lot the environment says nothing. `mcbd--[-]` is * the name CommandMate gave the session (`lib/cli-tools/session-name.ts`), * and a shell inside the pane can read it back from tmux. Shelling out to * `tmux` rather than importing anything: the CLI build sets `paths: {}` and * an ESLint guard (Issue #1922) forbids `lib/tmux/**` outright — `attach` * already talks to the binary the same way. * * Neither one answering means this is not a CommandMate-started session, and * that is {@link NOT_IN_SESSION} rather than a guess. */ import { Command } from 'commander'; import type { AgentInstance } from '../types/api-responses'; import { ApiClient } from '../utils/api-client'; /** * Exit code for "this shell is not inside a CommandMate agent session". * * 3 by the Issue's contract. It is the numeric slot `ExitCode.START_FAILED` * occupies for the server-lifecycle commands, and it is spelled here as its own * constant rather than reused from that enum because the two mean nothing alike: * a caller branching on `whoami` exiting 3 is asking "am I inside a session", * not "did a server fail to start". `src/cli/types/index.ts` is where the shared * enum lives; adding a name to it is a separate change from this one. */ export declare const NOT_IN_SESSION = 3; /** How this identity was established. Reported so a wrong answer is traceable. */ export type IdentitySource = 'env' | 'tmux-session'; /** Who the caller is, as far as anything can say. */ export interface SessionIdentity { worktreeId: string; /** Instance ID. The primary instance is `instanceId === cliToolId` (#868). */ instanceId: string; cliToolId: string; source: IdentitySource; /** The tmux session, when the identity came from (or could be checked against) one. */ sessionName: string | null; /** * Every worktree id the server answered with while this identity was resolved, * or null/undefined when no list was read (Issue #2404). * * Carried rather than discarded because it is the whole evidence for * {@link detectServerMismatch}: the tmux path already fetches it to break the * `-` ambiguity, so "is my own worktree on the server I am dialling?" costs * no second request. Absent on the env path, which never asks — and that is * the reason a `source: 'env'` identity never reports a mismatch here. */ knownWorktreeIds?: Set | null; } /** * The caller is on a session one server started and is talking to another * (Issue #2404). */ export interface ServerMismatch { /** The worktree this session belongs to, as {@link resolveSessionIdentity} read it. */ worktreeId: string; /** The server this CLI is dialling — the one that does NOT list that worktree. */ serverUrl: string; /** Where the worktree id came from, so the warning can say how it is known. */ source: IdentitySource; } /** * The tmux session this process is running inside, or null. * * `$TMUX` is checked first so a call from an ordinary shell costs no process * spawn at all — `whoami` outside a session is the case that has to be cheap, * because a script that probes for CommandMate runs it once per invocation. * * @param env - Environment to read * @returns The session name, or null when there is no tmux to ask */ export declare function readTmuxSessionName(env?: Readonly>): string | null; /** A session name split into the parts {@link resolveSessionName} put there. */ interface ParsedSessionName { cliToolId: string; /** Everything after `mcbd--`: a worktree id, possibly plus `-`. */ remainder: string; } /** * Split `mcbd--` into its tool and the rest. * * Matched against the tool ids longest-first, because two of them contain a * hyphen (`vibe-local`, `command-code`) and a shortest-first scan would read * `mcbd-command-code-wt` as the tool `command` — which is not a tool, but the * scan does not know that until it has already split in the wrong place. * * @param sessionName - A tmux session name * @returns The parts, or null when this is not a CommandMate session name */ export declare function parseSessionName(sessionName: string): ParsedSessionName | null; /** * Work out which session this process is running inside. * * @param client - API client, used only to disambiguate a session name * @param env - Environment to read * @returns The identity, or null when nothing establishes one */ export declare function resolveSessionIdentity(client: ApiClient, env?: Readonly>): Promise; /** * Whether the caller's own worktree is missing from the server it is dialling. * * The contradiction this names was, until now, unnameable: `whoami` answers * from the tmux session name and therefore succeeds, while `ls` / `instances` / * `peers` answer from a *different* server's ledger and therefore say the * worktree does not exist. Both are correct and they disagree, and an agent * reading them concluded the reasonable, wrong thing — "I must not be in the * ledger" — and delegated its work into an unrelated worktree on the other * server (#2403). Exit codes said nothing: `ask` returned 0. * * Deliberately NOT gated on {@link IdentitySource}. The gate is having a list at * all: the env path never fetches one, so it passes null and cannot flag, while * `peers` — which has just fetched the whole list for its own listing — can * check an env-derived identity for free. A missing list is never a mismatch: * a stopped server must not be reported as the wrong server. * * @param identity - Who the caller is * @param knownWorktreeIds - Worktree ids from the server, or null when unread * @param serverUrl - The URL those ids came from ({@link ApiClient.serverUrl}) * @returns The mismatch, or null when there is nothing to report */ export declare function detectServerMismatch(identity: SessionIdentity, knownWorktreeIds: Set | null | undefined, serverUrl: string): ServerMismatch | null; /** * The multi-line warning printed when {@link detectServerMismatch} fires. * * Says what is wrong, why it is not a typo, and what it costs to ignore — * because the failure it describes is silent by construction: every command * involved keeps exiting 0 while answering about the wrong machine's worktrees. */ export declare function formatServerMismatchWarning(mismatch: ServerMismatch): string; /** The one-line form, for a command that has already failed for another reason. */ export declare function formatServerMismatchHint(mismatch: ServerMismatch): string; /** * Resolve the caller's identity and report a mismatch, or null. * * For commands that only need this on a failure path: it costs a `tmux * display-message` and one `GET /api/worktrees`, and it is called after the * request that already failed. Never throws — a diagnosis that fails must not * replace the error it was diagnosing. * * @param client - The client whose 404 is being explained */ export declare function describeServerMismatch(client: ApiClient): Promise; /** * The roster row for this identity, or null. * * Best effort: the alias is a nicety (it is what the GUI calls this session), * and a worktree whose roster cannot be read still has a usable identity. */ export declare function readOwnRosterEntry(client: ApiClient, identity: SessionIdentity): Promise; /** The sentence printed when nothing establishes an identity. */ export declare const NOT_IN_SESSION_MESSAGE: string; export declare function createWhoamiCommand(): Command; export {}; //# sourceMappingURL=whoami.d.ts.map