import type { Employee, McpGlobalConfig } from "../shared/types.js"; /** * GRS-017e — the default-attachment machinery for the built-in `jinn` MCP * server (the company toolset). * * ONE decision point ({@link decideJinnAttachment}) answers "should this * session get the jinn toolset?" by composing, most-specific-last: * * 1. engine capability — an engine without a proven per-session MCP * lever can NEVER attach (hard gate, nothing * overrides it); * 2. the master switch — `mcp.gateway.enabled`: `false` is the global * KILL SWITCH (beats everything below), `true` * is on, absent falls through to the shipped * default ({@link JINN_ATTACH_DEFAULT}); * 3. per-engine opt-out — `mcp.gateway.engines.: false` detaches * one engine (a known-broken adapter shouldn't * attach) and beats per-employee force-on: * correctness over preference; * 4. per-employee override — `employee.jinnMcp: true|false` force-attaches/ * force-detaches ONE employee. The jinn-specific * field beats the general `mcp:` field * (specific-over-general); force-on works even * with no `mcp:` section at all, enabling a * single-employee pilot before the global flip. * The existing `mcp: false` / allowlist semantics * are unchanged when `jinnMcp` is unset; * 5. the default — {@link JINN_ATTACH_DEFAULT}, ON since v0.26. * * …and then the AUTHED-SMOKE GATE is a MANDATORY CONJUNCT of every positive * result (GRS-017e-fix, codex finding 1): whatever arm said "attach" — master * on, employee override, or the shipped default — the decision only stands if the gate * is armed AND passed ({@link runJinnAuthedSmokeTest} verified the builtin * server can authenticate on THIS gateway). Failed gate → no attach (broken * tools are worse than no tools; the reason was logged at arm time). UNARMED * gate → no attach either: an attach path whose probe has not run yet is * uncertainty, and this module fails CLOSED under uncertainty. Negative * decisions never consult the gate. * * The resolver (mcp/resolver.ts) is the only production caller; jinn-server * membership in a resolved set is decided HERE and nowhere else. */ /** * Shipped default for `mcp.gateway.enabled: `. Since v0.26, employee * sessions on MCP-capable engines receive the built-in company toolset by * default. `mcp.gateway.enabled: false` remains the global kill switch. */ export declare const JINN_ATTACH_DEFAULT = true; /** * Engines whose adapters can consume a resolved MCP server set (grounded in * reports/research/GRS-012-mcp-engine-support-matrix.md + the GRS-012c probe): * - claude: consumes it via `--mcp-config` (temp file). * - codex: per-session `-c mcp_servers.*` argv overrides, with scoped * capability carried by a 0600 profile file. * - hermes: native ACP `mcpServers` param. * - grok: session-scoped `/.grok/config.toml` written before spawn and * torn down on settle (GRS-012c; grok has no per-invocation config * flag, so the project-scoped file is its only per-session lever). * - pi: per-session generated `--extension` file; id/capability ride the * Pi child env. * - antigravity: guarded Gemini MCP config entry plus per-session child env. * (Lives here, not in resolver.ts, so the attachment decision can consult it * without an import cycle; resolver.ts re-exports it.) */ export declare const MCP_CAPABLE_ENGINES: ReadonlySet; /** Whether an engine's adapter can consume a resolved MCP server set. */ export declare function isMcpCapableEngine(engine: string | undefined): boolean; /** Result of the authed smoke probe — the gate's state when armed. */ export type JinnSmokeResult = { ok: true; } | { ok: false; reason: string; }; /** The attach decision plus a human-readable reason (logged/reported, never parsed). */ export interface JinnAttachDecision { attach: boolean; reason: string; } /** Current gate state (null = unarmed). */ export declare function getJinnAttachGate(): JinnSmokeResult | null; /** Set (or reset with null) the gate state. Production callers use * {@link armJinnAttachGate}; direct set is for tests. */ export declare function setJinnAttachGate(result: JinnSmokeResult | null): void; /** * Should jinn attachment be ON gateway-wide (before engine/employee scoping)? * This is also the "must the smoke gate be armed?" predicate: the probe only * runs only when broad attachment would actually happen. */ export declare function jinnAttachGloballyOn(globalMcp: McpGlobalConfig | undefined): boolean; /** * The single "should this session get the jinn toolset?" decision. * Pure given its inputs; when `gate` is omitted it reads the module gate state * (the resolver's path — the one impurity, isolated to this default). * * Structure (GRS-017e-fix, codex finding 1): the precedence rules produce a * WOULD-ATTACH verdict; the authed-smoke gate is then applied as a MANDATORY * CONJUNCT of every positive verdict — no attach arm can route around it, and * an unarmed gate fails CLOSED. Negative verdicts return without consulting * the gate (default-off stays byte-identical, zero probes). */ export declare function decideJinnAttachment(opts: { globalMcp: McpGlobalConfig | undefined; employee?: Employee; engine?: string; /** Explicit gate for pure/unit use; omitted = module state. */ gate?: JinnSmokeResult | null; }): JinnAttachDecision; /** * Read the gateway bearer from the 0600 `/gateway.json` — the SAME file, * shape check included, that the builtin server's `resolveServerToken` * (mcp/server.ts) falls back to when an engine (codex, probe-verified) gives * MCP subprocesses a clean env. Single-sourced here so the smoke probe and the * server can never drift apart on what "the child's token" means. * * Default home = the INSTANCE-AWARE `JINN_HOME` from shared/paths (GRS-017e-fix * finding 3): the same resolution the rest of the gateway uses (env JINN_HOME → * `~/.${JINN_INSTANCE}` → `~/.jinn`), and the same value `buildJinnServerSpec` * hands the child. A hardcoded `~/.jinn` fallback would probe the WRONG * instance's credential file on a multi-instance host. */ export declare function readGatewayJsonToken(home?: string): string | undefined; /** * The authed smoke test (GRS-017 design §6 step 2 — "the authed-token path has * never been exercised under real auth" hole, closed as a GATE). * * Form (argued): an in-process probe that authenticates the way the CHILD's * worst-case channel does — the bearer read from `/gateway.json` (the * codex clean-env fallback GRS-018 §3b built), NOT the in-process * JINN_GATEWAY_TOKEN env, which the gateway boot itself exports and is * therefore tautologically valid — driven through one real HTTP GET against a * privileged read route (`/api/org`, the cheapest call the belt actually * makes). This verifies, in one loopback round-trip with no child-process * management: the URL the resolver hands out is reachable, the token file the * child will read resolves, and the gateway accepts it (or needs no auth — * an auth-disabled gateway probing 200 unauthenticated is a WORKING config). * A full spawn-the-server-over-stdio probe was rejected as the gate form: * spawn/argv/env mechanics are engine-side contracts already pinned per-engine * by engine-wiring.test.ts, and a boot-time child process + JSON-RPC handshake * buys no additional auth signal for its cost. */ export declare function runJinnAuthedSmokeTest(opts: { gatewayUrl: string; home?: string; fetchFn?: typeof fetch; timeoutMs?: number; }): Promise; /** True when at least one employee force-attaches the belt (`jinnMcp: true`) — * the pilot path must be smoke-probed exactly like broad attachment. */ export declare function anyEmployeeForcesJinn(employees?: Iterable): boolean; /** * Arm (or disarm) the gate for the current config + org. Called at gateway * boot (right after listen — the probe is a loopback call to this very * server), on every config hot-reload, and on every ORG reload (an employee * YAML gaining `jinnMcp: true` must trigger the probe too — codex finding 1). * * Fail-closed re-arm (codex finding 2): when any attach path is possible, the * gate is set to a DENYING "probe in flight" state SYNCHRONOUSLY (before the * first await), so a stale `{ok:true}` can never serve attach decisions while * the new config's probe is pending. Callers may fire-and-forget: the window * between call and probe-landing denies, never widens. * * Nothing-can-attach (globally off AND no force-on employee) → gate reset to * null with ZERO probes — the default path makes no extra calls, and the * decision conjunct fails closed on null anyway. Probe failure logs loudly; * every attach decision degrades to no-attach until a later arm passes. */ export declare function armJinnAttachGate(globalMcp: McpGlobalConfig | undefined, opts: { gatewayUrl: string; home?: string; fetchFn?: typeof fetch; log?: { info(msg: string): void; warn(msg: string): void; }; /** Org registry values — scanned for `jinnMcp: true` pilots. */ employees?: Iterable; }): Promise; //# sourceMappingURL=attachment.d.ts.map