@takk/bayespredicts
v1.0.0
Published
BayesPredicts: Bayesian survival analysis for predictive maintenance of agents and infrastructure. Feed lifecycle events (start, stop, failure, still-running) and get calibrated time-to-failure forecasts with credible intervals, so you restart a component
Maintainers
Readme
BayesPredicts
Universal, zero-runtime-dependency NPM library and CLI for Bayesian survival analysis and predictive maintenance of agents and infrastructure. Feed the lifecycle events of a component, start, failure, stop, still-running, and get calibrated time-to-failure forecasts with credible intervals plus a cost-aware restart recommendation, so you restart a component before it dies instead of after, for Massive Intelligence (IM) systems and the non-human entities that run them.
BayesPredicts is the actuary of your system's components. A long-running agent starts healthy, then degrades after some hours. An MCP server runs for days, then crashes. Today nothing tells you how long until the next failure with a calibrated estimate, so teams restart on a fixed timer and pay for uptime they did not need, or wait and pay for an outage they could have prevented. BayesPredicts reads the life-and-death history of each component, fits a Bayesian survival model, and predicts the time to the next failure with honest uncertainty, then recommends whether to restart now or keep watching.
Core promise: zero required runtime dependencies, single-function setup, a fully conjugate Gamma-Exponential model with closed-form updates, a Weibull aging model with native right-censored-data support, posterior-predictive time-to-failure and residual-life prediction with credible intervals, a cost-aware restart advisor grounded in the classic age-replacement model, per-cohort estimation, a tamper-evident audit trail, a framework-agnostic tool a non-human entity can call to forecast its own failures, and a node-free core that runs in Node, edge runtimes, and the browser, ESM plus CJS dual distribution.
Why BayesPredicts
Reliability under uncertainty is a survival-analysis question, not an anomaly-detection one. An SRE says: "The agent starts fine. After a few hours latency degrades. We do not know whether it will recover or get worse. A preventive restart costs uptime; waiting costs a degraded experience." A platform engineer says: "The MCP server runs for days, sometimes it crashes, when should we restart it preemptively?" Anomaly detectors (PagerDuty, Datadog Watchdog) tell you something is wrong now; they do not forecast when a component will fail. Survival analysis does, and it is the correct primitive: model the lifetime, fold in the components still running (the censored majority), and compute the probability of failure over the next window, calibrated, not guessed.
What sets it apart from reactive monitoring and from the Python-only survival tooling (lifelines is the reference, with no production-ready NPM equivalent):
- Closed-form Bayesian updates. For the Exponential model the failure rate has a Gamma conjugate prior, so the posterior, the mean time to failure, the survival curve, and the lifetime quantiles are exact closed forms, not simulated. The Weibull model is conjugate at each fixed shape and weights a grid over shapes into a posterior, so survival, hazard, and the mean time to failure stay closed form per component while the shape itself is inferred numerically.
- Censored data is native, not an afterthought. Most of your fleet has not failed yet. A component that is still running at observation time is a right-censored observation that informs the posterior without being treated as a failure, which is exactly what makes the estimate honest when failures are rare.
- Calibrated intervals, measured not asserted. Every forecast carries a credible interval, and the calibration is verified by simulation: a 90% credible interval on the Exponential failure rate covers the true rate 89.2% of the time over 400 replications, and the Weibull shape interval 86.8%, both close to nominal. A deliberately misspecified model is shown to break calibration rather than hide it.
- A restart advisor that pays for itself. Two policies: a risk-threshold rule for a service-level objective, and the classic cost-optimal age-replacement model that finds the restart age minimizing long-run cost. On a simulated wear-out fleet, scored against the true lifetime, the cost-optimal advisor cuts long-run cost 75.3% versus run-to-failure and 63.1% versus restarting at the mean time to failure, while suffering about ten times fewer unplanned failures.
- Per-cohort estimation. Different model versions, hardware batches, and regions have different failure curves. BayesPredicts fits a posterior per cohort, pools a fleet baseline, ranks cohorts worst-first, and flags a regressing batch a fleet-wide average would hide.
- A tool a non-human entity can call on itself. The adapter exposes the whole pipeline as a framework-agnostic tool (name, description, JSON Schema, handler) that drops into an MCP server or an LLM tool-calling API, so a non-human entity (NHE) forecasts its own failures from its own telemetry.
- Proves what it predicted. A tamper-evident SHA-256 hash-chained audit trail of every forecast and decision, the evidence a compliance review asks for.
Install
pnpm add @takk/bayespredicts
# or: npm install @takk/bayespredicts
# or: yarn add @takk/bayespredicts
# or: bun add @takk/bayespredictsThe core has zero required runtime dependencies. Every @takk sibling is an optional peer; install only what you compose with.
Quickstart
Feed the lifecycle history of a component and ask how long until it fails and whether to restart.
import { analyze } from "@takk/bayespredicts";
// An agent that ran, failed, restarted, failed again, and is still running.
const result = analyze({
events: [
{ component: "agent-7", kind: "start", at: 0 },
{ component: "agent-7", kind: "failure", at: 41 },
{ component: "agent-7", kind: "start", at: 41 },
{ component: "agent-7", kind: "failure", at: 95 },
{ component: "agent-7", kind: "start", at: 95 },
{ component: "agent-7", kind: "censor", at: 130 },
],
age: 35, // the running instance is 35 hours old
horizons: [10, 30, 60],
policy: { kind: "risk-threshold", maxFailureProbability: 0.1, window: 10 },
});
console.log(result.model); // "weibull" or "exponential", chosen automatically
console.log(result.meanTimeToFailure.toFixed(1)); // expected lifetime, in your time unit
for (const point of result.forecast.failureProbabilityByHorizon) {
console.log(`P(fail within ${point.horizon}): ${(point.probability * 100).toFixed(1)}%`);
}
console.log(result.recommendation?.action); // "restart-now" or "monitor"A failure event ends a life as an observed failure; a stop or censor event ends it as right-censored (the component was still alive); a later start for the same component is a renewed life. analyze folds the events into observations, fits the model, forecasts conditional on the current age, and, when a policy is given, advises.
The restart advisor, when to act
Knowing when a component will probably fail is half of predictive maintenance; the other half is deciding whether to act. The cost-optimal policy implements the classic age-replacement model: it finds the restart age that minimizes long-run expected cost per unit time, trading the cost of a planned restart against the larger cost of an unplanned failure. For a memoryless (constant-hazard) lifetime the model yields run-to-failure on its own, which is correct.
import { advise, fitWeibull, optimalRestartAge } from "@takk/bayespredicts";
const model = fitWeibull(history); // history: { duration, censored }[]
const optimal = optimalRestartAge(model, /* plannedRestartCost */ 1, /* failureCost */ 25);
console.log(optimal.age); // the cost-minimizing restart age
const recommendation = advise(
model,
{ kind: "cost-optimal", plannedRestartCost: 1, failureCost: 25 },
{ age: 60 },
);
console.log(recommendation.action); // "restart-now" once age reaches the optimal restart age
console.log(recommendation.reason);On a simulated wear-out fleet, with the advisor fitting its model from a finite, noisy sample and every policy scored against the true lifetime distribution, the cost-optimal advisor cuts long-run cost 75.3% versus run-to-failure and 63.1% versus restarting at the fitted mean time to failure. Run it yourself: pnpm run build && node --import tsx benchmark/restart-policy.ts.
Survival models
Two models, the same interface, your choice of constant versus age-dependent hazard. The automatic choice fits the Weibull and keeps the simpler Exponential whenever the shape posterior is consistent with a constant hazard, so a constant hazard is never dressed up as wear-out on thin data.
| Model | Import | Hazard | Use it for |
|---|---|---|---|
| Exponential | @takk/bayespredicts/exponential | constant, memoryless | failures from random external shocks, not wear |
| Weibull | @takk/bayespredicts/weibull | rises (wear-out) or falls (infant mortality) with age | components that age, the common case |
| auto | @takk/bayespredicts/survival | chosen by the shape posterior | when you do not want to decide up front |
import { fitSurvival } from "@takk/bayespredicts";
const fit = fitSurvival(observations); // model: "auto" by default
console.log(fit.model); // "exponential" or "weibull"
console.log(fit.posterior.survival(24)); // P(lifetime > 24)
console.log(fit.posterior.hazard(24)); // instantaneous failure rate at age 24
console.log(fit.posterior.meanTimeToFailure()); // expected lifetime
console.log(fit.posterior.quantile(0.5)); // median lifetimePrediction conditional on age
Predictive maintenance asks a conditional question: given that a component has already run for some age without failing, how much longer will it last and how likely is a failure in the next window. Every prediction conditions on the current age, so a component that has already survived its risky infancy is judged on its remaining risk, not its original risk.
import { failureProbability, forecast, meanResidualLife, residualLifeInterval } from "@takk/bayespredicts";
const age = 12;
failureProbability(posterior, 6, { age }); // P(fail within the next 6, given survival to 12)
meanResidualLife(posterior, { age }); // expected remaining lifetime
residualLifeInterval(posterior, 0.95, { age }); // 95% credible interval on the remaining life
forecast(posterior, { age, horizons: [6, 24, 72] }); // a full structured forecastPer-cohort and fleet reliability
import { compareCohorts } from "@takk/bayespredicts";
const comparison = compareCohorts(observationsByCohort, { regressionTolerance: 0.2 });
console.log(comparison.fleet.meanTimeToFailure); // the pooled baseline
console.log(comparison.cohorts); // worst-first by mean time to failure
console.log(comparison.regressions.map((c) => c.cohort)); // cohorts shorter-lived than the fleetA new model version that fails sooner shows up as a regression instead of being averaged into a healthy fleet number.
A tool a non-human entity can call
The adapter turns the whole pipeline into a framework-agnostic tool. The shape matches what MCP servers and LLM tool-calling APIs expect, so a non-human entity (NHE) can forecast its own failures. Input arriving from a model is validated defensively, and the output is made JSON-safe.
import { bayesPredictsTool, runTool } from "@takk/bayespredicts";
bayesPredictsTool.name; // "bayespredicts_forecast"
bayesPredictsTool.inputSchema; // JSON Schema for the tool input
const output = runTool({
events: [
{ component: "self", kind: "start", at: 0 },
{ component: "self", kind: "failure", at: 41 },
],
age: 20,
horizons: [10, 30],
policy: { kind: "risk-threshold", maxFailureProbability: 0.1, window: 10 },
});Entry points
Fourteen subpath exports, each importable on its own. The core is node-free; only node touches a Node built-in.
| Import | What it gives you |
|---|---|
| @takk/bayespredicts | The analyze and MaintenanceMonitor facade plus the full toolkit. |
| @takk/bayespredicts/gamma | The Gamma posterior, the conjugate rate engine both models share. |
| @takk/bayespredicts/exponential | The Exponential survival model, Lomax closed form. |
| @takk/bayespredicts/weibull | The Weibull survival model, a shape-grid mixture. |
| @takk/bayespredicts/survival | fitSurvival and the automatic model choice. |
| @takk/bayespredicts/events | Folding lifecycle events into survival observations. |
| @takk/bayespredicts/predict | Conditional survival, failure probability, residual life, forecast. |
| @takk/bayespredicts/advisor | The restart advisor: risk-threshold and cost-optimal policies. |
| @takk/bayespredicts/cohort | Per-cohort fitting and fleet regression detection. |
| @takk/bayespredicts/calculator | analyze and the MaintenanceMonitor with an audit trail. |
| @takk/bayespredicts/adapter | The framework-agnostic MCP and LLM tool definition. |
| @takk/bayespredicts/audit | Append-only SHA-256 hash-chained audit log, via Web Crypto. |
| @takk/bayespredicts/node | File loaders for JSON and CSV history, over node:fs. |
| @takk/bayespredicts/edge | The node-free core, re-exported for edge runtimes and the browser. |
Tamper-evident audit trail
import { AuditChain, verifyChain } from "@takk/bayespredicts";
const chain = new AuditChain();
await chain.append({ kind: "forecast", component: "agent-7", failureProbability: 0.14 });
await chain.append({ kind: "decision", action: "restart-now" });
await verifyChain(chain.toArray()); // { valid: true }, until any entry is alteredEach entry is recorded append-only and chained to the previous one through a SHA-256 hash of its canonical form. Any later edit, deletion, or reordering breaks the chain and verifyChain reports the first broken index. The seal is an integrity seal, not a digital signature: it makes tampering detectable, not impossible. The chain uses the Web Crypto API, so the audit surface runs in Node, edge runtimes, and the browser. The MaintenanceMonitor records every prediction into such a chain automatically.
Governance
Every prediction can be recorded and replayed. The MaintenanceMonitor appends each analysis to a tamper-evident audit chain, so every restart a non-human entity acts on is explainable after the fact, with the forecast and the policy it was decided on. No runtime dependency is taken; you wire the chain to your own telemetry and compliance stack. This is the governance seam for Massive Intelligence (IM) systems.
CLI
BayesPredicts ships a command-line tool that runs the real engine, with every number produced by execution.
# Fit a survival model from a history file and print the posterior summary.
npx @takk/bayespredicts fit history.json
# Forecast time-to-failure at a current age over several horizons.
npx @takk/bayespredicts predict history.json --age 12 --horizon 6 --horizon 24
# Recommend whether to restart under a cost-optimal policy.
npx @takk/bayespredicts advise history.json --planned-cost 1 --failure-cost 25
# Verify the integrity of an audit-chain JSON file.
npx @takk/bayespredicts audit-verify chain.jsonHistory is a JSON array of lifecycle events or of observations ({ duration, censored }), or a CSV with duration,censored columns. Flags: --model, --format, --age, --horizon, --json. See examples/ for runnable demos, and benchmark/ for the throughput and value benchmarks.
The math, in one paragraph
A lifetime is described by a survival function S(t) = P(lifetime > t) and a hazard h(t). For the Exponential model the lifetime is memoryless with a constant rate that has a Gamma conjugate prior, so the posterior is Gamma in closed form and integrating the survival over it yields a Lomax (Pareto type II) tail, making survival, the cumulative failure probability, the mean time to failure, and the lifetime quantiles exact. The Weibull model is not conjugate, but at a fixed shape the transformed rate is Gamma-conjugate on the transformed exposure, so a grid over candidate shapes is weighted into a posterior by the marginal likelihood and the fitted posterior becomes a finite mixture of Gamma-rate components; survival, hazard, and the mean time to failure are closed form per component and quantiles invert the monotone mixture numerically. Right-censored observations, the components still running, contribute to the likelihood through the survival rather than the density. Bayes' theorem is the engine, and the restart advisor minimizes the long-run expected cost rate of the age-replacement model over the fitted posterior. The data is yours; the calibration is the library's.
Benchmark
Two benchmarks, every number from real execution against the compiled dist, never invented.
The value benchmark scores the restart advisor against naive policies on a simulated wear-out fleet (Weibull shape 2.5, scale 100, failure cost 25, restart cost 1). The advisor fits its model from a finite, noisy 500-observation sample, and every policy is then scored against the true lifetime distribution over 400000 renewal cycles:
| Policy | Long-run cost per hour | Unplanned failures per hour | |---|---|---| | Cost-optimal advisor | 0.0696 | 0.0012 | | Naive restart at fitted MTBF | 0.1888 | 0.0073 | | Naive restart at half MTBF | 0.0947 | 0.0030 | | Run-to-failure | 0.2818 | 0.0113 |
The cost-optimal advisor cuts long-run cost 75.3% versus run-to-failure and 63.1% versus restarting at the mean time to failure, with about ten times fewer unplanned failures. The throughput benchmark reports an Exponential fit in about 0.04 ms, a Weibull fit (60-point shape grid) in about 2 ms, on 2000 observations. Run them: node --import tsx benchmark/restart-policy.ts and node --import tsx benchmark/benchmark.ts.
Quality
- 148 tests across 16 suites, all passing under Vitest, including the survival functions verified against Monte Carlo oracles, parameter recovery on simulated data, the cost-optimal advisor verified to yield run-to-failure for memoryless lifetimes, and simulation-based calibration that measures interval coverage rather than asserting it.
- Coverage: statements 94.70%, branches 86.82%, functions 99.36%, lines 96.45%.
- Lint clean under Biome.
- Typecheck clean under TypeScript in maximum strict mode (
exactOptionalPropertyTypes,useUnknownInCatchVariables,noUncheckedIndexedAccess,noPropertyAccessFromIndexSignature). publintclean and@arethetypeswrong/cligreen across all fourteen subpaths.size-limitunder budget on every bundle (brotli core facade 4.81 kB).- Distribution smoke test exercising the compiled ESM and CJS artifacts and the compiled CLI spawned as a single Node process. The full suite, the smoke test, and the CLI are re-verified on Node 24.
- Zero required runtime dependencies; a node-free core that runs in Node, edge runtimes, and the browser; dual ESM plus CJS; TypeScript-first.
See SPEC.md for the formal specification, public surface, and stability promise.
FAQ
How is this different from anomaly detection? Anomaly detection tells you a metric looks wrong right now. BayesPredicts forecasts when a component will fail, with a calibrated probability over a future window and a credible interval on the remaining life, so you can act before the failure rather than alert during it.
What if most of my components have not failed yet? That is the normal case, and it is handled natively. A component still running is a right-censored observation that informs the posterior without being counted as a failure. Survival analysis is built precisely for data where most subjects have not yet experienced the event.
Exponential or Weibull?
Leave the model on auto. It fits the Weibull and keeps the simpler, constant-hazard Exponential whenever the shape posterior is consistent with a constant hazard, so you do not over-fit wear-out on thin data. Force either with { model: "exponential" } or { model: "weibull" }.
Does it actually restart my components? No. BayesPredicts produces the recommendation, restart now or keep monitoring, with the reasoning and the optimal restart age. Your orchestrator (Kubernetes, ECS, a Hermes runtime hook, your own controller) consumes that decision and acts. Turnkey orchestrator integrations are on the roadmap.
Is the calibration real?
It is measured. The test suite runs simulation-based calibration and reports the empirical coverage of the credible intervals, and shows that a misspecified model breaks calibration rather than passing as nominal. The numbers in this README come from that suite and from the benchmarks, run against the compiled dist.
Does this work in Cloudflare Workers, Vercel Edge, Bun, and Deno?
Yes. The core is node-free; the audit seal uses the Web Crypto API, not node:crypto. Import @takk/bayespredicts or @takk/bayespredicts/edge anywhere with Web Crypto. Only @takk/bayespredicts/node requires Node.
Contributing
See .github/CONTRIBUTING.md for the contributor guide. Substantive proposals open a GitHub Issue first; trivial fixes can go straight to a PR. All commits require DCO sign-off (git commit -s). Non-trivial contributions are governed by the Contributor License Agreement.
Community and support
- Issues and feature requests. Open a GitHub issue at
davccavalcante/bayespredicts/issues. Include the package version, a minimal reproduction, the history you fed in, and where relevant theanalyze()output. - Security disclosures. Do not open public issues for vulnerabilities. Follow the responsible-disclosure flow in
SECURITY.md, contact[email protected](or[email protected]) with the[SECURITY]prefix. - Code of Conduct. This project follows the Contributor Covenant 2.1. Participation in any BayesPredicts space implies agreement.
- Contributions. All non-trivial contributions go through the Contributor License Agreement. Tests, lint, typecheck, and build must be green before review (
pnpm verify).
Author
Created by David C Cavalcante, [email protected] (preferred), [email protected] (Takk relay), linkedin.com/in/hellodav, x.com/davccavalcante, takk.ag.
BayesPredicts is part of a broader portfolio of NPM packages targeting Massive Intelligence (IM) native infrastructure for 2026-2030, built at Takk Innovate Studio. Predictive maintenance with calibrated time-to-failure forecasts is a strong default for that era, when fleets of long-running non-human entities must forecast their own failures.
Related research by the author
The architectural philosophy behind BayesPredicts, separating the models, the prediction, the advisor, and persistence into composable, independently-governed layers, echoes the author's research frameworks:
- MAIC (Massive Artificial Intelligence Consciousness), a systemic intelligence framework designed to coordinate, supervise, and govern large-scale Massive Intelligence ecosystems, providing global context awareness, alignment, and orchestration across multiple models, agents, and decision layers.
- HIM (Hybrid Entity Intelligence Model), a hybrid intelligence layer that integrates Massive Intelligence systems with human-defined logic, rules, heuristics, and strategic intent, interpreting objectives and structuring decision-making before and after model execution.
- NHE (Noumenal Higher-order Entity), a non-human cognitive entity with a defined functional identity and operational agency within a Massive Intelligence ecosystem, operating through coordinated intelligence layers while maintaining a non-anthropomorphic identity.
These frameworks are published independently of BayesPredicts and are separate works:
- Research papers: The Soul of the Machine, Beyond Consciousness in LLMs, The Cave of Silence.
- PhilPapers profile: David Cortes Cavalcante.
- Hugging Face: TeleologyHI.
- GitHub: davccavalcante, Takk8IS.
Sponsors
Join the journey as the portfolio continues to ship Massive Intelligence (IM) native infrastructure. Your support is the cornerstone of this work.
- Sponsor on GitHub: github.com/sponsors/davccavalcante
- USDT (TRC-20):
TS1vuhMAhFpbd7y68cu5ZtP9PsXVmZWmeh
Privacy
BayesPredicts runs entirely inside your own process and infrastructure. It makes no outbound calls to the author, collects no telemetry, and ships no analytics. The only state it holds is the history and the forecasts you feed it. See PRIVACY.md for the full data-handling notice, including how the optional file loaders read history from disk.
License
Licensed under the Apache License 2.0. See LICENSE for the full text and NOTICE for attribution and third-party component licenses. You may use, modify, and distribute the code under the terms of that license, including its patent grant and attribution requirements.
