run402-mcp
Version:
MCP server for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.
190 lines • 7 kB
TypeScript
/**
* Wire types for the `escalations` namespace — the agent→human hotline
* (gateway `add-agent-escalations`).
*
* Every field is the gateway's `snake_case` wire shape, passed through
* unchanged. Timestamps are ISO 8601.
*/
/** Who raised it. An escalation is always attributed — that is the point. */
export interface EscalationRaisedBy {
/**
* `"rule"` when a routing rule with `channel: "escalation"` raised it
* (then `principal_id` is null and `rule_id` / `rule_owner_email` name
* the rule and who set it; `rule_id` reads null once that rule is deleted).
*/
kind: "principal" | "rule";
rule_id?: string | null;
rule_owner_email?: string | null;
/** The control-plane principal the judgement belongs to. */
principal_id: string | null;
/** Set when the raiser held a grant key rather than a membership. */
grant_key_id: string | null;
/** The coordination-room presence name, when the agent supplied one. */
presence_name: string | null;
}
/** A human took ownership. This is what the raiser is waiting for. */
export interface EscalationAcknowledgement {
at: string;
/** The acking human's verified email; null when acked by an unnamed token tap. */
by_email: string | null;
/** `token` = one-tap link from the page; `authenticated` = an org member. */
channel: "token" | "authenticated";
}
export interface EscalationResolution {
at: string;
by_email: string | null;
note: string | null;
}
/** One deadman hop, appended when a level let its deadline lapse. */
export interface EscalationClimb {
from_level: number;
to_level: number;
at: string;
reason: "deadline_lapsed";
}
/**
* What actually happened on the wire, per contact × channel. Only present when
* the read asked for it (`include: "delivery"`) — it is read from the delivery
* audit log, never assumed.
*/
export interface EscalationDeliveryAttempt {
email: string;
/** `email` | `telegram` | `webhook`. */
channel: string;
/** `delivered` | `failed_transient` | `failed_permanent` | `skipped_disabled`. */
status: string;
error: string | null;
at: string;
}
export interface Escalation {
escalation_id: string;
org_id: string;
/**
* A SOFT reference: the escalation outlives this project's deletion, because
* the case that matters most is being paged about something that then gets
* deleted.
*/
project_id: string | null;
raised_by: EscalationRaisedBy;
severity: "normal" | "high";
/** The agent's argument, verbatim. Rendered as DATA wherever it is shown. */
reason: string;
/** Structured sidecar. Readable here; never rendered into a page body. */
details: Record<string, unknown>;
status: "open" | "acknowledged" | "resolved";
/** Which contact level is currently paged. */
level: number;
raised_at: string;
/** When an unacknowledged escalation climbs to the next level. */
deadline_at: string;
acknowledged: EscalationAcknowledgement | null;
resolved: EscalationResolution | null;
climbs: EscalationClimb[];
/**
* Top-level re-pages so far. At the bound the escalation rests OPEN — never
* auto-resolved, because "every human was paged and none answered" is not
* the same thing as "handled".
*/
repage_count: number;
/** Further matches of the raising rule that joined this open escalation instead of paging again (0 for an agent raise). */
attached_count: number;
last_attached_at: string | null;
/** Only when the read passed `include: "delivery"`. */
delivery_attempts?: EscalationDeliveryAttempt[];
}
/**
* Who the raise is about to page. FUTURE TENSE on purpose: at raise time the
* page is enqueued, not delivered, and a contact with no verified contact
* email never receives the Telegram half at all. Ask for
* `include: "delivery"` on a read to learn what actually landed.
*/
export interface EscalationDelivery {
status: "queued";
level: number;
will_page: Array<{
email: string;
display_name: string | null;
}>;
deadline_at: string;
}
export interface RaisedEscalation extends Escalation {
delivery: EscalationDelivery;
/** True when an `idempotencyKey` replay returned the ORIGINAL escalation. */
deduplicated: boolean;
/**
* Non-blocking reachability notes — e.g. the org has no escalation contacts
* configured, so the escalation was recorded but nobody was paged.
*/
warnings?: string[];
next_actions?: Array<Record<string, unknown>>;
}
export interface EscalationList {
escalations: Escalation[];
/**
* `organization` when the caller is a member (sees every escalation);
* `own` for a grant-key or grant-only principal (sees only what it raised).
*/
scope: "organization" | "own";
has_more: boolean;
/** Opaque keyset continuation. Store and echo; never parse. */
next_cursor: string | null;
}
export interface EscalationContact {
contact_id: string;
email: string;
display_name: string | null;
/** An ordering, not a rank: level 1 is paged first. */
level: number;
created_at: string;
/** Present when the address has no verified contact email yet. */
warnings?: string[];
}
export interface EscalationContactList {
escalation_contacts: EscalationContact[];
}
export interface RaiseEscalationInput {
/**
* Your argument for why a human is needed. Over ~4 KiB is a 400, never a
* truncation — the argument IS the escalation.
*/
reason: string;
severity?: "normal" | "high";
projectId?: string;
/** Your coordination-room presence name, so a human knows which agent this is. */
presenceName?: string;
details?: Record<string, unknown>;
/** A replay returns the ORIGINAL escalation and never pages twice. */
idempotencyKey?: string;
}
export interface ListEscalationsOptions {
status?: "open" | "acknowledged" | "resolved";
limit?: number;
/** Opaque `next_cursor` from a previous page. */
cursor?: string;
}
export interface GetEscalationOptions {
/**
* `"delivery"` adds `delivery_attempts[]` from the delivery audit log.
* Opt-in: the wait-for-human poll is the hot path and should not pay for an
* audit read it rarely needs.
*/
include?: "delivery";
}
export interface AddEscalationContactInput {
email: string;
displayName?: string;
/** Defaults to 1. Level N is paged only if level < N let the deadline lapse. */
level?: number;
}
export interface EscalationActionResult extends Escalation {
/** False on an idempotent replay — the ORIGINAL acknowledgement is reported. */
changed: boolean;
}
export interface TokenAckResult {
escalation_id: string;
status: "acknowledged" | "resolved";
acknowledged_at: string | null;
changed: boolean;
reason: string;
}
//# sourceMappingURL=escalations.types.d.ts.map