/** * `MuseClient` — the SS7.1 facade's session-opening half (FR-018, T030). * * It turns the two v1 session verbs into correctly-shaped `session/start` / * `session/resume` params and hands back a WIRED `Session`. It frames nothing * and mints nothing: `Connection.command()` stamps the `commandId` from the * single injected mint, so INV-013's single-minter property is preserved by * DELEGATION rather than re-implemented here. * * IT OWNS TWO FACTS NO OTHER LAYER HOLDS, and that is why obligations (b) and * (c) of T030 live here rather than on `Session`: * * - WHETHER A CLOSE WAS OURS. `Connection` is duplex-generic; a transport * cannot tell a peer's hang-up from its own shutdown. SS2.13.3b counts * "process exit **or transport EOF**" as a death, so somebody has to know * the difference, and only the layer that owns `close()` does. * - WHAT AN EARLIER DEATH DISCARDED. `DiscardedSessions` is client-scoped, so * a `commandId` refusal and a withheld reattach survive the `Session` object * that died — which is exactly what "do not replay the SESSION's commandIds" * requires of a client that then opens a new session. * * OPTIONAL MEANS OMITTED, NEVER `null` (tdd SS1.2). Every member this module * can leave unset is optional-non-nullable in the generated params, so an * unset member is dropped from the frame rather than nulled. The guards below * are LOOSE (`!= null`) on purpose: the types only bind strict TypeScript * consumers, and a plain-JS caller reaching the built `dist` can pass an * explicit `null` that a `!== undefined` guard would forward verbatim. `false * != null` is true, so `excludeItems: false` still serializes. * * The params and the caller options are both COMPOSED from the generated * types rather than restated (INV-001: narrow and compose, never restate). * Composition alone does NOT stop drift, though: the guard chains below are * hand-listed, so a regenerated member would be settable by a caller and then * silently dropped on the way to the wire. The `AssertNever` pins next to each * chain close that gap — add a member upstream and the BUILD fails here until * it is forwarded (or explicitly allow-listed as unforwardable). */ import type { Connection } from "../connection/connection.js"; import type { ExitClassification, SpawnedMspConnection } from "../connection/spawn.js"; import type { SessionDurabilityProfile } from "./host-death.js"; import type { ApprovalFailureHandler, ApprovalHandler } from "./approval.js"; import { Session } from "./session.js"; import type { InitializeParams, InitializeResult, SessionResumeParams, SessionStartParams } from "../msp.js"; /** * Caller-facing options for `session/start` (tdd SS2.5.1). * * `config` is excluded as well as `commandId` until the facade implements the * required `sessionMcp` grant check and forwarding. */ export type StartSessionOptions = Readonly & { /** * Require the `permissionProfile` selection (TEST-42624-10): when set, * the client proves the committed selection equals the requested id — * from the start result's `session.permissionState`, else a * `session/read` read-back — and throws `PermissionProfileNotApplied` * instead of proceeding on the host default. Client-local: never sent * on the wire. Setting it without `permissionProfile` is a usage error. */ requirePermissionProfile?: boolean; }>; /** Caller-facing options for `session/resume` (tdd SS2.5.2). */ export type ResumeSessionOptions = Readonly & { /** * Answers this session's approvals from its first frame, so an approval * the host re-sends right after the resume reply reaches it. The same * contract as `session.onApproval`, installed before the frames that * arrived with the reply are applied. */ onApproval?: ApprovalHandler; /** * Hears this session's approval failures from its first frame, as * `session.onApprovalError` does later. A failure for an approval that * arrived with the resume reply can happen before `resumeSession` returns, * so only this option is sure to hear it. */ onApprovalError?: ApprovalFailureHandler; }>; /** * Options for building a `MuseClient` around a connection you already own. * `MuseClient.spawn` fills these in for you; supply them yourself only when * you compose the client with your own transport and handshake. */ export interface MuseClientOptions { /** * The handshake's `sessionDurability`, read through `readSessionDurability`. * * REQUIRED, for the same reason `SessionOptions.durability` is: it decides * what happens to every in-flight item and command when the host dies * (SS2.13.3b), and a default would let a caller skip reading the handshake * and silently inherit the wrong obligation. `MuseClient.spawn` supplies it. */ readonly durability: SessionDurabilityProfile; /** * The owned host, set by `MuseClient.spawn`. A client built around a * connection somebody else owns has none, and says so by omitting it — which * is why `close()` falls back to closing just the connection. */ readonly host?: SpawnedMspConnection; } /** What `MuseClient.spawn` needs: FR-018's `{ museBin, args?, env? }`, plus the handshake. */ export interface MuseClientSpawnOptions { readonly museBin: string; readonly args?: readonly string[]; readonly cwd?: string; /** REPLACES the child's environment (PATH included) — spread `process.env` to extend it. */ readonly env?: NodeJS.ProcessEnv; /** Client identification, forwarded verbatim into `initialize` (SS1.4). */ readonly clientInfo: InitializeParams["clientInfo"]; /** Capability posture; absent means all defaults. */ readonly capabilities?: InitializeParams["capabilities"]; /** Raw stderr chunks, drained from process birth and never parsed (INV-010). */ readonly onStderr?: (chunk: string) => void; /** See `MuseServeChildOptions.shutdownTimeoutMs`. */ readonly shutdownTimeoutMs?: number; } /** * Your entry point to the SDK. * * Spawn a host with `MuseClient.spawn`, then open conversations with * `startSession` or `resumeSession`. The client owns the connection: it * routes every server event to the session it belongs to and reports a host * death to all of them. Call `close` to shut the host down cleanly. */ export declare class MuseClient { #private; constructor(connection: Connection, options: MuseClientOptions); /** * Spawn an owned host, run the SS1.4 handshake, and hand back a client whose * durability profile came from that handshake (FR-018). * * The profile is READ rather than asked of the caller, which is the whole * reason this factory exists beside the bare constructor: SS2.13.1 makes the * absent/unrecognized distinction load-bearing, and a caller re-deriving it * by hand is a caller that can get it wrong. * * Rejects with {@link MuseHostStartError} when the host is lost before the * handshake completes (the binary could not be spawned, or the process * ended first — exit code or signal plus the bounded stderr tail; * `answeredInitialize` says whether it died after a usable answer). A host * that answers with a reply the SDK refuses keeps its own error: an * `MspError` for an SS1.6 refusal, or a `ProtocolError` (with the whole * refused frame on `line`) for a reply the SDK cannot use, such as one with * no schema fingerprint. */ static spawn(options: MuseClientSpawnOptions): Promise; /** * The handshake result, when this client was built by `spawn`. * * A plain `Error`, not a typed one: reading this off a client that was not * built by `spawn` is API misuse, not a protocol STATE an embedder branches * on — a typed class here would send the SS2.13.3b recovery path chasing a * host death that never happened. */ get initializeResult(): InitializeResult; /** The host's SS2.11 exit row, when this client was built by `spawn`. */ get exit(): Promise; /** The durability profile every session this client opens inherits. */ get durability(): SessionDurabilityProfile; /** Open a new root session (tdd SS2.5.1). */ startSession(options?: StartSessionOptions): Promise; /** * Load an existing session and subscribe this connection to its view * (tdd SS2.5.2). * * WITHHELD after an ephemeral host death (SS2.13.3b clause 2, "do not attempt * to reattach"), and refused on THIS side of the transport: a reattach * attempt that reaches the wire has already violated the clause, whatever the * server answers. */ resumeSession(options: ResumeSessionOptions): Promise; /** * Shut the host down in an orderly way (SS2.1.2); the shutdown this causes * is not reported to your sessions as a host death. * * The EOF this close produces is NOT a death, which is what `#closing` * records. * * `#closing` covers only the EOF; it must not swallow the EXIT ROW. When the * host ignores stdin EOF, `#child.close()` escalates to SIGTERM/SIGKILL and * the exit classifies as a crash — an abnormal death under SS2.13.3b no * matter who started the close, and one no transport event will ever report * here because `#closing` already claimed the EOF. So the classification the * close itself returns is forwarded to every session: `hostExited` answers * `notADeath` for `cleanShutdown`, keeping the orderly arm inert. */ close(): Promise; }