/** * How this build was launched. It decides what the fix actually is, which is not * the same command in each case: a global install stays pinned until someone * reinstalls it, an npx cache re-resolves on the next cold start, and a version * pinned in an MCP config is not fixable by any command at all. */ export type InstallKind = 'global' | 'npx' | 'local' | 'unknown'; export interface UpdateInfo { current: string; latest: string; /** False when the installed build is current, or ahead of the registry. */ isOutdated: boolean; /** Absent when the caller did not care how the build was installed. */ installKind?: InstallKind; /** One line on what `latest` adds, when that release published one. */ highlight?: string; } /** What the registry says about the newest release. */ export interface LatestRelease { version: string; highlight?: string; } export interface UpdateCheckOptions { current: string; /** * Injected in tests; defaults to the real registry lookup. A bare version * string is a release with no highlight. */ fetchLatest?: () => Promise; cachePath?: string; now?: number; env?: NodeJS.ProcessEnv; /** Injected in tests; defaults to detecting how this build was launched. */ installKind?: InstallKind; } export type CachedUpdateOptions = Omit; /** * The one function in this file that talks to the network. * * Exported for the tests: every other test in the suite injects `fetchLatest`, * which left this -- the only code path that runs in production -- unexercised. * * `accept: application/json` on purpose, and it is the whole bug fix. The * abbreviated `application/vnd.npm.install-v1+json` type is only defined for * the packument endpoint (`/`); on `//latest` the registry answers * 406 with an empty body, which `!res.ok` then reported as "no update * available". How often it does that varies by edge and by day -- measured at * both 0% and 100% of requests hours apart from one machine -- so it reads as a * flake but can silently disable the notice outright, which is what it did for * every release from 0.4.0 on. It also buys nothing here: `/latest` is a single * ~3 KB manifest, where the abbreviated packument is ~9 KB. * * The retry below is deliberately not the fix -- two attempts against a 406 * both fail. It only covers genuinely transient trouble. */ export declare function fetchLatestFromRegistry(): Promise; /** * The version, and the release's highlight when it carries one. * * `/latest` returns the published `package.json` whole, custom fields included, * so a release can say what it adds without a second request: a top-level * `releaseHighlight: { version, text }`. The `version` inside it must match the * release's own -- a highlight left over from an earlier release, because the * release commit forgot to replace it, is dropped rather than shown as news. */ export declare function parseLatestManifest(body: unknown): LatestRelease | null; /** * Compares `major.minor.patch`, treating a prerelease as below its own release * so a local `0.3.0-rc.1` is not reported as newer than published `0.3.0`. * * Hand-rolled on purpose: `semver` would be the package's only runtime * dependency added for a single comparison, and this project's install story * ("no toolchain, no surprises") is worth more than full range support. */ export declare function isNewer(latest: string, current: string): boolean; /** * Classifies an install from the path this module was loaded from. * * Read from `import.meta.url`, never `process.argv[1]`: a global install is * reached through a symlinked bin, and argv[1] can name the link rather than the * file, which makes a global install look like a bare script. * * A heuristic, and treated as one -- every branch still names a real fix, and * `unknown` says both of the likely ones rather than guessing between them. */ export declare function detectInstallKind(moduleUrl: string): InstallKind; /** Memoised: the answer cannot change while the process is alive. */ export declare function installKind(): InstallKind; /** * What to actually do about a stale install, given how it was installed. * * An undefined kind means the caller did not detect one; the global command is * the right default, because a global install is the one that goes stale. */ export declare function updateCommand(kind?: InstallKind): string; /** * One line, for a channel that gets exactly one line. * * Deliberately a statement of fact and not an instruction: this text is read by * a model before it reaches a person, and a server that tells the model what to * say is doing prompt injection against its own users. */ export declare function formatUpdateNotice(info: UpdateInfo): string; /** * Reports whether a newer release is on the registry. * * Never throws and never blocks for longer than the timeout. Returns null when * the answer is unknown (offline, opted out) rather than guessing. * * The MCP server calls this too, but only in the background once the JSON-RPC * handshake is done -- see `src/mcp/notice.ts`. It is the one outbound request * this package makes: a GET for a version string, carrying no usage data and no * identifier, disabled by `AI_USAGE_NO_UPDATE_CHECK=1`. */ export declare function checkForUpdate(options: UpdateCheckOptions): Promise; /** * The same answer, from the cache only: synchronous, and it never touches the * network. * * This exists for the two places that cannot afford a fetch. The MCP server's * `instructions` are built before the transport is connected, so a fetch there * would add its timeout to every client's handshake; a resource read should * answer at once. Returns null until some earlier run has populated the cache, * which is the honest answer -- an unknown is not the same as up to date. */ export declare function readCachedUpdate(options: CachedUpdateOptions): UpdateInfo | null;