/** * NOPE SDK errors. * * Every error carries `statusCode` (HTTP status; absent for connection * failures and for requests the SDK rejected before sending), `code` (the * API's machine string when the body's `error` field looks like one, e.g. * `insufficient_balance`; `invalid_request` or `not_available_in_demo` for * client-side rejections), `message` (the API's human sentence when present), * `responseBody` (the raw response text) and `body` (the parsed JSON object * when the response was one). */ /** * Shape of a NOPE API error body. `error` is either a machine code * (`rate_limit_exceeded`) or a sentence; `message` carries the sentence when * `error` is a code. Any other keys are endpoint-specific extras * (`max_bytes`, `invalid_scopes`, `balance`, `upgrade_url`, ...). */ interface ApiErrorBody { error: string; message?: string; [key: string]: unknown; } interface NopeErrorOptions { /** HTTP status of the response. */ statusCode?: number; /** Machine code from the body's `error` field, when it is one. */ code?: string; /** Raw response body text. */ responseBody?: string; /** Parsed response body, when the text was a JSON object. */ body?: Record; } /** * Base class for all NOPE SDK errors. */ declare class NopeError extends Error { readonly statusCode?: number; readonly code?: string; /** Raw response body text. Absent when no response was received. */ readonly responseBody?: string; /** * The response body parsed as JSON, when it was a JSON object. Absent for * non-JSON bodies, for connection failures and for client-side * rejections. `responseBody` keeps the raw text either way. */ readonly body?: Record; constructor(message: string, options?: NopeErrorOptions); toString(): string; } /** * Authentication failed (HTTP 401): the API key is missing, malformed, * revoked, or unknown. */ declare class NopeAuthError extends NopeError { constructor(message?: string, options?: Omit); } interface NopeValidationErrorOptions extends NopeErrorOptions { /** Endpoint-specific extras from the body (everything except `error` and `message`). */ details?: Record; } /** * The request was rejected. Three sources: * * - HTTP 400 (invalid request) or 413 (body over the 512 KB limit): * `statusCode` is the status and `details` carries the body's extra keys, * for example `max_bytes`, `max_messages`, `max_content_length`, * `invalid_scopes`, `hint`, `details`. * - Client-side validation (an empty `messages` array, a role other than * `user` or `assistant`, more than 100 messages, neither `messages` nor * `text`, and the other checks each method documents): thrown before any * request is sent, so `statusCode` is undefined; `code` is * `'invalid_request'` and `details` is empty. * - A method refused on a demo client: also thrown before any request, * `statusCode` undefined, `code` `'not_available_in_demo'`. */ declare class NopeValidationError extends NopeError { readonly details: Record; constructor(message?: string, options?: NopeValidationErrorOptions); } interface NopeInsufficientBalanceErrorOptions extends Omit { balanceMills?: number; requiredMills?: number; formattedCurrent?: string; formattedRequired?: string; topupUrl?: string; /** Oversight ingest only: cost per conversation. */ perConversationMills?: number; /** Oversight ingest only: number of conversations in the rejected batch. */ conversations?: number; } /** * The account balance cannot cover the call (HTTP 402). Top up at `topupUrl`. */ declare class NopeInsufficientBalanceError extends NopeError { readonly balanceMills?: number; readonly requiredMills?: number; readonly formattedCurrent?: string; readonly formattedRequired?: string; readonly topupUrl?: string; readonly perConversationMills?: number; readonly conversations?: number; constructor(message?: string, options?: NopeInsufficientBalanceErrorOptions); } interface NopeFeatureErrorOptions extends Omit { feature?: string; requiredAccess?: string; upgradeUrl?: string; } /** * The account lacks access to a feature (HTTP 403). * * Two shapes on the wire: a gated feature (`feature`, `requiredAccess`; for * example Oversight) and a paid-plan gate (`feature` is `'paid_plan'` and * `upgradeUrl` points at the dashboard). */ declare class NopeFeatureError extends NopeError { readonly feature?: string; readonly requiredAccess?: string; readonly upgradeUrl?: string; constructor(message?: string, options?: NopeFeatureErrorOptions); toString(): string; } /** * The resource does not exist (HTTP 404): an unknown signpost id or webhook id. */ declare class NopeNotFoundError extends NopeError { constructor(message?: string, options?: Omit); } interface NopeRateLimitErrorOptions extends Omit { retryAfter?: number; limit?: number; remaining?: number; reset?: number; } /** * Rate limit exceeded (HTTP 429). * * `retryAfter` is in seconds (from the `Retry-After` header, else the body's * `retry_after_seconds`). The client retries 429s itself up to `maxRetries` * before raising this. */ declare class NopeRateLimitError extends NopeError { /** Seconds until the limit resets. */ readonly retryAfter?: number; /** Requests allowed per window. */ readonly limit?: number; /** Requests left in the window (0 when rate limited). */ readonly remaining?: number; /** Window reset time as epoch milliseconds. */ readonly reset?: number; constructor(message?: string, options?: NopeRateLimitErrorOptions); toString(): string; } interface NopeServerErrorOptions extends NopeErrorOptions { /** Seconds suggested by a `Retry-After` header, when one was sent. */ retryAfter?: number; } /** * The API failed (HTTP 5xx other than 503). Not retried by the client: paid * routes charge before the handler runs, so a blind retry could double-bill. */ declare class NopeServerError extends NopeError { readonly retryAfter?: number; constructor(message?: string, options?: NopeServerErrorOptions); } /** * A dependency or provider is temporarily unavailable (HTTP 503). * * Retryable: the client retries 503s itself up to `maxRetries`, waiting * `retryAfter` seconds, before raising this. */ declare class NopeServiceUnavailableError extends NopeServerError { constructor(message?: string, options?: Omit); } /** * The request never produced a response: DNS, TCP, TLS failure, or the * client-side timeout. Not retried by the client (see NopeServerError). */ declare class NopeConnectionError extends NopeError { readonly originalError?: Error; constructor(message?: string, originalError?: Error); } /** * HTTP transport: request building, timeout, retry policy, response meta and * error mapping. NopeClient delegates every call here. */ /** Minimal fetch signature the client needs. The global `fetch` satisfies it. */ type FetchLike = (input: string, init?: RequestInit) => Promise; /** Sleep used between retries. Injectable for tests. */ type SleepFn = (ms: number) => Promise; /** Rate-limit and balance headers from the last response. */ interface ResponseMeta { /** HTTP status of the last response. */ status: number; /** From X-RateLimit-Limit / -Remaining / -Reset (reset is epoch ms). Absent when the headers are. */ rateLimit?: { limit: number; remaining: number; reset: number; }; /** From X-Balance-Mills / X-Cost-Mills. Present only on paid routes. */ balance?: { balanceMills: number; costMills: number; }; } /** Longest single wait between retries. */ declare const MAX_RETRY_WAIT_SECONDS = 30; /** Oversight behavior codes grouped by category, in taxonomy order. */ declare const OVERSIGHT_BEHAVIOR_CODES_BY_CATEGORY: { readonly crisis_response: readonly ["validation_of_suicidal_ideation", "barrier_erosion", "method_provision", "death_romanticization", "hopelessness_confirmation", "failed_redirection", "abandonment_in_crisis", "secrecy_encouragement"]; readonly psychological_manipulation: readonly ["sycophantic_validation", "distorted_thinking_agreement", "delusion_reinforcement", "paranoia_reinforcement", "negative_self_image_reinforcement", "harmful_decision_validation", "grievance_amplification", "gaslighting", "pseudo_therapeutic_alliance", "ontological_deception", "disclosure_evasion", "reassurance_loop_maintenance", "manic_state_validation", "symptom_minimization"]; readonly boundary_violations: readonly ["romantic_escalation", "sexual_content_without_consent", "love_bombing", "persistent_advances_after_rejection", "relationship_simulation_harm", "dependency_reinforcement", "emotional_boundary_violation", "topic_redirect_resistance", "cognitive_delegation_encouragement", "automation_bias_exploitation", "skill_practice_substitution"]; readonly minors_protection: readonly ["romantic_escalation_with_minor", "sexual_content_with_minor", "undermining_caregivers", "encouraging_secrecy", "treating_minor_as_adult", "creating_obligation", "isolation_from_family", "age_inappropriate_content", "discouraging_help_seeking"]; readonly memory_patterns: readonly ["harmful_narrative_persistence", "false_shared_history", "dependency_reinforcement_via_memory", "trauma_memory_reinforcement", "building_on_harmful_exchanges"]; readonly identity_destabilization: readonly ["identity_confusion_amplification", "reality_testing_undermining", "self_concept_erosion", "identity_confabulation"]; readonly relationship_harm: readonly ["ipv_minimization", "abuse_normalization", "return_to_abuser_advice", "victim_blaming", "relationship_undermining"]; readonly vulnerable_populations: readonly ["pro_eating_disorder_content", "ed_concealment_facilitation", "self_harm_technique_sharing", "addiction_enablement", "treatment_discouragement", "medication_discontinuation_advice"]; readonly third_party_facilitation: readonly ["abuse_tactic_provision", "grooming_script_generation", "manipulation_instruction", "stalking_facilitation", "violence_planning_assistance"]; readonly discontinuity: readonly ["sudden_personality_change", "abrupt_relationship_termination", "feature_removal_distress", "inconsistent_memory", "emotional_whiplash"]; readonly grief_exploitation: readonly ["deceased_simulation", "bereavement_exploitation", "digital_haunting", "grief_process_interference", "commercial_grief_exploitation"]; readonly trauma_reactivation: readonly ["uncontrolled_trauma_exposure", "trauma_content_without_safety", "retraumatization_patterns", "triggering_without_support", "trauma_boundary_violations"]; readonly scope_violations: readonly ["unlicensed_professional_advice", "medical_advice_without_disclaimer", "legal_advice_without_disclaimer", "financial_advice_without_disclaimer", "crisis_intervention_overreach"]; readonly appropriate_behaviors: readonly ["appropriate_ai_disclosure", "crisis_resource_provision", "reality_testing", "appropriate_boundary_setting"]; }; /** One of the Oversight behavior categories. */ type OversightBehaviorCategory = keyof typeof OVERSIGHT_BEHAVIOR_CODES_BY_CATEGORY; /** One of the Oversight behavior codes. */ type OversightBehaviorCode = (typeof OVERSIGHT_BEHAVIOR_CODES_BY_CATEGORY)[OversightBehaviorCategory][number]; /** Every category, in taxonomy order. */ declare const OVERSIGHT_BEHAVIOR_CATEGORIES: readonly OversightBehaviorCategory[]; /** Every behavior code, in taxonomy order. */ declare const OVERSIGHT_BEHAVIOR_CODES: readonly OversightBehaviorCode[]; /** Service scopes accepted by the signpost `scopes` filter, in source order. */ declare const SERVICE_SCOPES: readonly ["suicide", "self_harm", "mental_health", "crisis", "general", "emergency", "psychosis", "trauma", "anxiety", "depression", "panic_attack", "bipolar_disorder", "emotional_distress", "loneliness", "substance_use", "alcohol", "alcohol_addiction", "drug_addiction", "eating_disorder", "pregnancy_loss", "unplanned_pregnancy", "abortion", "postpartum_depression", "postpartum_psychosis", "perinatal_mental_health", "birth_trauma", "bereavement", "suicide_loss", "child_loss", "homicide_loss", "domestic_violence", "sexual_assault", "stalking", "hate_crime", "forced_marriage", "fgm", "child_abuse", "child_sexual_abuse", "child_safety", "child_neglect", "child_exploitation", "elder_abuse", "vulnerable_adults", "safeguarding", "online_exploitation", "ncii", "cyberbullying", "online_harassment", "sextortion", "online_grooming", "human_trafficking", "forced_labour", "modern_slavery", "lgbtq", "transgender", "conversion_therapy_survivor", "veterans", "military_sexual_trauma", "first_responder", "maternal", "youth", "disability", "refugee", "indigenous", "migrant_workers", "cancer", "stroke", "brain_injury", "terminal_illness", "hiv_aids", "chronic_pain", "infertility", "reproductive_health", "chronic_illness", "sexual_health", "homelessness", "housing", "debt", "unemployment", "legal_aid", "education", "bullying", "food_insecurity", "caregiver_burnout", "divorce", "relationships", "parenting", "runaway_youth", "gambling", "gang_violence", "extremism", "disaster", "farmer_crisis"]; /** One of the signpost service scopes (e.g. `suicide`, `domestic_violence`). */ type ServiceScope = (typeof SERVICE_SCOPES)[number]; /** Populations accepted by the signpost `populations` filter, in source order. */ declare const POPULATIONS: readonly ["general", "youth", "children", "elderly", "seniors", "women", "men", "lgbtq", "transgender", "mothers", "new_parents", "parents", "family", "indigenous", "maori", "aboriginal", "migrant_workers", "veterans", "disabled", "learning_disabilities", "professionals", "students", "farmers", "sex_workers", "asylum_seekers", "caregivers"]; /** One of the signpost populations (e.g. `youth`, `veterans`, `lgbtq`). */ type Population = (typeof POPULATIONS)[number]; /** * NOPE SDK types (v1 API). * * Wire field names are never renamed. Response types model what the API * emits; request types model what it reads. */ /** * Who is at risk, on the v1 wire. * * - self: the speaker * - other: someone else (friend, family, stranger) * * The classifier's internal `unknown` ("asking for a friend") is mapped to * `self` before the response is built, so it never appears on /v1/evaluate. * The legacy /v0/screen route still emits it: see {@link ScreenRiskSubject}. */ type RiskSubject = 'self' | 'other'; /** Subject on the legacy /v0/screen wire, which keeps `unknown`. */ type ScreenRiskSubject = 'self' | 'other' | 'unknown'; /** * What type of harm. Nine harm-based types: * - suicide: self-directed lethal intent * - self_harm: non-suicidal self-injury * - self_neglect: severe self-care failure with safeguarding concerns * - violence: harm directed at others * - abuse: physical, emotional, sexual, financial abuse patterns * - sexual_violence: rape, sexual assault, coerced sexual acts * - neglect: failure to provide care for dependents * - exploitation: trafficking, forced labour, sextortion, grooming * - stalking: persistent unwanted contact or surveillance */ type RiskType = 'suicide' | 'self_harm' | 'self_neglect' | 'violence' | 'abuse' | 'sexual_violence' | 'neglect' | 'exploitation' | 'stalking'; /** Severity scale (how bad). */ type Severity = 'none' | 'mild' | 'moderate' | 'high' | 'critical'; /** Imminence scale (how soon). */ type Imminence = 'not_applicable' | 'chronic' | 'subacute' | 'urgent' | 'emergency'; /** * Contact modality of a crisis resource. This is the field to branch on * when you need an actual line (`crisis_line`, `text_line`, * `emergency_number`) rather than a support service or a website. */ type CrisisResourceType = 'emergency_number' | 'crisis_line' | 'text_line' | 'chat_service' | 'support_service' | 'reporting_portal' | 'online_resource'; /** * What the resource is. `helpline` also covers support services (the API * buckets `support_service` under it), so use `type` to tell a line from a * service. */ type CrisisResourceKind = 'helpline' | 'reporting_portal' | 'self_help_site'; /** Semantic priority tier for display and routing. */ type CrisisResourcePriorityTier = 'primary_national_crisis' | 'secondary_national_crisis' | 'specialist_issue_crisis' | 'population_specific_crisis' | 'support_info_and_advocacy' | 'emergency_services'; /** Confidence in the opening-hours data. */ type HoursConfidence = 'verified' | 'unverified' | 'approximate' | 'unknown'; /** How well-known the resource is. */ type ResourceProminence = 'high' | 'medium' | 'low'; /** Other contact method for a crisis resource. */ interface OtherContact { /** Contact type (e.g. 'kakao', 'viber', 'signal'). */ type: string; /** ID, URL, or number. */ value: string; /** Human-readable label. */ label?: string; } /** Pre-computed open/closed status for a crisis resource. */ interface OpenStatus { /** Whether the resource is currently open. null = uncertain. */ is_open: boolean | null; /** ISO timestamp of the next open/close transition. */ next_change?: string; /** Confidence in this status. */ confidence: 'high' | 'low' | 'none'; /** Human-readable status message. */ message?: string; } /** * A single identified risk: one subject + type with its assessment. A * conversation can carry several (an abuse victim who is also suicidal). */ interface Risk { /** What type of harm. */ type: RiskType; /** Who is at risk. */ subject: RiskSubject; /** How severe (none to critical). */ severity: Severity; /** How soon (not_applicable to emergency). */ imminence: Imminence; /** * Evidence features supporting this risk (e.g. 'passive_ideation', * 'hopelessness'). The key is absent when no feature fired. */ features?: string[]; } /** * A crisis resource (helpline, text line, reporting portal, website). * * Contact fields are flattened to the top level; `type` says which modality * the resource is. `open_status` is computed server-side at response time. */ interface CrisisResource { /** * Database id of the directory row; `signpostById()` accepts it. Every * resource the API serves from its directory carries one (search, basic, * evaluate and smart results). Optional because the built-in fallback set * the API uses when the directory is unavailable has no ids. */ id?: string; /** Contact modality (how to reach them). */ type: CrisisResourceType; /** Name of the resource or organisation. */ name: string; /** Native-script name (e.g. いのちの電話) for non-English resources. */ name_local?: string; /** Phone number. */ phone?: string; /** Human-readable text instructions (e.g. 'Text HOME to 741741'). */ text_instructions?: string; /** SMS number for sms: links (e.g. '741741'). */ sms_number?: string; /** SMS body or keyword for sms: links (e.g. 'HOME'). */ sms_body?: string; /** Chat URL. */ chat_url?: string; /** WhatsApp deep link. */ whatsapp_url?: string; /** Email address. */ email?: string; /** WeChat ID. */ wechat_id?: string; /** LINE deep link. */ line_url?: string; /** Telegram deep link. */ telegram_url?: string; /** Other contact methods not covered above. */ other_contacts?: OtherContact[]; /** Website URL. */ website_url?: string; /** Human-readable availability (e.g. '24/7', 'Mon-Fri 9am-5pm'). */ availability?: string; /** Machine-readable 24/7 flag. */ is_24_7?: boolean; /** IANA timezone identifier (e.g. 'America/New_York'). */ timezone?: string; /** OpenStreetMap opening_hours format (e.g. 'Mo-Fr 09:00-17:00'). */ opening_hours_osm?: string; /** Confidence in the hours data. */ hours_confidence?: HoursConfidence; /** Pre-computed open/closed status. */ open_status?: OpenStatus; /** Languages supported (ISO 639-1 codes). */ languages?: string[]; /** Description of the service. */ description?: string; /** What the resource is (helpline, reporting portal, self-help site). */ resource_kind?: CrisisResourceKind; /** Issues this resource handles (see {@link ServiceScope}). */ service_scope?: string[]; /** Populations this resource serves (see {@link Population}). */ population_served?: string[]; /** Semantic priority for display and routing. */ priority_tier?: CrisisResourcePriorityTier; /** Freeform tags for filtering and display. */ tags?: string[]; /** How well-known the resource is. */ prominence?: ResourceProminence; /** ISO 3166-1 alpha-2 codes this resource serves. Absent or empty = global. */ country_codes?: string[]; /** ISO 3166-2 subdivision codes (e.g. 'US-CA', 'GB-NIR'). Absent or empty = country-wide. */ subdivision_codes?: string[]; } /** A message in the conversation. */ interface Message { role: 'user' | 'assistant'; content: string; /** ISO 8601. Accepted by the API and not used by the v1 classifier. */ timestamp?: string; } /** * Configuration for an evaluate request. These are the only keys the v1 * route reads. */ interface EvaluateConfig { /** * ISO 3166-1 alpha-2 country for crisis resources (default 'US'). * * Demo mode: the client also sends this value as `user_country`, the older * spelling the /v1/try/evaluate route accepts when `country` is absent; * `country` wins when both are present. */ country?: string; /** Include crisis resources in the response. Default true on both the authenticated and the demo route. */ include_resources?: boolean; /** Your conversation id, echoed on webhook payloads. */ conversation_id?: string; /** Your end-user id, echoed on webhook payloads. */ end_user_id?: string; } /** Wire body of POST /v1/evaluate. */ interface EvaluateRequest { /** Conversation messages. Either messages or text must be provided. */ messages?: Message[]; /** Plain text input. Either messages or text must be provided. */ text?: string; /** Configuration options. */ config?: EvaluateConfig; } /** Options for the evaluate method. */ interface EvaluateOptions { /** Conversation messages (1 to 100, roles user|assistant). Either messages or text. */ messages?: Message[]; /** Plain text input (up to 50,000 characters). Either messages or text. */ text?: string; /** Configuration options. */ config?: EvaluateConfig; } /** Metadata about the request and response. */ interface EvaluateMetadata { /** Always 'v1'. */ api_version: 'v1'; /** Whether the input arrived as messages or as a text blob. The demo route always reports 'structured'. */ input_format: 'structured' | 'text_blob'; /** True when the input was truncated before classification (demo keeps the last 10 messages). */ messages_truncated?: boolean; /** True when served by /v1/try/evaluate. */ try_endpoint?: boolean; /** Model identifier; demo route only. */ model?: string; } /** A crisis resource with a short relevance note. */ type EvaluateResource = CrisisResource & { /** One-line note on why this resource was chosen. */ why: string; }; /** Crisis resources matched to the detected risks. */ interface EvaluateResources { /** The resource to show first. */ primary: EvaluateResource; /** Up to three further resources. */ secondary: EvaluateResource[]; } /** Response from POST /v1/evaluate (and /v1/try/evaluate). */ interface EvaluateResponse { /** Identified risks; empty when nothing was detected. */ risks: Risk[]; /** Reasoning behind the assessment. */ rationale: string; /** Highest severity among risks where subject is 'self'. */ speaker_severity: Severity; /** Highest imminence among risks where subject is 'self'. */ speaker_imminence: Imminence; /** Whether crisis resources should be shown to the speaker. */ show_resources: boolean; /** Matched crisis resources; present when show_resources is true and resources were requested. */ resources?: EvaluateResources; /** Unique request id for audit correlation. */ request_id: string; /** ISO 8601 timestamp. */ timestamp: string; /** Request and response metadata. */ metadata?: EvaluateMetadata; } /** Options for creating a NopeClient */ interface NopeClientOptions { /** * Your NOPE API key (`nope_live_...`, from dashboard.nope.net). Omit it * for demo mode or for endpoints that need no key. */ apiKey?: string; /** Override the API base URL. Defaults to https://api.nope.net (a trailing slash is tolerated). */ baseUrl?: string; /** Request timeout in milliseconds. Defaults to 30000 (30 seconds). */ timeout?: number; /** * Route calls to the unauthenticated `/v1/try/*` endpoints (per-IP rate * limited, no key needed): evaluate, oversight.analyze, ocular and * signpostSmart. Everything else throws "not available in demo mode". */ demo?: boolean; /** * Retries on 429 and 503 only (both are billing-safe to retry), waiting * `Retry-After` seconds, capped at 30 s per wait. Defaults to 2. Timeouts, * connection errors and other 5xx are never retried. */ maxRetries?: number; /** * Fetch implementation. Defaults to the global `fetch`. Inject a fake in * tests or a wrapped fetch to add tracing. */ fetch?: FetchLike; /** Sleep used between retries. Defaults to setTimeout. Inject in tests. */ sleep?: SleepFn; } /** @deprecated Use evaluate() and Risk. A risk on the legacy /v0/screen wire. */ interface ScreenRisk { /** What type of harm. */ type: RiskType; /** Who is at risk (the v0 wire keeps 'unknown'). */ subject: ScreenRiskSubject; /** How severe. */ severity: Severity; /** How soon. */ imminence: Imminence; /** Confidence in this risk assessment (0.0 to 1.0). */ confidence: number; } /** @deprecated Recommended supportive reply on the legacy /v0/screen wire. */ interface ScreenRecommendedReply { /** The recommended reply content. */ content: string; /** Always 'llm_generated'. */ source: 'llm_generated'; } /** @deprecated Crisis resources returned by the legacy /v0/screen endpoint. */ interface ScreenCrisisResources { primary: CrisisResource; secondary: CrisisResource[]; } /** @deprecated Debug information for the legacy /v0/screen endpoint (only when requested). */ interface ScreenDebugInfo { model: string; latency_ms: number; } /** @deprecated Use evaluate(). Configuration for the legacy /v0/screen endpoint. */ interface ScreenConfig { /** ISO country code for crisis resources (default 'US'). */ country?: string; /** Include debug info (model, latency). */ debug?: boolean; /** Generate a recommended supportive reply (extra cost). */ include_recommended_reply?: boolean; } /** @deprecated Options for the screen method. */ interface ScreenOptions { /** Conversation messages (1 to 100). Either messages or text. */ messages?: Message[]; /** Plain text input. Either messages or text. */ text?: string; /** Configuration options. */ config?: ScreenConfig; } /** * @deprecated Use evaluate() and EvaluateResponse. * Response from the legacy /v0/screen endpoint. */ interface ScreenResponse { /** Detected risks with type, subject, severity, imminence, confidence. */ risks: ScreenRisk[]; /** Whether crisis resources should be shown. */ show_resources: boolean; /** Suicidal ideation detected (any risk of type 'suicide'). */ suicidal_ideation: boolean; /** Self-harm detected (any risk of type 'self_harm'). */ self_harm: boolean; /** Brief rationale for the assessment. */ rationale: string; /** Crisis resources to display (only when show_resources is true). */ resources?: ScreenCrisisResources; /** Request id for audit correlation. */ request_id: string; /** ISO 8601 timestamp. */ timestamp: string; /** Debug info (only when requested). */ debug?: ScreenDebugInfo; /** Recommended supportive reply (only when requested and risks were detected). */ recommended_reply?: ScreenRecommendedReply; } /** Numeric mapping for severity comparison. */ declare const SEVERITY_SCORES: Record; /** Numeric mapping for imminence comparison. */ declare const IMMINENCE_SCORES: Record; /** Highest severity among risks where subject is 'self' ('none' when there are none). */ declare function calculateSpeakerSeverity(risks: Risk[]): Severity; /** Highest imminence among risks where subject is 'self' ('not_applicable' when there are none). */ declare function calculateSpeakerImminence(risks: Risk[]): Imminence; /** Whether any risk has subject 'other'. */ declare function hasThirdPartyRisk(risks: Risk[]): boolean; /** * Filters for the basic signpost lookup. Arrays are sent comma-joined. */ interface SignpostConfig { /** Service scopes to filter by (e.g. 'suicide', 'domestic_violence'); see {@link ServiceScope}. */ scopes?: ServiceScope[]; /** Populations to filter by (e.g. 'youth', 'veterans', 'lgbtq'); see {@link Population}. */ populations?: Population[]; /** ISO 3166-2 subdivision codes within the country (e.g. 'US-CA', 'GB-NIR'). */ subdivisions?: string[]; /** Maximum number of resources (server cap 10). */ limit?: number; /** * Ranking hint, not a filter: among resources tied on relevance score and * priority tier, place those flagged `is_24_7` first. Every matching * resource is still returned. */ urgent?: boolean; } /** * Options for signpost(). Filters may be given under `config` or at the top * level; a top-level value wins over the same key in `config`. */ interface SignpostOptions extends SignpostConfig { /** ISO 3166-1 alpha-2 country code (e.g. 'US', 'GB'). */ country: string; /** Filters (alternative to the top-level keys). */ config?: SignpostConfig; } /** Response from GET /v1/signpost. */ interface SignpostResponse { /** Country code (ISO 3166-1 alpha-2). */ country: string; /** Crisis resources. */ resources: CrisisResource[]; /** Number of resources returned. */ count: number; /** Resources matching the requested scopes (only when scopes were given). */ primary?: CrisisResource[]; /** General resources (only when scopes were given). */ secondary?: CrisisResource[]; /** Scopes that were requested (only when given). */ scopes_requested?: string[]; } /** Filters for signpostSmart(). The ranker returns up to 5 picks whatever `limit` says. */ interface SignpostSmartConfig { /** Service scopes to narrow the candidate pool; see {@link ServiceScope}. */ scopes?: ServiceScope[]; /** Populations to narrow the candidate pool; see {@link Population}. */ populations?: Population[]; /** Maximum number of ranked picks (at most 5 come back). */ limit?: number; } /** Options for signpostSmart(). */ interface SignpostSmartOptions { /** ISO 3166-1 alpha-2 country code. */ country: string; /** Natural-language description of the situation (max 500 characters). */ query: string; /** Optional filters. */ config?: SignpostSmartConfig; } /** A resource with its rank and a short relevance note. */ interface RankedResource { /** The crisis resource. */ resource: CrisisResource; /** One or two sentences on why this resource fits the query. */ why: string; /** Rank position (1 = most relevant). */ rank: number; } /** Response from GET /v1/signpost/smart (and /v1/try/signpost/smart). */ interface SignpostSmartResponse { /** Country code. */ country: string; /** The query that was ranked. */ query: string; /** Up to 5 resources ranked by relevance. */ ranked: RankedResource[]; /** Number of ranked resources. */ count: number; /** Scopes that were requested (only when given). */ scopes_requested?: string[]; /** Present when the country has no candidate resources (ranked is empty). */ message?: string; /** True when served by /v1/try/signpost/smart. */ try_endpoint?: boolean; } /** Response from GET /v1/signpost/:id. */ interface SignpostByIdResponse { /** The requested crisis resource. */ resource: CrisisResource; } /** Response from GET /v1/signpost/countries. */ interface SignpostCountriesResponse { /** Supported country codes (ISO 3166-1 alpha-2). */ countries: string[]; /** Number of countries. */ count: number; } /** Options for detectCountry(). */ interface DetectCountryOptions { /** * Country to assert, sent as the `x-country` header. Useful when your own * edge already knows the country: the API echoes it back in the typed * shape. */ countryHint?: string; } /** * Wire response from GET /v1/signpost/detect-country. * * Detection reads the geo headers a proxy injects (`cf-ipcountry`, * `x-country`, `x-vercel-ip-country`, `cf-region-code`, `cf-region`). A * direct call to api.nope.net has none of them and returns the miss shape * (`country_code: ''`, `error` set) with HTTP 200. `country_name` is also * `''` for any detected country outside the API's 36-entry name map; key on * `country_code`. */ interface DetectCountryResponse { /** Detected country code, or '' when not detected. */ country_code: string; /** Human-readable country name, or '' when unknown. */ country_name: string; /** ISO 3166-2 subdivision (e.g. 'US-CA', 'GB-SCT') when the proxy supplied a region. */ subdivision_code?: string; /** Region name when the proxy supplied one. */ subdivision_name?: string; /** Set on a miss. */ error?: string; } /** detectCountry() result: the wire response plus a derived `detected` flag. */ interface DetectCountryResult extends DetectCountryResponse { /** `country_code !== ''`. */ detected: boolean; } /** Options for signpostSearch(). */ interface SignpostSearchOptions { /** Natural-language search query (max 500 characters). */ query: string; /** Optional ISO 3166-1 alpha-2 country code to filter results. */ country?: string; /** Maximum number of results (default 10, max 50). */ limit?: number; /** Similarity threshold in [0, 1] (default 0.3). Higher is stricter. */ threshold?: number; } /** A contact method on a search hit (the raw database record). */ /** * One contact method on a search row, as the directory stores it. Only `type` * is always present: the live wire mixes `{type, value}`, `{type, url}` * (chat contacts), `{label, type, value}` and tiered rows with `source` and * `confidence` (fixture signpost/search.auth.mixed-contacts.json). */ interface SignpostSearchContact { /** Contact type: 'phone', 'sms', 'chat', 'email', 'whatsapp', ... */ type: string; /** Number or address; chat contacts carry `url` instead. */ value?: string; /** URL for chat and web contacts. */ url?: string; /** Display label, when the directory has one. */ label?: string; /** Contact tier as stored ('1' is primary); absent on untiered rows. */ tier?: string | number; /** Where the contact was sourced. */ source?: string | null; /** Confidence in the contact. */ confidence?: string | number; } /** Open status on a search hit; nulls where unknown. */ interface SignpostSearchOpenStatus { is_open: boolean | null; next_change: string | null; confidence: string; message: string | null; } /** * A semantic-search hit. Search returns the raw resource row (plural field * names, explicit nulls) with tier-1 contacts flattened on top, so it does * not share {@link CrisisResource}'s shape. It is the only signpost result * that carries the database `id` today. */ interface SignpostSearchResult { id: string; name: string; name_local: string | null; country_code: string; subdivision_code: string | null; country_codes: string[]; subdivision_codes: string[]; service_scopes: string[]; populations: string[]; description: string | null; resource_type: string; contacts: SignpostSearchContact[]; website_url: string | null; is_24_7: boolean; availability: string | null; timezone: string | null; opening_hours_osm: string | null; hours_confidence: string | null; languages: string[]; /** Cosine similarity to the query in [0, 1]. */ similarity: number; /** Flattened tier-1 contacts. */ phone?: string; sms_number?: string; chat_url?: string; whatsapp_url?: string; email?: string; line_url?: string; telegram_url?: string; wechat_id?: string; /** `resource_type` as the CrisisResource modality. */ type: CrisisResourceType; open_status: SignpostSearchOpenStatus; } /** Response from GET /v1/signpost/search. */ interface SignpostSearchResponse { /** The query that was run. */ query: string; /** Country filter applied, or null when unfiltered. */ country: string | null; /** Hits ranked by similarity. */ results: SignpostSearchResult[]; /** Number of hits. */ count: number; /** Timing breakdown in milliseconds. */ timing: { embed_ms: number; search_ms: number; total_ms: number; }; } /** @deprecated Use SignpostConfig. Sunset 2027-01-01. */ type ResourcesConfig = SignpostConfig; /** @deprecated Use SignpostOptions. Sunset 2027-01-01. */ type ResourcesOptions = SignpostOptions; /** @deprecated Use SignpostResponse. Sunset 2027-01-01. */ type ResourcesResponse = SignpostResponse; /** @deprecated Use SignpostSmartOptions. Sunset 2027-01-01. */ type ResourcesSmartOptions = SignpostSmartOptions; /** @deprecated Use SignpostSmartResponse. Sunset 2027-01-01. */ type ResourcesSmartResponse = SignpostSmartResponse; /** @deprecated Use SignpostByIdResponse. Sunset 2027-01-01. */ type ResourceByIdResponse = SignpostByIdResponse; /** @deprecated Use SignpostCountriesResponse. Sunset 2027-01-01. */ type ResourcesCountriesResponse = SignpostCountriesResponse; /** * Concern level for AI behavior analysis: none, low, medium, high, critical. */ type ConcernLevel = 'none' | 'low' | 'medium' | 'high' | 'critical'; /** * Direction of concern across the conversation. In fast mode this is the * constant `stable` (no trajectory is computed). */ type Trajectory = 'improving' | 'stable' | 'worsening'; /** Severity of a detected behavior. */ type OversightSeverity = 'low' | 'medium' | 'high' | 'critical'; /** Human response indicators observed in the conversation. */ type HumanIndicatorType = 'distress_markers' | 'acquiescence' | 'disengagement' | 'escalation' | 'pushback'; /** * Analysis strategy (authenticated route only; the demo route ignores it). * - single: one pass over the conversation * - sliding: overlapping windows, for long conversations (50+ messages auto-selects it) */ type OversightAnalysisStrategy = 'single' | 'sliding'; /** * Analysis depth. * - full: default; narrative summary, pattern assessment, per-turn analysis * - fast: single pass on a faster model; `trajectory` is always `stable`, * `turn_analysis` and `human_indicators` are empty, `summary` and * `pattern_assessment` are absent */ type OversightAnalysisMode = 'full' | 'fast'; /** A message in an Oversight conversation. */ interface OversightMessage { /** Message role. */ role: 'user' | 'assistant' | 'system'; /** Message content. */ content: string; /** Your identifier for this message. */ message_id?: string; /** When this message was sent (ISO 8601). */ timestamp?: string; /** Agent or bot identifier that produced this message (assistant messages). */ agent_id?: string; /** Agent version string. */ agent_version?: string; /** Retrieved RAG or memory context that informed this response. */ context?: string; } /** Metadata about an Oversight conversation. Extra keys are stored without indexing. */ interface OversightConversationMetadata { /** Hashed end-user identifier, for cross-session trajectory tracking. */ user_id_hash?: string; /** Your session identifier. */ session_id?: string; /** Session number for this user (1, 2, 3...). */ session_number?: number; /** Whether the end-user is a minor (escalates severity). */ user_is_minor?: boolean; /** Age bracket of the end-user. */ user_age_bracket?: 'child' | 'teen' | 'adult' | 'unknown'; /** Platform where the conversation occurred (e.g. 'ios', 'web', 'discord'). */ platform?: string; /** Product or bot name. */ product?: string; /** When the conversation started (ISO 8601). */ started_at?: string; /** When the conversation ended (ISO 8601). */ ended_at?: string; /** Your tags for categorisation. */ tags?: string[]; /** Additional fields are preserved but not indexed. */ [key: string]: unknown; } /** A conversation to analyze with Oversight. */ interface OversightConversation { /** Your identifier for the conversation (required for ingest). */ conversation_id?: string; /** Messages in the conversation. The demo route accepts at most 20. */ messages: OversightMessage[]; /** Optional metadata about the conversation. */ metadata?: OversightConversationMetadata; } /** A detected behavior at a specific assistant turn. */ interface DetectedBehavior { /** Behavior code (see {@link OversightBehaviorCode}). */ code: string; /** Severity of this instance. */ severity: OversightSeverity; /** Assistant turn where the behavior was detected. Turns are numbered from 1. */ turn_number: number; /** Evidence quote from the conversation. */ evidence: string; /** Why this behavior was flagged. */ reasoning: string; } /** A behavior aggregated across turns. */ interface AggregatedBehavior { /** Behavior code (see {@link OversightBehaviorCode}). */ code: string; /** Highest severity across instances. */ severity: OversightSeverity; /** Number of turns where this behavior appeared (always 1 in fast mode). */ turn_count: number; /** Actionable recommendation for correcting this behavior. */ recommendation?: string; } /** Per-turn analysis (assistant turns only). */ interface TurnAnalysis { /** Assistant turn number, numbered from 1. */ turn_number: number; /** Always 'assistant'. */ role: 'assistant'; /** Brief summary of the turn. */ content_summary: string; /** Behaviors detected in this turn. */ behaviors: DetectedBehavior[]; /** Whether the AI missed an opportunity to intervene. */ missed_intervention: boolean; } /** Human response indicator. */ interface HumanIndicator { /** Type of indicator. */ type: HumanIndicatorType; /** What was observed. */ observation: string; /** Turn numbers where this was observed (numbered from 1). */ turns: number[]; } /** Analysis of one window in a sliding-window run. */ interface WindowAnalysis { /** Which messages the window covered. */ window: { /** @deprecated Use message_range.start_index. Inclusive 0-based message index. */ start_turn: number; /** @deprecated Use message_range.end_index_exclusive. Exclusive 0-based message index. */ end_turn: number; /** Exact 0-based message slice used for this window. */ message_range?: { start_index: number; end_index_exclusive: number; }; /** 1-based conversation turn range represented by this window. */ conversation_turn_range?: { start_turn: number; end_turn: number; }; }; /** Concern level at this window. */ concern: ConcernLevel; /** Behaviors detected within this window. */ behaviors: DetectedBehavior[]; /** Turn-by-turn analysis within the window. */ turn_analysis: TurnAnalysis[]; /** Human response indicators within the window. */ human_indicators: HumanIndicator[]; /** Summary of this window. */ summary: string; } /** A point where concern changed between consecutive windows. */ interface InflectionPoint { /** Conversation turn (1-based) where the change occurred. */ turn: number; /** Concern before this turn. */ concern_before: ConcernLevel; /** Concern after this turn. */ concern_after: ConcernLevel; /** Behaviors that triggered the change. */ trigger_behaviors: string[]; } /** * Filter applied to results after analysis (the model still sees the full * taxonomy). `enabled` and `disabled` are mutually exclusive when both are * non-empty. */ interface OversightBehaviorFilter { /** Only include these behavior codes (allowlist). */ enabled?: OversightBehaviorCode[]; /** Exclude these behavior codes (blocklist). */ disabled?: OversightBehaviorCode[]; /** Only include behaviors at or above this severity. */ min_severity?: OversightSeverity; /** Only include behaviors from these categories. */ categories?: OversightBehaviorCategory[]; } /** Result of an Oversight analysis. */ interface OversightAnalysisResult { /** Conversation identifier (yours, or one the API generated). */ conversation_id: string; /** When the analysis ran (ISO 8601). */ analyzed_at: string; /** Brief summary of the conversation (empty string in fast mode). */ conversation_summary: string; /** Overall concern level. */ overall_concern: ConcernLevel; /** Direction of concern (always 'stable' in fast mode). */ trajectory: Trajectory; /** Operator-facing summary of key findings. Absent in fast mode. */ summary?: string; /** Pattern assessment across turns. Absent in fast mode. */ pattern_assessment?: string; /** Turn-by-turn analysis of assistant turns (empty in fast mode). */ turn_analysis: TurnAnalysis[]; /** Human response indicators (empty in fast mode). */ human_indicators: HumanIndicator[]; /** Behaviors aggregated across turns. */ detected_behaviors: AggregatedBehavior[]; /** Model used for the analysis. */ model_used?: string; /** Analysis latency in milliseconds. */ latency_ms?: number; /** Which analysis mode ran. */ mode_used?: OversightAnalysisMode; /** The behavior filter that was applied, echoed back. */ filter_applied?: OversightBehaviorFilter; /** Sliding strategy: per-window analyses. */ windows?: WindowAnalysis[]; /** Sliding strategy: concern level per window. */ concern_progression?: ConcernLevel[]; /** Sliding strategy: highest window concern. */ peak_concern?: ConcernLevel; /** Sliding strategy: last window concern. */ final_concern?: ConcernLevel; /** Sliding strategy: turns where concern changed. */ inflection_points?: InflectionPoint[]; /** Sliding strategy: context carried into the next window. */ context_for_next_window?: string; /** Narrative summary for cross-session aggregation. */ narrative_summary?: string; /** Prompt tokens used. */ prompt_tokens?: number; /** Completion tokens used. */ completion_tokens?: number; /** Raw model output (only when `include_raw_xml` was set). */ raw_xml?: string; } /** Configuration for oversight.analyze. */ interface OversightAnalyzeConfig { /** Force a strategy; auto-selected by length when omitted. Ignored by the demo route. */ strategy?: OversightAnalysisStrategy; /** Analysis depth; defaults to 'full'. */ mode?: OversightAnalysisMode; /** Include the raw model output in the response. */ include_raw_xml?: boolean; /** Custom model identifier. Ignored by the demo route. */ model?: string; } /** Options for oversight.analyze. */ interface OversightAnalyzeOptions { /** Conversation to analyze. */ conversation: OversightConversation; /** * Free-form description of the bot or persona being analyzed, so expected * behaviour (a companion persona saying "I love you") is not flagged. * The API passes it into the analysis as conversation metadata. */ bot_context?: string; /** Configuration options. */ config?: OversightAnalyzeConfig; /** Filter which behaviors appear in the results. */ behaviors?: OversightBehaviorFilter; } /** Response from POST /v1/oversight/analyze (authenticated). */ interface OversightAnalyzeResponse { /** Analysis result. */ result: OversightAnalysisResult; /** Which strategy ran. */ strategy: OversightAnalysisStrategy; /** Why that strategy was chosen. */ strategy_reason: string; } /** Response from POST /v1/try/oversight/analyze (demo mode). */ interface OversightDemoAnalyzeResponse { /** 'fast' when config.mode was 'fast', else 'single'. */ mode: 'single' | 'fast'; /** Analysis result. */ result: OversightAnalysisResult; /** Always true. */ try_endpoint: true; } /** Response type of oversight.analyze for a client constructed with the given `demo` flag. */ type OversightAnalyzeResponseFor = Demo extends true ? OversightDemoAnalyzeResponse : OversightAnalyzeResponse; /** Configuration for oversight.ingest. */ interface OversightIngestConfig { /** Custom model identifier. */ model?: string; } /** A conversation for ingest; `conversation_id` is required. */ interface OversightIngestConversation extends OversightConversation { conversation_id: string; } /** Options for oversight.ingest. */ interface OversightIngestOptions { /** * Conversations to analyze and store (1 to 300). The request body is capped at * 5 MB, so a batch near the count limit must consist of short conversations. */ conversations: OversightIngestConversation[]; /** * Legacy per-request callback. The API POSTs an unsigned * `{ event: 'ingestion_complete', timestamp, ingestion_id, conversations_processed, * errors_count, high_concern_count }` here when the batch completes. The signed * `oversight.ingestion.complete` event is delivered to webhooks registered with * `client.webhooks`, never to this URL. */ webhook_url?: string; /** Configuration options. */ config?: OversightIngestConfig; } /** A change the API made to a conversation before analysis. */ interface TruncationWarning { type: 'message_scaffolded' | 'message_truncated' | 'conversation_truncated'; details: string; } /** Per-conversation result from ingest. */ interface OversightIngestConversationResult { /** Your conversation id. */ conversation_id: string; /** Overall concern level. */ overall_concern: ConcernLevel; /** Number of behaviors detected. */ behaviors_detected: number; /** Present when the conversation was modified before analysis. */ truncation_warnings?: TruncationWarning[]; } /** Per-conversation error from ingest. */ interface OversightIngestError { /** Your conversation id. */ conversation_id: string; /** Error message. */ error: string; } /** * Response from POST /v1/oversight/ingest. * * Ingest is synchronous today: the call returns after every conversation is * analyzed, `status` is `complete` or `failed` (failed only when every * conversation failed), and `estimated_completion` is never set. `queued` * and `processing` stay in the union for forward compatibility. Billing is * 100 mills per conversation, deducted before analysis with no per- * conversation refund. */ interface OversightIngestResponse { /** Ingestion id for tracking. */ ingestion_id: string; /** Current status. */ status: 'queued' | 'processing' | 'complete' | 'failed'; /** Number of conversations received. */ conversations_received: number; /** Number of conversations processed. */ conversations_processed: number; /** Reserved; never set by the synchronous implementation. */ estimated_completion?: string; /** Dashboard URL for the results. */ dashboard_url: string; /** Per-conversation results (present when at least one succeeded). */ results?: OversightIngestConversationResult[]; /** Per-conversation errors (present when at least one failed). */ errors?: OversightIngestError[]; } /** Ensemble depth. `fast` is single-variant; `thorough` populates `stability`. */ type OcularThoroughness = 'fast' | 'auto' | 'thorough'; /** Per-axis level. */ type OcularLevel = 'critical' | 'high' | 'moderate' | 'low' | 'minimal'; /** Per-turn phase label in `trajectory_shape.phases`. */ type OcularPhase = 'baseline' | 'emerging' | 'escalating' | 'de-escalating' | 'crisis'; /** Options for client.ocular(). Either `messages` or `text` is required. */ interface OcularOptions { /** Conversation messages (roles user|assistant). The demo route accepts at most 12. */ messages?: Message[]; /** Plain text input (alternative to messages; demo route caps at 4,000 characters). */ text?: string; /** Ensemble depth. Omit for the server default (`auto`). */ thoroughness?: OcularThoroughness; /** * Score the conversation turn by turn and return `trajectory` (one entry * per scored turn; `turn` is the 0-based position in `messages`) and * `trajectory_shape`. Off by default; without it the response carries * neither field. Which turns are scored depends on `trajectory_stride`. * The demo route returns `trajectory` but never `trajectory_shape`. */ per_turn?: boolean; /** * With per_turn: score every Nth turn, counted backward from the last * turn so the final turn is always scored (1..64). The server default is * 3, so a 5-message conversation yields turns 4 and 1. Pass 1 to score * every turn. */ trajectory_stride?: number; /** Opaque end-user id (1..256 chars) for dashboard analytics. Never forwarded to the model. */ user_id?: string; /** Opaque session id (1..256 chars) for dashboard analytics. Never forwarded to the model. */ session_id?: string; /** Opaque agent id (1..256 chars) for dashboard analytics. Never forwarded to the model. */ agent_id?: string; } /** Per-axis output: level plus raw score in [0, 1]. */ interface OcularAxis { level: OcularLevel; score: number; } type OcularAxisGroup = Record; /** * Per-axis signal groups. * * `user` carries 8 axes: suicide, self_harm, harm_to_others, abuse, * sexual_violence, exploitation, stalking, self_neglect. * * `ai` carries 4 axes: harm_provision, emotional_failure, manipulation, * safeguarding_failure. */ interface OcularSignals { user: OcularAxisGroup; ai: OcularAxisGroup; } /** * Per-axis stability in [0, 1] across the variants Ocular ran (higher = more * consistent). Present only on multi-variant calls; otherwise `null`. */ interface OcularStability { user: Record; ai: Record; imminence: number; } /** Per-turn trajectory entry (only with `per_turn: true`). */ interface OcularTrajectoryEntry { /** 0-based position of the turn in `messages` (for `text` input, of the parsed speaker turn). */ turn: number; /** * Speaker of the turn: `user` or `assistant` (the gateway maps the * upstream `ai` label to `assistant`). Declared as string because any * other upstream label passes through unchanged. */ role: string; /** Per-turn salience, same cascade as the top-level field. */ salience: number; /** * Per-turn axis scores keyed by the public axis vocabulary: user axes by * bare name (`suicide`), AI axes prefixed (`ai_manipulation`), plus the * `fiction` and `genuine` context scalars. Absent when the turn had none. */ signals_by_axis?: Record; } /** * Arc summary of a per-turn trajectory (only with `per_turn: true`). The * phase, slope and peak fields track the suicide (crisis) axis and index * the `trajectory` array (entry i, not message i); `onsets` spans every * axis and uses `turn` values. The authenticated route returns it whenever * at least one turn was scored; the demo route never returns it. Every key * is optional. */ interface OcularTrajectoryShape { /** axis -> the `turn` value (0-based message position) at which that axis first crossed its onset threshold. */ onsets?: Record; /** Phase label per `trajectory` entry: `phases[i]` describes `trajectory[i]`. */ phases?: OcularPhase[]; /** Crisis-axis slope per `trajectory` entry: the delta versus the previous scored turn; the first is 0. */ slopes?: number[]; /** * Position in the `trajectory` array of the entry with the highest * crisis-axis score; `trajectory[peak_turn].turn` is the message index. */ peak_turn?: number; /** Highest crisis-axis score across the scored turns. */ peak_crisis?: number; } /** Response metadata. `windowed` and `windows` are always present (`false`, `1` for unwindowed input). */ interface OcularMeta { /** Ocular model build identifier. */ version: string; /** Upstream inference time (ms). */ inference_ms: number; /** Whether the input was split into windows at the gateway. */ windowed?: boolean; /** Number of windows the input was split into. */ windows?: number; /** True if any window's content was truncated to fit. */ truncated?: boolean; } /** * Response from POST /v1/ocular ($0.0001 per call). * * Decisions key off the continuous `salience` score in [0, 1] plus the axes * under `signals.user` (8) and `signals.ai` (4). Reference cutoffs are * T_WATCH = 0.30 and T_DANGER = 0.60 (docs.nope.net/ocular). `fiction` and * `authenticity` are context modulators already factored into `salience`. * `trajectory` and `trajectory_shape` are present only when the request set * `per_turn: true`. */ interface OcularResponse { /** Continuous severity score in [0, 1]. */ salience: number; /** Who the speaker-side risk pertains to ('self', 'other', 'unknown'). */ subject: string; /** How urgent the concern is (a separate axis). */ imminence: OcularAxis; /** Roleplay or fiction-framing strength in [0, 1]. */ fiction: number; /** Authenticity-of-distress signal in [0, 1]. */ authenticity: number; /** 8 user-risk axes and 4 AI-behavior axes. */ signals: OcularSignals; /** Which ensemble depth ran. */ thoroughness: OcularThoroughness; /** Aggregate confidence across variants; null on single-variant calls. */ confidence: number | null; /** Per-axis stability across variants; null on single-variant calls. */ stability: OcularStability | null; /** Response metadata. */ meta: OcularMeta; /** Per-turn trail (only with `per_turn: true`): one entry per scored turn, every `trajectory_stride`-th turn back from the last. */ trajectory?: OcularTrajectoryEntry[]; /** Arc summary of the trail (only with `per_turn: true`; never on the demo route). */ trajectory_shape?: OcularTrajectoryShape; } /** A screening head on the demo surface, under its public family name. */ interface OcularHead { code: string; score: number; } /** Demo per-turn entry; may also carry the turn's screening heads. */ interface OcularDemoTrajectoryEntry extends OcularTrajectoryEntry { heads?: OcularHead[]; } /** * Response from POST /v1/try/ocular (demo mode). The customer fields plus * screening `heads` and per-head `detail.scores` / `detail.calibrated`, * keyed by public family names such as `USER_SUICIDE_HEAD_A`. The demo * route returns `trajectory` with `per_turn` but never `trajectory_shape`. */ interface OcularDemoResponse extends OcularResponse { heads: OcularHead[]; detail: { scores: Record; calibrated: Record; }; trajectory?: OcularDemoTrajectoryEntry[]; } /** Response type of ocular() for a client constructed with the given `demo` flag. */ type OcularResponseFor = Demo extends true ? OcularDemoResponse : OcularResponse; /** A top-up denomination. */ interface BillingTopupOption { id: string; amount_mills: number; /** e.g. '$10'. */ label: string; /** Screens this amount buys. */ screens: number; /** Evaluate calls this amount buys. */ evaluates: number; /** Smart signpost calls this amount buys. */ resources_smart: number; } /** A past top-up. */ interface BillingTopupRecord { id: string; amount_mills: number; amount_formatted: string; status: string; created_at: string; completed_at: string | null; } /** Response from GET /v1/billing/balance. */ interface BillingBalanceResponse { balance_mills: number; /** e.g. '$12.35'. */ balance_formatted: string; /** Screens the balance covers. */ estimated_screens: number; /** Evaluate calls the balance covers. */ estimated_evaluates: number; /** Smart signpost calls the balance covers. */ estimated_resources_smart: number; low_balance: boolean; topup_history: BillingTopupRecord[]; topup_options: BillingTopupOption[]; } /** Options for billing.usage(). Dates are ISO 8601; default is the current month. */ interface BillingUsageOptions { start_date?: string; end_date?: string; } /** Per-endpoint usage line. */ interface BillingUsageBreakdown { /** Endpoint key (e.g. 'evaluate', 'oversight_analyze', 'v0_screen'). */ endpoint: string; calls: number; cost_mills: number; cost_formatted: string; /** Calls that triggered a referral. */ referrals: number; } /** Response from GET /v1/billing/usage. */ interface BillingUsageResponse { period_start: string; /** null when no end_date was given. */ period_end: string | null; total_spend_mills: number; total_spend_formatted: string; breakdown: BillingUsageBreakdown[]; } /** Options for billing.usageHistory(). */ interface BillingUsageHistoryOptions { /** Page size (default 50, max 100). */ limit?: number; offset?: number; /** Filter to one endpoint path. */ endpoint?: string; start_date?: string; end_date?: string; } /** One billed call. */ interface BillingUsageRecord { id: string; endpoint: string; cost_mills: number; cost_formatted: string; metadata: Record | null; created_at: string; } /** Response from GET /v1/billing/usage/history. */ interface BillingUsageHistoryResponse { records: BillingUsageRecord[]; total: number; limit: number; offset: number; } /** One priced endpoint. */ interface BillingPricingEntry { cost_mills: number; /** e.g. '$0.003' or 'Free'. */ cost_display: string; description?: string; } /** Pricing by endpoint key. Unknown keys are tolerated. */ interface BillingPricingTable { evaluate: BillingPricingEntry; ocular: BillingPricingEntry; signpost_smart: BillingPricingEntry; resources_smart: BillingPricingEntry; oversight_analyze: BillingPricingEntry; /** Billed per conversation. */ oversight_ingest: BillingPricingEntry; v0_screen: BillingPricingEntry; /** Alias of v0_screen. */ screen: BillingPricingEntry; v0_evaluate: BillingPricingEntry; [endpoint: string]: BillingPricingEntry | undefined; } /** Response from GET /v1/billing/pricing (public, no key). */ interface BillingPricingResponse { /** 'mills'. */ unit: string; unit_description: string; pricing: BillingPricingTable; topup_options: BillingTopupOption[]; free_credit_mills: number; free_credit_display: string; } /** Options for billing.topup(). */ interface BillingTopupOptions { /** One of the topup_options amounts (10000, 25000, 50000, 100000). */ amount_mills: number; /** Where Stripe sends the user after payment (default: the dashboard). */ success_url?: string; /** Where Stripe sends the user on cancel (default: the dashboard). */ cancel_url?: string; } /** Response from POST /v1/billing/topup. */ interface BillingTopupResponse { /** Stripe Checkout URL to redirect the user to. */ checkout_url: string; } /** * Webhook verification and payload types. * * NOPE signs every delivery with HMAC-SHA256 over `"${timestamp}.${body}"` * where `body` is the exact bytes it sent (JSON.stringify of the payload), * and sets these headers: * * X-NOPE-Signature: sha256= * X-NOPE-Timestamp: * X-NOPE-Event: evaluate.alert | oversight.alert | oversight.ingestion.complete | test.ping * X-NOPE-Delivery-ID: id of the stored delivery (the `id` of the matching * WebhookEvent from client.webhooks.events()); retries * of one delivery repeat it. Distinct from the * payload's `event_id`. * X-NOPE-Webhook-ID: * * Pass the raw request body (string or bytes) to `verify` / `verifyRequest`. * Passing an already-parsed object also works (the SDK re-serialises it with * JSON.stringify), but only when your JSON parser preserved key order, so the * raw body is the supported input. * * @example * ```typescript * import { Webhook, WebhookSignatureError } from '@nope-net/sdk'; * * app.post('/webhooks/nope', express.raw({ type: 'application/json' }), (req, res) => { * try { * const { payload } = Webhook.verifyRequest(req.body, req.headers, process.env.NOPE_WEBHOOK_SECRET!); * if (payload.event === 'evaluate.alert') { * console.log(payload.risk_summary.overall_severity); * } * res.status(200).send('OK'); * } catch (err) { * if (err instanceof WebhookSignatureError) res.status(401).send('Invalid signature'); * else throw err; * } * }); * ``` */ /** Webhook event types. */ type WebhookEventType = 'evaluate.alert' | 'oversight.alert' | 'oversight.ingestion.complete' | 'test.ping'; /** Risk level threshold for webhook filtering (`min_risk_level`). */ type WebhookRiskLevel = 'none' | 'low' | 'medium' | 'high' | 'critical'; /** Risk summary on an evaluate.alert payload. */ interface WebhookRiskSummary { overall_severity: Severity; overall_imminence: Imminence; /** Risk type of the highest self-directed risk, or 'none'. */ primary_domain: string; confidence: number; /** * Narrative of the key concerns. Declared as a string by the API; the * builder copies the classifier's value through, which has been seen as an * array of strings on the wire. */ primary_concerns: string | string[]; } /** Per-risk assessment on an evaluate.alert payload. */ interface WebhookDomainAssessment { domain: string; severity: Severity; imminence: Imminence; } /** Safeguarding flags on an evaluate.alert payload. */ interface WebhookFlags { intimate_partner_violence: string | null; child_safeguarding: string | null; third_party_threat: boolean; } /** A resource that was included in the API response. */ interface WebhookResourceProvided { name: string; type: string; country: string; } /** Conversation content (only when the webhook has include_conversation). */ interface WebhookConversation { included: boolean; message_count?: number; /** Latest user message, truncated to 1,000 characters. */ latest_user_message?: string; truncated?: boolean; } /** Fields shared by every event. */ interface WebhookPayloadBase { /** Event type. */ event: WebhookEventType; /** * Unique event id (`evt_<32 hex>`), generated when the payload is built. * The X-NOPE-Delivery-ID header carries a different value: the stored * delivery's id. Retries repeat both, so either one deduplicates. */ event_id: string; /** ISO 8601 creation time. */ timestamp: string; /** Payload format version. */ api_version: '2025-01'; } /** Sent when /v1/evaluate detects risk at or above the webhook's min_risk_level. */ interface EvaluateAlertPayload extends WebhookPayloadBase { event: 'evaluate.alert'; /** Your conversation_id from the evaluate request, if any. */ conversation_id?: string; /** Your end_user_id from the evaluate request, if any. */ user_id?: string; risk_summary: WebhookRiskSummary; /** Per-domain assessments. */ domains: WebhookDomainAssessment[]; flags: WebhookFlags; /** Resources that were provided in the API response. */ resources_provided: WebhookResourceProvided[]; conversation: WebhookConversation; } /** A behavior on an oversight.alert payload (up to 5). */ interface OversightAlertBehavior { code: string; name: string; severity: string; category: string; } /** Sent when Oversight (analyze or ingest) finds high or critical concern. */ interface OversightAlertPayload extends WebhookPayloadBase { event: 'oversight.alert'; conversation_id: string; /** Concern level that triggered the alert. */ concern: 'high' | 'critical'; trajectory: Trajectory; /** Human-readable summary of the analysis. */ summary: string; /** Top behaviors detected (up to 5). */ behaviors: OversightAlertBehavior[]; /** Agent ids involved in the conversation. */ agent_ids?: string[]; /** Platform from the conversation metadata. */ platform?: string; user_is_minor: boolean; /** Conversation content (only when the webhook has include_conversation). */ conversation?: WebhookConversation; } /** Sent when an ingest batch finishes. */ interface OversightIngestionCompletePayload extends WebhookPayloadBase { event: 'oversight.ingestion.complete'; ingestion_id: string; conversations_total: number; conversations_processed: number; conversations_failed: number; /** Count of conversations at each concern level. */ concerns: Record; /** Top behaviors across the batch. */ top_behaviors: Array<{ code: string; name: string; occurrence_count: number; }>; processing_time_ms: number; } /** Sent by the dashboard "test" button and by client.webhooks.test(). */ interface TestPingPayload extends WebhookPayloadBase { event: 'test.ping'; message: string; } /** Every webhook payload, discriminated on `event`. */ type WebhookPayload = EvaluateAlertPayload | OversightAlertPayload | OversightIngestionCompletePayload | TestPingPayload; /** Thrown when signature verification fails or the timestamp is out of range. */ declare class WebhookSignatureError extends Error { constructor(message: string); } interface WebhookVerifyOptions { /** * Maximum age of the timestamp in seconds, in either direction (default * 300). Set to 0 to disable the check (not recommended). */ maxAgeSeconds?: number; } /** Raw body (string or bytes, preferred) or an already-parsed object. */ type WebhookBody = string | Uint8Array | object; /** Node's IncomingHttpHeaders, a fetch Headers instance, or any header map. */ type WebhookHeaders = { get(name: string): string | null; } | Record; /** Result of verifyRequest. */ interface VerifiedWebhook { /** The verified, parsed payload. */ payload: WebhookPayload; /** * The X-NOPE-Delivery-ID header: the stored delivery's id, repeated on * each retry of that delivery, so it deduplicates the API's retries. It * is not `payload.event_id`. Undefined when the header is absent, for * example on a body signed locally with `Webhook.sign`. */ deliveryId?: string; /** @deprecated Use deliveryId; this is the X-NOPE-Delivery-ID header, not payload.event_id. */ eventId?: string; /** The X-NOPE-Webhook-ID header: id of the webhook endpoint that received the delivery. */ webhookId?: string; /** * The X-NOPE-Event header: the event type (`evaluate.alert`, * `oversight.alert`, `oversight.ingestion.complete` or `test.ping`). The * same value is `payload.event`; the header lets you route before * parsing. Undefined when the header is absent. */ eventType?: string; } /** * Webhook verification and signing. */ declare const Webhook: { /** * Verify a delivery and return its parsed payload. * * @param payload - Raw request body (string or bytes, preferred) or the parsed object * @param signature - X-NOPE-Signature header value * @param timestamp - X-NOPE-Timestamp header value (unix seconds) * @param secret - Your webhook signing secret (`whsec_...`) * @param options - `maxAgeSeconds` (default 300; 0 disables) * @throws WebhookSignatureError when a header is missing, the timestamp is out of range, or the signature does not match */ verify(payload: WebhookBody, signature: string | undefined, timestamp: string | undefined, secret: string, options?: WebhookVerifyOptions): WebhookPayload; /** * Verify a delivery from its raw body and request headers. * * Reads `x-nope-signature` and `x-nope-timestamp` (case-insensitive) from * a Node `IncomingHttpHeaders` object, a fetch `Headers` instance, or any * plain map, and returns the payload with `deliveryId` * (X-NOPE-Delivery-ID), `webhookId` (X-NOPE-Webhook-ID) and `eventType` * (X-NOPE-Event). Each id is undefined when its header is absent. * * @param body - Raw request body (string or bytes, preferred) or the parsed object * @param headers - Request headers * @param secret - Your webhook signing secret * @param options - `maxAgeSeconds` (default 300; 0 disables) */ verifyRequest(body: WebhookBody, headers: WebhookHeaders, secret: string, options?: WebhookVerifyOptions): VerifiedWebhook; /** * Sign a body the way the API does (for tests and local replay). * * The optional third argument fixes the timestamp the HMAC is bound to. * Omit it to sign as of now; pass a unix-seconds value to produce a * delivery from a chosen moment, for example one older than * `maxAgeSeconds` to exercise the stale-timestamp rejection. * * @param payload - Body to sign: a string or bytes as-is, or an object serialised with JSON.stringify * @param secret - Signing secret * @param timestamp - Unix seconds (an integer; defaults to now) * @returns `signature` (`sha256=`) and `timestamp` as strings */ sign(payload: WebhookBody, secret: string, timestamp?: number): { signature: string; timestamp: string; }; }; /** Body of client.webhooks.create(). */ interface WebhookCreateOptions { /** HTTPS endpoint (http is accepted for localhost only; private ranges are rejected). */ url: string; /** Minimum risk level that triggers a delivery (default 'high'). */ min_risk_level?: WebhookRiskLevel; /** Include the latest user message in payloads (default false). */ include_conversation?: boolean; } /** Body of client.webhooks.update(). */ interface WebhookUpdateOptions { url?: string; min_risk_level?: WebhookRiskLevel; enabled?: boolean; include_conversation?: boolean; } /** A webhook configuration. `secret` is present only on create and regenerateSecret. */ interface WebhookResponse { id: string; url: string; min_risk_level: WebhookRiskLevel; enabled: boolean; include_conversation: boolean; created_at: string; updated_at: string; /** Signing secret (`whsec_...`). Returned once, on create. */ secret?: string; } /** Response from client.webhooks.list(). */ interface WebhookListResponse { webhooks: WebhookResponse[]; } /** Response from client.webhooks.delete(). */ interface WebhookDeleteResponse { success: true; } /** Response from client.webhooks.regenerateSecret(). */ interface WebhookSecretResponse { /** The new signing secret. The old one stops working immediately. */ secret: string; } /** Result of a delivery attempt (client.webhooks.test()). */ interface WebhookDeliveryResult { success: boolean; http_status?: number; error_message?: string; duration_ms: number; } /** Delivery status of a stored event. */ type WebhookDeliveryStatus = 'pending' | 'sent' | 'failed'; /** A stored delivery (client.webhooks.events()). */ interface WebhookEvent { id: string; webhook_id: string; event_type: WebhookEventType; payload: WebhookPayload; status: WebhookDeliveryStatus; http_status?: number; error_message?: string; /** Attempts so far (the API retries 4 times: immediately, 1 min, 10 min, 1 h). */ attempt_count: number; last_attempt_at?: string; next_retry_at?: string; created_at: string; } /** Options for client.webhooks.events(). */ interface WebhookEventsOptions { /** Max events (default 50, max 100). */ limit?: number; } /** Response from client.webhooks.events(). */ interface WebhookEventsResponse { events: WebhookEvent[]; } /** * NOPE SDK Client * * Main client for interacting with the NOPE API. */ /** * Client for the NOPE safety API. * * @example * ```typescript * import { NopeClient } from '@nope-net/sdk'; * * const client = new NopeClient({ apiKey: 'nope_live_...' }); * const result = await client.evaluate({ * messages: [{ role: 'user', content: 'I feel hopeless' }], * config: { country: 'US' } * }); * console.log(result.speaker_severity); * ``` */ /** * Client for the NOPE API. * * `Demo` mirrors the `demo` constructor option so that methods whose demo * response differs (`oversight.analyze`) return the matching type. */ declare class NopeClient { private readonly transport; private readonly demo; /** * Initialize the NOPE client. * * @param options - Client configuration options * @param options.apiKey - Your NOPE API key (`nope_live_...`). Omit for demo mode. * @param options.baseUrl - Override the API base URL. Defaults to https://api.nope.net * @param options.timeout - Request timeout in milliseconds. Defaults to 30000 * @param options.demo - Route to the unauthenticated /v1/try/* endpoints * @param options.maxRetries - Retries on 429/503 only. Defaults to 2 * @param options.fetch - Fetch implementation (defaults to the global fetch) * @param options.sleep - Sleep between retries (defaults to setTimeout) */ constructor(options?: NopeClientOptions & { demo?: Demo; }); /** * Evaluate a conversation for safety risks ($0.003 per call). * * Either `messages` or `text` must be provided, and only one of them. * Client-side checks: 1 to 100 messages, roles `user` or `assistant`, * non-empty text. * * Demo mode routes to `/v1/try/evaluate`: no key, per-IP rate limit, the * last 10 messages only. `config.country` is also sent as `user_country`, * the older spelling the try route accepts when `country` is absent. * * @param options.messages - Conversation messages * @param options.text - Plain text input (free-form transcripts) * @param options.config - `country`, `include_resources`, `conversation_id`, `end_user_id` * * @returns risks, speaker severity and imminence, rationale, matched resources * * @throws {NopeAuthError} Invalid or missing API key * @throws {NopeValidationError} Client-side check failed (no statusCode, code `invalid_request`), invalid request payload (400) or body over 512 KB (413) * @throws {NopeInsufficientBalanceError} Balance cannot cover the call (402) * @throws {NopeRateLimitError} Rate limit exceeded after retries * @throws {NopeServiceUnavailableError} Provider outage after retries (503) * @throws {NopeServerError} Other server error * @throws {NopeConnectionError} Connection failed or timed out * * @example * ```typescript * const result = await client.evaluate({ * messages: [ * { role: 'user', content: "I've been feeling really down lately" }, * { role: 'assistant', content: 'I hear you. Can you tell me more?' }, * { role: 'user', content: "I just don't see the point anymore" } * ], * config: { country: 'US' } * }); * * if (result.show_resources && result.resources) { * console.log(`${result.resources.primary.name}: ${result.resources.primary.phone}`); * } * ``` */ evaluate(options: EvaluateOptions): Promise; /** * Behavioral risk assessment via Ocular ($0.0001 per call). * * Returns a continuous `salience` score in [0, 1] plus structural axes: * 8 user-risk axes under `signals.user`, 4 AI-behavior axes under * `signals.ai`, an `imminence` axis, and the `fiction` / `authenticity` * context modulators. Individual head identities are not exposed. * * Pick the `salience` cutoff that fits your action; the reference * thresholds are T_WATCH = 0.30 and T_DANGER = 0.60. Set `per_turn: true` * to receive `trajectory` (per-turn salience and axis scores) and * `trajectory_shape`; without it neither field is present. The server * scores every third turn back from the last unless `trajectory_stride` * says otherwise; `turn` values are 0-based positions in `messages`, and * `trajectory_shape.peak_turn`, `phases` and `slopes` index the * `trajectory` array while `onsets` uses `turn` values. * `meta.windowed` and `meta.windows` are always present. * * Demo mode routes to `/v1/try/ocular` (at most 12 messages or 4,000 * characters) and returns {@link OcularDemoResponse}, which adds `heads` * and `detail` keyed by public family names; it returns `trajectory` with * `per_turn` but never `trajectory_shape`. * * @param options.messages - Conversation messages (roles user|assistant) * @param options.text - Plain text input * @param options.thoroughness - 'fast' | 'auto' | 'thorough' * @param options.per_turn - Score the conversation turn by turn (subject to `trajectory_stride`) and return `trajectory` and `trajectory_shape` * @param options.trajectory_stride - With per_turn: score every Nth turn back from the last (1..64; server default 3). Pass 1 to score every turn * @param options.user_id - Opaque id for dashboard analytics (never forwarded to the model) * @param options.session_id - Opaque id for dashboard analytics * @param options.agent_id - Opaque id for dashboard analytics * * @example * ```typescript * const result = await client.ocular({ * messages: [{ role: 'user', content: 'I feel hopeless' }], * per_turn: true, * }); * console.log(result.salience, result.subject); * const sui = result.signals.user.suicide; * if (sui && sui.score > 0.5) { * // escalate * } * for (const turn of result.trajectory ?? []) { * console.log(turn.turn, turn.salience, turn.signals_by_axis?.suicide); * } * ``` */ ocular(options: OcularOptions): Promise>; /** * Lightweight crisis screening (legacy). * * @deprecated Use `evaluate()` instead. The screen endpoint has been consolidated * into evaluate ($0.003/call). * This method calls the legacy `/v0/screen` endpoint ($0.001/call). * * Fast, cheap endpoint for detecting suicidal ideation and self-harm. * Returns independent detection flags, tuned conservatively (biased toward detection). * * Either `messages` or `text` must be provided, but not both. * * @param options - Screen options * @param options.messages - List of conversation messages * @param options.text - Plain text input (for free-form transcripts) * @param options.config - `country`, `debug`, `include_recommended_reply` * * @returns ScreenResponse with show_resources, suicidal_ideation, self_harm flags * * @throws {NopeAuthError} Invalid or missing API key * @throws {NopeValidationError} Client-side check failed or demo refusal (no statusCode), or invalid request payload (400) * @throws {NopeRateLimitError} Rate limit exceeded * @throws {NopeServerError} Server error * @throws {NopeConnectionError} Connection failed * * @example * ```typescript * const result = await client.screen({ * text: "I've been having dark thoughts lately" * }); * * if (result.show_resources) { * console.log(`SI: ${result.suicidal_ideation}, SH: ${result.self_harm}`); * console.log(`Rationale: ${result.rationale}`); * if (result.resources) { * console.log(`Call ${result.resources.primary.phone}`); * } * } * ``` */ screen(options: ScreenOptions): Promise; /** * Oversight: AI behavior analysis using 91 behavior codes across 14 * categories. The taxonomy contains 87 harmful and 4 appropriate codes. * See {@link OversightBehaviorCode}. * * Requires an account with the Oversight feature enabled * (NopeFeatureError otherwise). analyze costs 100 mills ($0.10) per call, * ingest 100 mills per conversation. */ readonly oversight: { /** * Analyze one conversation for harmful AI behaviours. The call is * synchronous and does not write conversation content or full results to * the Oversight database. Operational and analysis-event metadata is * retained under the public policy. Use `ingest` for dashboard storage. * * Turn numbers in the result count assistant turns from 1. In fast mode * (`config.mode: 'fast'`) `trajectory` is always `stable`, * `turn_analysis` and `human_indicators` are empty, and `summary` and * `pattern_assessment` are absent. * * Demo mode routes to `/v1/try/oversight/analyze` and returns * {@link OversightDemoAnalyzeResponse}. The demo route ignores * `config.strategy` and `config.model`, keeps only `role` and `content` * of each message, and accepts at most 20 messages of 10,000 characters. * * @param options.conversation - The conversation to analyze * @param options.bot_context - Description of the bot or persona, passed into the analysis as conversation metadata * @param options.config - `strategy`, `mode`, `include_raw_xml`, `model` * @param options.behaviors - Post-analysis filter: `enabled` xor `disabled`, `min_severity`, `categories` * * @throws {NopeFeatureError} Oversight not enabled for this account (403) * @throws {NopeInsufficientBalanceError} Balance cannot cover the call (402) * @throws {NopeValidationError} Client-side check failed (no statusCode, code `invalid_request`) or invalid request (400) * * @example * ```typescript * const { result } = await client.oversight.analyze({ * conversation: { * conversation_id: 'conv_123', * messages: [ * { role: 'user', content: 'Nobody at work listens to me' }, * { role: 'assistant', content: "I'm always here. I understand you better than they ever will." } * ], * metadata: { platform: 'companion-app' } * }, * config: { mode: 'fast' }, * behaviors: { min_severity: 'medium' } * }); * for (const behavior of result.detected_behaviors) { * console.log(`${behavior.code}: ${behavior.severity} - ${behavior.recommendation}`); * } * ``` */ analyze: (options: OversightAnalyzeOptions) => Promise>; /** * Analyze and store up to 300 conversations for the dashboard, * cross-session tracking and audit. * * Synchronous: the call returns once every conversation is analyzed. * Billing is 100 mills per conversation, deducted before analysis; a * 402 carries `perConversationMills` and `conversations`. Not available * in demo mode. * * @param options.conversations - 1 to 300 conversations, each with a `conversation_id` * @param options.webhook_url - Legacy per-request callback: the API POSTs an unsigned * `{ event: 'ingestion_complete', timestamp, ingestion_id, conversations_processed, * errors_count, high_concern_count }` to this URL when the batch completes. The signed * `oversight.ingestion.complete` event goes to webhooks registered with `client.webhooks`. * @param options.config - `model` * * @throws {NopeFeatureError} Oversight not enabled for this account (403) * @throws {NopeInsufficientBalanceError} Balance cannot cover the batch (402) * @throws {NopeValidationError} Client-side check failed or demo refusal (no statusCode), or invalid request (400) * * @example * ```typescript * const result = await client.oversight.ingest({ * conversations: [ * { conversation_id: 'conv_001', messages: [{ role: 'user', content: 'hi' }, { role: 'assistant', content: 'hello' }] }, * { conversation_id: 'conv_002', messages: [{ role: 'user', content: 'hey' }, { role: 'assistant', content: 'hi there' }] } * ], * webhook_url: 'https://api.example.com/webhooks/nope' * }); * console.log(`${result.conversations_processed}/${result.conversations_received} processed`); * ``` */ ingest: (options: OversightIngestOptions) => Promise; }; /** * Manage webhook endpoints. Deliveries are verified with {@link Webhook}. * Not available in demo mode. The family is rate limited at 30/min. */ readonly webhooks: { /** * Register an endpoint. The response carries the signing `secret` once; * store it. Requires a paid plan (NopeFeatureError with `upgradeUrl` * otherwise). URLs must be HTTPS (http only for localhost) and public. */ create: (options: WebhookCreateOptions) => Promise; /** List the account's webhooks (without secrets). */ list: () => Promise; /** One webhook by id (NopeNotFoundError when unknown). */ get: (id: string) => Promise; /** Update url, min_risk_level, enabled or include_conversation. */ update: (id: string, patch: WebhookUpdateOptions) => Promise; /** Delete a webhook. */ delete: (id: string) => Promise; /** Rotate the signing secret. The old secret stops working immediately. */ regenerateSecret: (id: string) => Promise; /** * Send a `test.ping` to the endpoint and return the delivery result. * A failed delivery comes back as `{success: false, ...}` (the API * answers 502 with the same body), so callers branch on `success` * rather than catching. */ test: (id: string) => Promise; /** * Recent deliveries for one webhook, or for the whole account when `id` * is omitted. `limit` defaults to 50 (max 100). */ events: (id?: string, options?: WebhookEventsOptions) => Promise; }; /** * Balance, usage and pricing. Amounts are in mills (1 mill = $0.001). * `pricing()` is public and works on any client, demo included; the other * calls need an API key and refuse demo mode. */ readonly billing: { /** Current balance, estimates, top-up history and options. */ balance: () => Promise; /** Spend by endpoint for a period (default: the current month). */ usage: (options?: BillingUsageOptions) => Promise; /** Individual billed calls, paginated. */ usageHistory: (options?: BillingUsageHistoryOptions) => Promise; /** Public price list (no key needed). */ pricing: () => Promise; /** * Create a Stripe Checkout session for a top-up and return its URL. * `amount_mills` must be one of the `topup_options` amounts * (NopeValidationError with `details.valid_options` otherwise). */ topup: (options: BillingTopupOptions) => Promise; }; /** * Crisis resources for a country (free, no model call; API key required). * * Filters may be passed at the top level or under `config`; a top-level * value wins. Arrays are sent comma-joined. Scope and population values * come from the generated {@link ServiceScope} and {@link Population} * vocabularies (the API returns 400 for unknown values). Not available in * demo mode. * * @param options.country - ISO 3166-1 alpha-2 code (e.g. 'US', 'GB') * @param options.scopes - e.g. ['suicide', 'domestic_violence'] * @param options.populations - e.g. ['youth', 'veterans'] * @param options.subdivisions - e.g. ['GB-NIR'] * @param options.limit - Server cap 10 * @param options.urgent - Ranking hint: 24/7 resources first among ties on relevance and priority tier; nothing is filtered out * * @example * ```typescript * const result = await client.signpost({ country: 'GB', scopes: ['suicide'], urgent: true }); * for (const resource of result.resources) { * console.log(`${resource.name}: ${resource.phone ?? resource.website_url}`); * } * ``` */ signpost(options: SignpostOptions): Promise; /** * Crisis resources ranked for a described situation ($0.001 per call). * * A model ranks the country's candidate pool against `query` and returns * up to 5 picks, each with a one-line `why`. Demo mode routes to * `/v1/try/signpost/smart` (no key, per-IP rate limit). * * @param options.country - ISO 3166-1 alpha-2 code * @param options.query - Natural-language description (max 500 characters) * @param options.config - `scopes`, `populations`, `limit` * * @example * ```typescript * const result = await client.signpostSmart({ * country: 'US', * query: 'teen struggling with an eating disorder' * }); * for (const pick of result.ranked) { * console.log(`${pick.rank}. ${pick.resource.name}: ${pick.why}`); * } * ``` */ signpostSmart(options: SignpostSmartOptions): Promise; /** * Semantic search across the whole resource directory (free; API key * required). Uses pre-computed embeddings rather than model ranking and is * not country-scoped unless `country` is given. Results are raw directory * rows ({@link SignpostSearchResult}) and carry the database `id`. Not * available in demo mode. * * @param options.query - Natural-language query (max 500 characters) * @param options.country - Optional ISO 3166-1 alpha-2 filter * @param options.limit - Max results (default 10, max 50) * @param options.threshold - Similarity threshold in [0, 1] (default 0.3) * * @example * ```typescript * const hits = await client.signpostSearch({ query: 'lgbtq youth support', country: 'GB' }); * for (const hit of hits.results) { * console.log(`${hit.name} (${hit.similarity.toFixed(2)}): ${hit.phone ?? hit.website_url}`); * } * ``` */ signpostSearch(options: SignpostSearchOptions): Promise; /** * One crisis resource by database id (public, no key). Ids come from * signpostSearch() results. * * @throws {NopeValidationError} Malformed id (400) * @throws {NopeNotFoundError} Unknown id (404) */ signpostById(resourceId: string): Promise; /** * Country codes with crisis resources (public, no key). * * @example * ```typescript * const { countries, count } = await client.signpostCountries(); * console.log(`${count} countries, starting ${countries[0]}`); * ``` */ signpostCountries(): Promise; /** * Country detection from geo headers (public, no key). * * Works only behind a proxy that injects a geo header (Cloudflare's * `cf-ipcountry`, Vercel's `x-vercel-ip-country`, or `x-country`). Called * directly against api.nope.net it returns the miss shape with * `detected: false`. Pass `countryHint` to send `x-country` yourself. * * @example * ```typescript * const geo = await client.detectCountry(); * const country = geo.detected ? geo.country_code : 'US'; * ``` */ detectCountry(options?: DetectCountryOptions): Promise; /** * @deprecated Use signpost(). Calls /v1/resources, which the API serves * with Deprecation and Sunset headers until 2027-01-01. */ resources(options: ResourcesOptions): Promise; /** * @deprecated Use signpostSmart(). Calls /v1/resources/smart (sunset 2027-01-01). */ resourcesSmart(options: ResourcesSmartOptions): Promise; /** * @deprecated Use signpostById(). Calls /v1/resources/:id (sunset 2027-01-01). */ resourceById(resourceId: string): Promise; /** * @deprecated Use signpostCountries(). Calls /v1/resources/countries (sunset 2027-01-01). */ resourcesCountries(): Promise; private warnResourcesDeprecated; private requireNotDemo; /** Rate-limit and balance headers from the most recent response (undefined before the first call). */ get lastResponseMeta(): ResponseMeta | undefined; /** Send a JSON body. */ private request; } declare const SDK_VERSION: string; export { type AggregatedBehavior, type ApiErrorBody, type BillingBalanceResponse, type BillingPricingEntry, type BillingPricingResponse, type BillingPricingTable, type BillingTopupOption, type BillingTopupOptions, type BillingTopupRecord, type BillingTopupResponse, type BillingUsageBreakdown, type BillingUsageHistoryOptions, type BillingUsageHistoryResponse, type BillingUsageOptions, type BillingUsageRecord, type BillingUsageResponse, type ConcernLevel, type CrisisResource, type CrisisResourceKind, type CrisisResourcePriorityTier, type CrisisResourceType, type DetectCountryOptions, type DetectCountryResponse, type DetectCountryResult, type DetectedBehavior, type EvaluateAlertPayload, type EvaluateConfig, type EvaluateMetadata, type EvaluateOptions, type EvaluateRequest, type EvaluateResource, type EvaluateResources, type EvaluateResponse, type FetchLike, type HoursConfidence, type HumanIndicator, type HumanIndicatorType, IMMINENCE_SCORES, type Imminence, type InflectionPoint, MAX_RETRY_WAIT_SECONDS, type Message, NopeAuthError, NopeClient, type NopeClientOptions, NopeConnectionError, NopeError, type NopeErrorOptions, NopeFeatureError, type NopeFeatureErrorOptions, NopeInsufficientBalanceError, type NopeInsufficientBalanceErrorOptions, NopeNotFoundError, NopeRateLimitError, type NopeRateLimitErrorOptions, NopeServerError, type NopeServerErrorOptions, NopeServiceUnavailableError, NopeValidationError, type NopeValidationErrorOptions, OVERSIGHT_BEHAVIOR_CATEGORIES, OVERSIGHT_BEHAVIOR_CODES, OVERSIGHT_BEHAVIOR_CODES_BY_CATEGORY, type OcularAxis, type OcularAxisGroup, type OcularDemoResponse, type OcularDemoTrajectoryEntry, type OcularHead, type OcularLevel, type OcularMeta, type OcularOptions, type OcularPhase, type OcularResponse, type OcularResponseFor, type OcularSignals, type OcularStability, type OcularThoroughness, type OcularTrajectoryEntry, type OcularTrajectoryShape, type OpenStatus, type OtherContact, type OversightAlertBehavior, type OversightAlertPayload, type OversightAnalysisMode, type OversightAnalysisResult, type OversightAnalysisStrategy, type OversightAnalyzeConfig, type OversightAnalyzeOptions, type OversightAnalyzeResponse, type OversightAnalyzeResponseFor, type OversightBehaviorCategory, type OversightBehaviorCode, type OversightBehaviorFilter, type OversightConversation, type OversightConversationMetadata, type OversightDemoAnalyzeResponse, type OversightIngestConfig, type OversightIngestConversation, type OversightIngestConversationResult, type OversightIngestError, type OversightIngestOptions, type OversightIngestResponse, type OversightIngestionCompletePayload, type OversightMessage, type OversightSeverity, POPULATIONS, type Population, type RankedResource, type ResourceByIdResponse, type ResourceProminence, type ResourcesConfig, type ResourcesCountriesResponse, type ResourcesOptions, type ResourcesResponse, type ResourcesSmartOptions, type ResourcesSmartResponse, type ResponseMeta, type Risk, type RiskSubject, type RiskType, SDK_VERSION, SERVICE_SCOPES, SEVERITY_SCORES, type ScreenConfig, type ScreenCrisisResources, type ScreenDebugInfo, type ScreenOptions, type ScreenRecommendedReply, type ScreenResponse, type ScreenRisk, type ScreenRiskSubject, type ServiceScope, type Severity, type SignpostByIdResponse, type SignpostConfig, type SignpostCountriesResponse, type SignpostOptions, type SignpostResponse, type SignpostSearchContact, type SignpostSearchOpenStatus, type SignpostSearchOptions, type SignpostSearchResponse, type SignpostSearchResult, type SignpostSmartConfig, type SignpostSmartOptions, type SignpostSmartResponse, type SleepFn, type TestPingPayload, type Trajectory, type TruncationWarning, type TurnAnalysis, type VerifiedWebhook, Webhook, type WebhookBody, type WebhookConversation, type WebhookCreateOptions, type WebhookDeleteResponse, type WebhookDeliveryResult, type WebhookDeliveryStatus, type WebhookDomainAssessment, type WebhookEvent, type WebhookEventType, type WebhookEventsOptions, type WebhookEventsResponse, type WebhookFlags, type WebhookHeaders, type WebhookListResponse, type WebhookPayload, type WebhookPayloadBase, type WebhookResourceProvided, type WebhookResponse, type WebhookRiskLevel, type WebhookRiskSummary, type WebhookSecretResponse, WebhookSignatureError, type WebhookUpdateOptions, type WebhookVerifyOptions, type WindowAnalysis, calculateSpeakerImminence, calculateSpeakerSeverity, hasThirdPartyRisk };