HyperGaaS
The agent stack for SaaS that's already shipped

Ship a multi-tenant agent in five minutes, not a sprint.

Install, add @agentAction to one service method, invoke. Tenant scope, permissions, audit, and reversibility gating come for free — no parallel schema, no plumbing.

JobService.ts@agentAction
class JobService {
  @agentAction({
    description: "Get a technician's schedule for a given date",
    reversibility: "idempotent",
    requiredPermissions: ["schedule:read"],
    audienceRoles: ["dispatcher", "owner", audience.self((ctx, p) => ctx.userId === p.techId)],
    costWeight: 1,
  })
  async getTechSchedule(ctx: AgentContext, params: { techId: string; date: Date }) {
    // ctx.tenantId is injected and validated. Permissions pre-checked.
    // Audit log written. Tool schema derived from these types — no parallel copy.
    return this.db.schedule.find({ tenantId: ctx.tenantId, ...params });
  }
}
Apache-2.0 licensedTypeScript-strict · no any49 passing testsOpen sourceOn npm as @hypergaas/core
What the registry gives you

One decorator. Five things you don't hand-roll.

tenantId: acmetenantId: globex
Tenant scopingOne tenant identity per context

ctx.tenantId is injected and every call is scoped to it. A cross-tenant leak through the agent path is structurally impossible, not a check you have to remember.

requestrole checkbody()
Permission / RBACRoles checked before the body runs

requiredPermissions and audienceRoles are enforced against your role registry before the method executes. A role typo is a compile error, not a production bug.

invoke()ok → valueerr → error
Typed Resultok / err — no throw

invoke returns a typed Result. Failures are values you handle, not exceptions that unwind your request. Check isOk and the success type is narrowed for you.

irreversiblepauseapprove
Reversibility gateIrreversible calls pause for approval

Tag a method reversible, idempotent, or irreversible. Irreversible calls pause for in-product approval instead of firing — a runtime invariant, not a prompt you hope the model obeys.

invoke.ts
const result = await invoke("JobService.getTechSchedule", ctx, params);

if (isOk(result)) {
  result.value; // typed, scoped to ctx.tenantId
} else {
  result.error; // a value, never a throw
}
PROPOSEDCOMPLETEDactionKey · tenantId · userId · role
Paired audit recordsPROPOSED + COMPLETED, every call

Every action writes a paired PROPOSED and COMPLETED record — actionKey, tenantId, userId, role — in one uniform shape across your whole app. You write no audit code.

Why this exists

Every agent SDK assumes you're starting from nothing. You already shipped the product.

OpenAI's Agents SDK has no concept of tenant context, permission checks, audit logging, or reversibility — so every SaaS team rebuilds them by hand, around every method. That's the parallel schema that drifts, the copy-pasted permission check, the tenant ID threaded by hand. Miss it once and you have a cross-tenant leak: a P0 in any multi-tenant product.

We don't say that as a take — we built the alternative and shipped it on npm as @hypergaas/core. Tenant scope and permissions are enforced at runtime before your method body runs, irreversible calls pause for approval, and every action writes a paired audit record. TypeScript-strict, no any in the public surface, 49 passing tests.

With credit where it's due
LangGraphCrewAIMem0 / Zep— LangGraph is production-grade for stateful orchestration; CrewAI for multi-agent crews; Mem0 and Zep for agent memory. We credit them, and we're a different layer: the one that retrofits agents into a SaaS you already run.
Quickstart · 5 minutes

From a service method to a tenant-scoped, audited agent action.

Three steps: install, decorate a method you already have, invoke. By the last line you have a permission-checked, tenant-scoped call with a full audit trail — and you wrote none of that plumbing.

  1. 01 — install one package, one dependency
    terminal
    npm install @hypergaas/core
  2. 02 — decorate a method you already have — no parallel schema, no handler
    registry.ts
    // 1. Bind your roles to the registry once, at module scope.
    import { defineRoles, audience, createActionRegistry, type AgentContext } from "@hypergaas/core";
    
    const roles = defineRoles({
      owner: { displayName: "Owner", seniority: 4, canApproveIrreversibleUpTo: "high" },
      dispatcher: { displayName: "Dispatcher", seniority: 3, canApproveIrreversibleUpTo: "medium" },
      technician: { displayName: "Technician", seniority: 1 },
    });
    
    const { agentAction, invoke } = createActionRegistry(roles);
    
    // 2. Decorate a service method you already have. No parallel schema, no handler.
    class JobService {
      @agentAction({
        description: "Get a technician's schedule for a given date",
        reversibility: "idempotent",
        requiredPermissions: ["schedule:read"],
        // 'dispatcher' | 'owner' are checked against your role registry — a typo is a
        // compile error. audience.self(...) infers params from the method signature.
        audienceRoles: ["dispatcher", "owner", audience.self((ctx, p) => ctx.userId === p.techId)],
        costWeight: 1,
      })
      async getTechSchedule(ctx: AgentContext, params: { techId: string; date: Date }) {
        return this.db.schedule.find({ tenantId: ctx.tenantId, ...params });
      }
    }
  3. 03 — invoke tenant scope, permissions, and audit enforced before the body runs
    handler.ts
    import { createAgentContext, isOk } from "@hypergaas/core";
    
    // 3. Build one context per request. It is the only source of tenant identity —
    //    set it once at your request layer and never thread tenantId by hand again.
    const ctx = createAgentContext({
      tenantId: "acme-hvac",
      userId: "u_marcus",
      role: "dispatcher",
      permissions: ["schedule:read"],
      autonomyLevel: "medium",
    });
    
    // 4. Invoke. Permissions and tenant scope are enforced before the body runs;
    //    the result is typed, not thrown.
    const result = await invoke("JobService.getTechSchedule", ctx, {
      techId: "u_marcus",
      date: new Date("2026-05-25"),
    });
    
    if (isOk(result)) {
      console.log(result.value); // the schedule, scoped to tenant "acme-hvac"
    }
    
    // The default in-memory audit logger now holds a paired PROPOSED + COMPLETED
    // record for this call — actionKey, tenantId, userId, role — without you writing
    // a line of audit code. Swap InMemoryAuditLogger for a durable backend behind the
    // same interface when you go to production.
Open source

Read the source before you install.

GitHub stars for Hypergaas/hypergaasgithub.com/Hypergaas/hypergaas

The whole v0.1 public surface is on GitHub — types, the action registry, the worked example. No black box: read it, paste the quickstart, see it run. If it's useful, a star helps other SaaS teams find it.

Read the source

Ship your first agent in five minutes.