@nexusm/sdk 5.0.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -360,25 +360,6 @@ declare class OfflineQueue {
360
360
  * every failure surfaces as a typed {@link NexusError} subclass.
361
361
  */
362
362
 
363
- /**
364
- * HTTP client that communicates with the Nexus API.
365
- *
366
- * All service-level modules (Memory, Conversation, Knowledge, Context)
367
- * delegate their network calls to a shared `HttpClient` instance, which
368
- * guarantees consistent authentication, timeout handling, and error
369
- * mapping across the entire SDK surface.
370
- *
371
- * @example
372
- * ```typescript
373
- * import { resolveConfig } from '../config';
374
- * import { HttpClient } from './client';
375
- *
376
- * const config = resolveConfig({ apiKey: 'nx_test_abc123' });
377
- * const http = new HttpClient(config);
378
- *
379
- * const memories = await http.get<Memory[]>('/memory/search', { query: 'hello' });
380
- * ```
381
- */
382
363
  declare class HttpClient {
383
364
  /** Underlying Axios instance. */
384
365
  private readonly axios;
@@ -457,6 +438,21 @@ declare class HttpClient {
457
438
  * @returns The parsed response body.
458
439
  */
459
440
  patch<T>(path: string, data?: unknown, signal?: AbortSignal): Promise<T>;
441
+ /**
442
+ * Send a GET request and return the raw response body as a string.
443
+ *
444
+ * Intended for file-download endpoints (e.g. `GET /dashboard/export`) that
445
+ * return `text/csv` or `application/json` as a raw file stream rather than a
446
+ * JSON-parsed object. The retry and auth interceptors still apply; the
447
+ * response cache is intentionally bypassed (export payloads are not cacheable
448
+ * at the SDK layer).
449
+ *
450
+ * @param path - URL path relative to the base URL (e.g. `/dashboard/export`).
451
+ * @param params - Optional query parameters.
452
+ * @param signal - Optional {@link AbortSignal} to cancel the request.
453
+ * @returns The raw response body as a string.
454
+ */
455
+ getText(path: string, params?: Record<string, unknown>, signal?: AbortSignal): Promise<string>;
460
456
  /**
461
457
  * Send a DELETE request.
462
458
  *
@@ -2305,6 +2301,133 @@ declare class ErrorService extends BaseService {
2305
2301
  submit(data: ErrorReportRequest, options?: RequestOptions): Promise<ErrorReportResponse>;
2306
2302
  }
2307
2303
 
2304
+ /**
2305
+ * @module types/dashboard
2306
+ * @description Dashboard Service type definitions.
2307
+ *
2308
+ * Mirrors the Nexus API dashboard export contract:
2309
+ * - GET /v1/dashboard/export — download a dataset as CSV or JSON
2310
+ *
2311
+ * The export endpoint returns raw file content (text/csv or application/json),
2312
+ * not a JSON-parsed object. The SDK therefore exposes these types only for
2313
+ * the _request_ side; the return value is `string`.
2314
+ */
2315
+ /**
2316
+ * The set of exportable dashboard datasets.
2317
+ *
2318
+ * Must stay in sync with the backend `DashboardExportDataset` Literal:
2319
+ * - `quality_distribution` — Quality score bucket distribution
2320
+ * - `feedback_trend` — Feedback rating trend over time
2321
+ * - `diagnosis_stats` — Diagnosis type statistics
2322
+ * - `feedback_health` — Overall feedback health metrics
2323
+ * - `error_heatmap` — Error frequency heatmap by endpoint / time
2324
+ * - `ab_distribution` — A/B experiment assignment distribution
2325
+ */
2326
+ type DashboardExportDataset = 'quality_distribution' | 'feedback_trend' | 'diagnosis_stats' | 'feedback_health' | 'error_heatmap' | 'ab_distribution';
2327
+ /**
2328
+ * Query parameters for `GET /v1/dashboard/export`.
2329
+ */
2330
+ interface DashboardExportParams {
2331
+ /**
2332
+ * The dataset to export. Required.
2333
+ *
2334
+ * Must be one of the six whitelisted values ({@link DashboardExportDataset}).
2335
+ * The backend returns HTTP 422 for unknown values.
2336
+ */
2337
+ dataset: DashboardExportDataset;
2338
+ /**
2339
+ * File format for the exported data.
2340
+ *
2341
+ * - `'csv'` — Comma-separated values (default when omitted).
2342
+ * - `'json'` — Raw JSON text (not parsed; returned as a string by the SDK).
2343
+ *
2344
+ * @default 'csv'
2345
+ */
2346
+ format?: 'csv' | 'json';
2347
+ /**
2348
+ * Filter results to a specific tenant.
2349
+ *
2350
+ * Only usable by API keys that carry the admin scope. The backend returns
2351
+ * HTTP 403 when this field is present but the caller lacks admin privileges,
2352
+ * and HTTP 400 when the value is not a valid UUID (it is normalized to
2353
+ * canonical form server-side).
2354
+ */
2355
+ target_tenant_id?: string;
2356
+ }
2357
+
2358
+ /**
2359
+ * @module services/dashboard
2360
+ * @description Dashboard Service — export analytics datasets.
2361
+ *
2362
+ * Wraps the Nexus Dashboard API:
2363
+ * - GET /v1/dashboard/export — download a dataset as raw CSV or JSON text
2364
+ *
2365
+ * The export endpoint is a file-download endpoint: the backend responds with
2366
+ * `Content-Disposition: attachment` and a raw file body (not a JSON envelope).
2367
+ * Accordingly, `export()` returns the raw response string rather than parsing
2368
+ * it into an object — callers receive exactly the bytes the server sent.
2369
+ *
2370
+ * ### WebSocket realtime subscriptions — deferred
2371
+ *
2372
+ * US-033b FU-3 scope is limited to the REST export method. A WebSocket
2373
+ * subscription helper (`subscribe()` / `DashboardSubscription`) was evaluated
2374
+ * for inclusion but is deferred to FU-4 (WS replay/catchup protocol decision).
2375
+ * Reasons:
2376
+ * 1. The WS message schema is not yet stabilised (FU-4 owns that contract).
2377
+ * 2. A proper WS abstraction requires a browser/Node `WebSocket` shim strategy
2378
+ * that is a non-trivial independent surface.
2379
+ * Track the WS helper in FU-4; this file should be extended there.
2380
+ *
2381
+ * @example
2382
+ * ```typescript
2383
+ * const nexus = new NexusClient({ apiKey: 'nx_test_abc123' });
2384
+ *
2385
+ * // Download quality distribution as CSV (default format)
2386
+ * const csv = await nexus.dashboard.export({ dataset: 'quality_distribution' });
2387
+ * // csv is a string like: "bucket,count\n0-1,12\n1-2,45\n..."
2388
+ *
2389
+ * // Download feedback trend as JSON
2390
+ * const json = await nexus.dashboard.export({
2391
+ * dataset: 'feedback_trend',
2392
+ * format: 'json',
2393
+ * });
2394
+ *
2395
+ * // Admin: export data scoped to a specific tenant
2396
+ * const tenantCsv = await nexus.dashboard.export({
2397
+ * dataset: 'error_heatmap',
2398
+ * format: 'csv',
2399
+ * target_tenant_id: 'tenant-abc',
2400
+ * });
2401
+ * ```
2402
+ */
2403
+
2404
+ /**
2405
+ * Service for exporting dashboard analytics datasets.
2406
+ *
2407
+ * Exposes `GET /v1/dashboard/export` as a typed method that returns the raw
2408
+ * file body (CSV or JSON text) as a string.
2409
+ */
2410
+ declare class DashboardService extends BaseService {
2411
+ /**
2412
+ * Export a dashboard dataset as raw file content.
2413
+ *
2414
+ * Sends `GET /dashboard/export` with the given query parameters and returns
2415
+ * the raw response body as a string. The string is exactly the file the
2416
+ * server would send for a browser download:
2417
+ * - `format: 'csv'` (default) → comma-separated text
2418
+ * - `format: 'json'` → JSON text (not parsed into an object)
2419
+ *
2420
+ * @param params - Dataset selection and format options.
2421
+ * @param options - Optional request options (e.g. AbortSignal).
2422
+ * @returns Raw file body string.
2423
+ *
2424
+ * @throws {ApiError} HTTP 401 — missing or invalid API key.
2425
+ * @throws {ApiError} HTTP 403 — `target_tenant_id` requires admin scope.
2426
+ * @throws {ApiError} HTTP 422 — `dataset` is not one of the six whitelisted values.
2427
+ */
2428
+ export(params: DashboardExportParams, options?: RequestOptions): Promise<string>;
2429
+ }
2430
+
2308
2431
  /**
2309
2432
  * @module client
2310
2433
  * @description Main entry point for the Nexus SDK.
@@ -2356,6 +2479,8 @@ declare class NexusClient {
2356
2479
  readonly feedback: FeedbackService;
2357
2480
  /** Error reporting — submit structured error reports (US-031). */
2358
2481
  readonly errors: ErrorService;
2482
+ /** Dashboard analytics — export datasets as CSV or JSON (US-033b FU-3). */
2483
+ readonly dashboard: DashboardService;
2359
2484
  /** @internal Shared HTTP transport. */
2360
2485
  private readonly http;
2361
2486
  /**
@@ -2505,6 +2630,25 @@ declare class ValidationError extends ApiError {
2505
2630
  /**
2506
2631
  * HTTP 404 -- the requested resource does not exist.
2507
2632
  */
2633
+ /**
2634
+ * Something between the caller and the Nexus API answered the request.
2635
+ *
2636
+ * Raised for two shapes that are otherwise indistinguishable from success:
2637
+ *
2638
+ * 1. a **3xx redirect** (auth edges such as Cloudflare Access bounce
2639
+ * unauthenticated calls to a login page), and
2640
+ * 2. a **2xx whose body is not JSON** on a call that expects JSON
2641
+ * (a captive portal or proxy returning its own HTML with status 200).
2642
+ *
2643
+ * Both used to resolve successfully with an HTML string as `data`, which a
2644
+ * caller's `result.results ?? []` turns into "zero results" — Kairos logged
2645
+ * "connected" for two months while Nexus recorded zero calls
2646
+ * (Kairos#66 / Aether#372). A wrong credential must fail loudly, not
2647
+ * quietly look like an empty database.
2648
+ */
2649
+ declare class UpstreamInterceptError extends ApiError {
2650
+ constructor(message: string, statusCode: number, response?: unknown);
2651
+ }
2508
2652
  declare class NotFoundError extends ApiError {
2509
2653
  constructor(message: string, response?: unknown);
2510
2654
  }
@@ -2676,4 +2820,4 @@ declare const apiKeyCreateSchema: z.ZodObject<{
2676
2820
  expires_in_days?: number | undefined;
2677
2821
  }>;
2678
2822
 
2679
- export { type Activity, type ActivityProcessingStatus, ActivityService, type ActivityStats, type ActivityStatusResponse, type ActivityStreamRequest, type ActivityStreamResponse, type ActivityType, ApiError, type ApiErrorDetail, type ApiKey, type ApiKeyCreate, type ApiKeyCreated, type ApiResponse, AuthenticationError, type CacheConfig, type CompoundId, ConfigurationError, type ContextDepth, type ContextDepthPreset, type ContextGraphEntity, type ContextLayer, type ContextRequest, type ContextRetrieveResponse, ContextService, type Conversation, type ConversationCreate, type ConversationList, type ConversationListParams, type ConversationMessage, ConversationService, type ConversationStatus, type ConversationSummary, DEFAULT_CONFIG, DEPTH_PRESETS, type EntityListParams, type EntityListResponse, type ErrorReportRequest, type ErrorReportResponse, ErrorService, type ErrorSeverity, type ErrorType, type ExtractionRequest, type ExtractionResult, type FeedbackItemRequest, type FeedbackListItem, type FeedbackListParams, type FeedbackListResponse, type FeedbackResponse, FeedbackService, type FeedbackSubmitRequest, type GraphPath, type GraphPathEntity, type GraphPathRelationship, type GraphQueryRequest, type GraphQueryResponse, type HealthResponse, type HealthStatus, InputValidationError, type JournalEntry, type JournalResponse, type KnowledgeEntity, type KnowledgeRelationship, KnowledgeService, type Memory, type MemoryCreate, type MemoryJournalParams, type MemoryList, type MemoryListParams, type MemorySearch, type MemorySearchResult, MemoryService, type MemoryType, type MemoryUpdate, type Message, type MessageCreate, type MessageList, type MessageListParams, type MessageRole, NetworkError, NexusClient, type NexusConfig, NexusError, NotFoundError, type OfflineConfig, OfflineQueue, type PaginatedResponse, type Pagination, type ProfileMemory, type QueuedRequest, RateLimitError, type RequestOptions, type ResolvedCacheConfig, type ResolvedConfig, type ResolvedRetryConfig, type RetryConfig, type SearchResult, type ServiceStatus, type SortOrder, type Tenant, type TenantQuotas, TenantService, type TenantTier, TimeoutError, type UsageStats, ValidationError, apiKeyCreateSchema, contextRequestSchema, conversationCreateSchema, extractionRequestSchema, graphQueryRequestSchema, memoryCreateSchema, memorySearchSchema, memoryUpdateSchema, messageCreateSchema, resolveConfig };
2823
+ export { type Activity, type ActivityProcessingStatus, ActivityService, type ActivityStats, type ActivityStatusResponse, type ActivityStreamRequest, type ActivityStreamResponse, type ActivityType, ApiError, type ApiErrorDetail, type ApiKey, type ApiKeyCreate, type ApiKeyCreated, type ApiResponse, AuthenticationError, type CacheConfig, type CompoundId, ConfigurationError, type ContextDepth, type ContextDepthPreset, type ContextGraphEntity, type ContextLayer, type ContextRequest, type ContextRetrieveResponse, ContextService, type Conversation, type ConversationCreate, type ConversationList, type ConversationListParams, type ConversationMessage, ConversationService, type ConversationStatus, type ConversationSummary, DEFAULT_CONFIG, DEPTH_PRESETS, type EntityListParams, type EntityListResponse, type ErrorReportRequest, type ErrorReportResponse, ErrorService, type ErrorSeverity, type ErrorType, type ExtractionRequest, type ExtractionResult, type FeedbackItemRequest, type FeedbackListItem, type FeedbackListParams, type FeedbackListResponse, type FeedbackResponse, FeedbackService, type FeedbackSubmitRequest, type GraphPath, type GraphPathEntity, type GraphPathRelationship, type GraphQueryRequest, type GraphQueryResponse, type HealthResponse, type HealthStatus, InputValidationError, type JournalEntry, type JournalResponse, type KnowledgeEntity, type KnowledgeRelationship, KnowledgeService, type Memory, type MemoryCreate, type MemoryJournalParams, type MemoryList, type MemoryListParams, type MemorySearch, type MemorySearchResult, MemoryService, type MemoryType, type MemoryUpdate, type Message, type MessageCreate, type MessageList, type MessageListParams, type MessageRole, NetworkError, NexusClient, type NexusConfig, NexusError, NotFoundError, type OfflineConfig, OfflineQueue, type PaginatedResponse, type Pagination, type ProfileMemory, type QueuedRequest, RateLimitError, type RequestOptions, type ResolvedCacheConfig, type ResolvedConfig, type ResolvedRetryConfig, type RetryConfig, type SearchResult, type ServiceStatus, type SortOrder, type Tenant, type TenantQuotas, TenantService, type TenantTier, TimeoutError, UpstreamInterceptError, type UsageStats, ValidationError, apiKeyCreateSchema, contextRequestSchema, conversationCreateSchema, extractionRequestSchema, graphQueryRequestSchema, memoryCreateSchema, memorySearchSchema, memoryUpdateSchema, messageCreateSchema, resolveConfig };
package/dist/index.d.ts CHANGED
@@ -360,25 +360,6 @@ declare class OfflineQueue {
360
360
  * every failure surfaces as a typed {@link NexusError} subclass.
361
361
  */
362
362
 
363
- /**
364
- * HTTP client that communicates with the Nexus API.
365
- *
366
- * All service-level modules (Memory, Conversation, Knowledge, Context)
367
- * delegate their network calls to a shared `HttpClient` instance, which
368
- * guarantees consistent authentication, timeout handling, and error
369
- * mapping across the entire SDK surface.
370
- *
371
- * @example
372
- * ```typescript
373
- * import { resolveConfig } from '../config';
374
- * import { HttpClient } from './client';
375
- *
376
- * const config = resolveConfig({ apiKey: 'nx_test_abc123' });
377
- * const http = new HttpClient(config);
378
- *
379
- * const memories = await http.get<Memory[]>('/memory/search', { query: 'hello' });
380
- * ```
381
- */
382
363
  declare class HttpClient {
383
364
  /** Underlying Axios instance. */
384
365
  private readonly axios;
@@ -457,6 +438,21 @@ declare class HttpClient {
457
438
  * @returns The parsed response body.
458
439
  */
459
440
  patch<T>(path: string, data?: unknown, signal?: AbortSignal): Promise<T>;
441
+ /**
442
+ * Send a GET request and return the raw response body as a string.
443
+ *
444
+ * Intended for file-download endpoints (e.g. `GET /dashboard/export`) that
445
+ * return `text/csv` or `application/json` as a raw file stream rather than a
446
+ * JSON-parsed object. The retry and auth interceptors still apply; the
447
+ * response cache is intentionally bypassed (export payloads are not cacheable
448
+ * at the SDK layer).
449
+ *
450
+ * @param path - URL path relative to the base URL (e.g. `/dashboard/export`).
451
+ * @param params - Optional query parameters.
452
+ * @param signal - Optional {@link AbortSignal} to cancel the request.
453
+ * @returns The raw response body as a string.
454
+ */
455
+ getText(path: string, params?: Record<string, unknown>, signal?: AbortSignal): Promise<string>;
460
456
  /**
461
457
  * Send a DELETE request.
462
458
  *
@@ -2305,6 +2301,133 @@ declare class ErrorService extends BaseService {
2305
2301
  submit(data: ErrorReportRequest, options?: RequestOptions): Promise<ErrorReportResponse>;
2306
2302
  }
2307
2303
 
2304
+ /**
2305
+ * @module types/dashboard
2306
+ * @description Dashboard Service type definitions.
2307
+ *
2308
+ * Mirrors the Nexus API dashboard export contract:
2309
+ * - GET /v1/dashboard/export — download a dataset as CSV or JSON
2310
+ *
2311
+ * The export endpoint returns raw file content (text/csv or application/json),
2312
+ * not a JSON-parsed object. The SDK therefore exposes these types only for
2313
+ * the _request_ side; the return value is `string`.
2314
+ */
2315
+ /**
2316
+ * The set of exportable dashboard datasets.
2317
+ *
2318
+ * Must stay in sync with the backend `DashboardExportDataset` Literal:
2319
+ * - `quality_distribution` — Quality score bucket distribution
2320
+ * - `feedback_trend` — Feedback rating trend over time
2321
+ * - `diagnosis_stats` — Diagnosis type statistics
2322
+ * - `feedback_health` — Overall feedback health metrics
2323
+ * - `error_heatmap` — Error frequency heatmap by endpoint / time
2324
+ * - `ab_distribution` — A/B experiment assignment distribution
2325
+ */
2326
+ type DashboardExportDataset = 'quality_distribution' | 'feedback_trend' | 'diagnosis_stats' | 'feedback_health' | 'error_heatmap' | 'ab_distribution';
2327
+ /**
2328
+ * Query parameters for `GET /v1/dashboard/export`.
2329
+ */
2330
+ interface DashboardExportParams {
2331
+ /**
2332
+ * The dataset to export. Required.
2333
+ *
2334
+ * Must be one of the six whitelisted values ({@link DashboardExportDataset}).
2335
+ * The backend returns HTTP 422 for unknown values.
2336
+ */
2337
+ dataset: DashboardExportDataset;
2338
+ /**
2339
+ * File format for the exported data.
2340
+ *
2341
+ * - `'csv'` — Comma-separated values (default when omitted).
2342
+ * - `'json'` — Raw JSON text (not parsed; returned as a string by the SDK).
2343
+ *
2344
+ * @default 'csv'
2345
+ */
2346
+ format?: 'csv' | 'json';
2347
+ /**
2348
+ * Filter results to a specific tenant.
2349
+ *
2350
+ * Only usable by API keys that carry the admin scope. The backend returns
2351
+ * HTTP 403 when this field is present but the caller lacks admin privileges,
2352
+ * and HTTP 400 when the value is not a valid UUID (it is normalized to
2353
+ * canonical form server-side).
2354
+ */
2355
+ target_tenant_id?: string;
2356
+ }
2357
+
2358
+ /**
2359
+ * @module services/dashboard
2360
+ * @description Dashboard Service — export analytics datasets.
2361
+ *
2362
+ * Wraps the Nexus Dashboard API:
2363
+ * - GET /v1/dashboard/export — download a dataset as raw CSV or JSON text
2364
+ *
2365
+ * The export endpoint is a file-download endpoint: the backend responds with
2366
+ * `Content-Disposition: attachment` and a raw file body (not a JSON envelope).
2367
+ * Accordingly, `export()` returns the raw response string rather than parsing
2368
+ * it into an object — callers receive exactly the bytes the server sent.
2369
+ *
2370
+ * ### WebSocket realtime subscriptions — deferred
2371
+ *
2372
+ * US-033b FU-3 scope is limited to the REST export method. A WebSocket
2373
+ * subscription helper (`subscribe()` / `DashboardSubscription`) was evaluated
2374
+ * for inclusion but is deferred to FU-4 (WS replay/catchup protocol decision).
2375
+ * Reasons:
2376
+ * 1. The WS message schema is not yet stabilised (FU-4 owns that contract).
2377
+ * 2. A proper WS abstraction requires a browser/Node `WebSocket` shim strategy
2378
+ * that is a non-trivial independent surface.
2379
+ * Track the WS helper in FU-4; this file should be extended there.
2380
+ *
2381
+ * @example
2382
+ * ```typescript
2383
+ * const nexus = new NexusClient({ apiKey: 'nx_test_abc123' });
2384
+ *
2385
+ * // Download quality distribution as CSV (default format)
2386
+ * const csv = await nexus.dashboard.export({ dataset: 'quality_distribution' });
2387
+ * // csv is a string like: "bucket,count\n0-1,12\n1-2,45\n..."
2388
+ *
2389
+ * // Download feedback trend as JSON
2390
+ * const json = await nexus.dashboard.export({
2391
+ * dataset: 'feedback_trend',
2392
+ * format: 'json',
2393
+ * });
2394
+ *
2395
+ * // Admin: export data scoped to a specific tenant
2396
+ * const tenantCsv = await nexus.dashboard.export({
2397
+ * dataset: 'error_heatmap',
2398
+ * format: 'csv',
2399
+ * target_tenant_id: 'tenant-abc',
2400
+ * });
2401
+ * ```
2402
+ */
2403
+
2404
+ /**
2405
+ * Service for exporting dashboard analytics datasets.
2406
+ *
2407
+ * Exposes `GET /v1/dashboard/export` as a typed method that returns the raw
2408
+ * file body (CSV or JSON text) as a string.
2409
+ */
2410
+ declare class DashboardService extends BaseService {
2411
+ /**
2412
+ * Export a dashboard dataset as raw file content.
2413
+ *
2414
+ * Sends `GET /dashboard/export` with the given query parameters and returns
2415
+ * the raw response body as a string. The string is exactly the file the
2416
+ * server would send for a browser download:
2417
+ * - `format: 'csv'` (default) → comma-separated text
2418
+ * - `format: 'json'` → JSON text (not parsed into an object)
2419
+ *
2420
+ * @param params - Dataset selection and format options.
2421
+ * @param options - Optional request options (e.g. AbortSignal).
2422
+ * @returns Raw file body string.
2423
+ *
2424
+ * @throws {ApiError} HTTP 401 — missing or invalid API key.
2425
+ * @throws {ApiError} HTTP 403 — `target_tenant_id` requires admin scope.
2426
+ * @throws {ApiError} HTTP 422 — `dataset` is not one of the six whitelisted values.
2427
+ */
2428
+ export(params: DashboardExportParams, options?: RequestOptions): Promise<string>;
2429
+ }
2430
+
2308
2431
  /**
2309
2432
  * @module client
2310
2433
  * @description Main entry point for the Nexus SDK.
@@ -2356,6 +2479,8 @@ declare class NexusClient {
2356
2479
  readonly feedback: FeedbackService;
2357
2480
  /** Error reporting — submit structured error reports (US-031). */
2358
2481
  readonly errors: ErrorService;
2482
+ /** Dashboard analytics — export datasets as CSV or JSON (US-033b FU-3). */
2483
+ readonly dashboard: DashboardService;
2359
2484
  /** @internal Shared HTTP transport. */
2360
2485
  private readonly http;
2361
2486
  /**
@@ -2505,6 +2630,25 @@ declare class ValidationError extends ApiError {
2505
2630
  /**
2506
2631
  * HTTP 404 -- the requested resource does not exist.
2507
2632
  */
2633
+ /**
2634
+ * Something between the caller and the Nexus API answered the request.
2635
+ *
2636
+ * Raised for two shapes that are otherwise indistinguishable from success:
2637
+ *
2638
+ * 1. a **3xx redirect** (auth edges such as Cloudflare Access bounce
2639
+ * unauthenticated calls to a login page), and
2640
+ * 2. a **2xx whose body is not JSON** on a call that expects JSON
2641
+ * (a captive portal or proxy returning its own HTML with status 200).
2642
+ *
2643
+ * Both used to resolve successfully with an HTML string as `data`, which a
2644
+ * caller's `result.results ?? []` turns into "zero results" — Kairos logged
2645
+ * "connected" for two months while Nexus recorded zero calls
2646
+ * (Kairos#66 / Aether#372). A wrong credential must fail loudly, not
2647
+ * quietly look like an empty database.
2648
+ */
2649
+ declare class UpstreamInterceptError extends ApiError {
2650
+ constructor(message: string, statusCode: number, response?: unknown);
2651
+ }
2508
2652
  declare class NotFoundError extends ApiError {
2509
2653
  constructor(message: string, response?: unknown);
2510
2654
  }
@@ -2676,4 +2820,4 @@ declare const apiKeyCreateSchema: z.ZodObject<{
2676
2820
  expires_in_days?: number | undefined;
2677
2821
  }>;
2678
2822
 
2679
- export { type Activity, type ActivityProcessingStatus, ActivityService, type ActivityStats, type ActivityStatusResponse, type ActivityStreamRequest, type ActivityStreamResponse, type ActivityType, ApiError, type ApiErrorDetail, type ApiKey, type ApiKeyCreate, type ApiKeyCreated, type ApiResponse, AuthenticationError, type CacheConfig, type CompoundId, ConfigurationError, type ContextDepth, type ContextDepthPreset, type ContextGraphEntity, type ContextLayer, type ContextRequest, type ContextRetrieveResponse, ContextService, type Conversation, type ConversationCreate, type ConversationList, type ConversationListParams, type ConversationMessage, ConversationService, type ConversationStatus, type ConversationSummary, DEFAULT_CONFIG, DEPTH_PRESETS, type EntityListParams, type EntityListResponse, type ErrorReportRequest, type ErrorReportResponse, ErrorService, type ErrorSeverity, type ErrorType, type ExtractionRequest, type ExtractionResult, type FeedbackItemRequest, type FeedbackListItem, type FeedbackListParams, type FeedbackListResponse, type FeedbackResponse, FeedbackService, type FeedbackSubmitRequest, type GraphPath, type GraphPathEntity, type GraphPathRelationship, type GraphQueryRequest, type GraphQueryResponse, type HealthResponse, type HealthStatus, InputValidationError, type JournalEntry, type JournalResponse, type KnowledgeEntity, type KnowledgeRelationship, KnowledgeService, type Memory, type MemoryCreate, type MemoryJournalParams, type MemoryList, type MemoryListParams, type MemorySearch, type MemorySearchResult, MemoryService, type MemoryType, type MemoryUpdate, type Message, type MessageCreate, type MessageList, type MessageListParams, type MessageRole, NetworkError, NexusClient, type NexusConfig, NexusError, NotFoundError, type OfflineConfig, OfflineQueue, type PaginatedResponse, type Pagination, type ProfileMemory, type QueuedRequest, RateLimitError, type RequestOptions, type ResolvedCacheConfig, type ResolvedConfig, type ResolvedRetryConfig, type RetryConfig, type SearchResult, type ServiceStatus, type SortOrder, type Tenant, type TenantQuotas, TenantService, type TenantTier, TimeoutError, type UsageStats, ValidationError, apiKeyCreateSchema, contextRequestSchema, conversationCreateSchema, extractionRequestSchema, graphQueryRequestSchema, memoryCreateSchema, memorySearchSchema, memoryUpdateSchema, messageCreateSchema, resolveConfig };
2823
+ export { type Activity, type ActivityProcessingStatus, ActivityService, type ActivityStats, type ActivityStatusResponse, type ActivityStreamRequest, type ActivityStreamResponse, type ActivityType, ApiError, type ApiErrorDetail, type ApiKey, type ApiKeyCreate, type ApiKeyCreated, type ApiResponse, AuthenticationError, type CacheConfig, type CompoundId, ConfigurationError, type ContextDepth, type ContextDepthPreset, type ContextGraphEntity, type ContextLayer, type ContextRequest, type ContextRetrieveResponse, ContextService, type Conversation, type ConversationCreate, type ConversationList, type ConversationListParams, type ConversationMessage, ConversationService, type ConversationStatus, type ConversationSummary, DEFAULT_CONFIG, DEPTH_PRESETS, type EntityListParams, type EntityListResponse, type ErrorReportRequest, type ErrorReportResponse, ErrorService, type ErrorSeverity, type ErrorType, type ExtractionRequest, type ExtractionResult, type FeedbackItemRequest, type FeedbackListItem, type FeedbackListParams, type FeedbackListResponse, type FeedbackResponse, FeedbackService, type FeedbackSubmitRequest, type GraphPath, type GraphPathEntity, type GraphPathRelationship, type GraphQueryRequest, type GraphQueryResponse, type HealthResponse, type HealthStatus, InputValidationError, type JournalEntry, type JournalResponse, type KnowledgeEntity, type KnowledgeRelationship, KnowledgeService, type Memory, type MemoryCreate, type MemoryJournalParams, type MemoryList, type MemoryListParams, type MemorySearch, type MemorySearchResult, MemoryService, type MemoryType, type MemoryUpdate, type Message, type MessageCreate, type MessageList, type MessageListParams, type MessageRole, NetworkError, NexusClient, type NexusConfig, NexusError, NotFoundError, type OfflineConfig, OfflineQueue, type PaginatedResponse, type Pagination, type ProfileMemory, type QueuedRequest, RateLimitError, type RequestOptions, type ResolvedCacheConfig, type ResolvedConfig, type ResolvedRetryConfig, type RetryConfig, type SearchResult, type ServiceStatus, type SortOrder, type Tenant, type TenantQuotas, TenantService, type TenantTier, TimeoutError, UpstreamInterceptError, type UsageStats, ValidationError, apiKeyCreateSchema, contextRequestSchema, conversationCreateSchema, extractionRequestSchema, graphQueryRequestSchema, memoryCreateSchema, memorySearchSchema, memoryUpdateSchema, messageCreateSchema, resolveConfig };
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ __export(index_exports, {
51
51
  RateLimitError: () => RateLimitError,
52
52
  TenantService: () => TenantService,
53
53
  TimeoutError: () => TimeoutError,
54
+ UpstreamInterceptError: () => UpstreamInterceptError,
54
55
  ValidationError: () => ValidationError,
55
56
  apiKeyCreateSchema: () => apiKeyCreateSchema,
56
57
  contextRequestSchema: () => contextRequestSchema,
@@ -230,6 +231,12 @@ var ValidationError = class extends ApiError {
230
231
  this.details = details;
231
232
  }
232
233
  };
234
+ var UpstreamInterceptError = class extends ApiError {
235
+ constructor(message, statusCode, response) {
236
+ super(message, statusCode, response, "NEXUS_UPSTREAM_INTERCEPT");
237
+ this.name = "UpstreamInterceptError";
238
+ }
239
+ };
233
240
  var NotFoundError = class extends ApiError {
234
241
  constructor(message, response) {
235
242
  super(message, 404, response, "NEXUS_NOT_FOUND_ERROR");
@@ -641,6 +648,35 @@ var OfflineQueue = class {
641
648
  };
642
649
 
643
650
  // src/http/client.ts
651
+ function redirectHost(location) {
652
+ if (typeof location !== "string" || location === "") return "an unknown host";
653
+ try {
654
+ return new URL(location).host;
655
+ } catch {
656
+ return "an unparseable location";
657
+ }
658
+ }
659
+ function upstreamRedirectError(response, url) {
660
+ const headers = response.headers ?? {};
661
+ const host = redirectHost(headers.location ?? headers.Location);
662
+ return new UpstreamInterceptError(
663
+ `Request to ${url ?? "the API"} was redirected (HTTP ${response.status}) to ${host} instead of being answered by Nexus. This is typically an expired or missing edge credential (e.g. a Cloudflare Access service token) \u2014 the API itself was never reached.`,
664
+ response.status,
665
+ response.data
666
+ );
667
+ }
668
+ function assertNotIntercepted(response) {
669
+ if (response.config?.responseType === "text") return;
670
+ const headers = response.headers ?? {};
671
+ const raw = headers["content-type"] ?? headers["Content-Type"];
672
+ if (typeof raw !== "string" || raw === "") return;
673
+ if (raw.toLowerCase().includes("json")) return;
674
+ throw new UpstreamInterceptError(
675
+ `Request to ${response.config?.url ?? "the API"} returned HTTP ${response.status} with content-type "${raw}" where JSON was expected. Something between this client and Nexus answered the request (auth edge, proxy, or captive portal); treating it as data would look like an empty result.`,
676
+ response.status,
677
+ response.data
678
+ );
679
+ }
644
680
  var HttpClient = class {
645
681
  /**
646
682
  * Create a new HTTP client.
@@ -658,7 +694,14 @@ var HttpClient = class {
658
694
  }
659
695
  this.axios = import_axios.default.create({
660
696
  baseURL: config.baseUrl,
661
- timeout: config.timeout
697
+ timeout: config.timeout,
698
+ // Never follow redirects (Kairos#66 / Aether#372). An auth edge such as
699
+ // Cloudflare Access answers an unauthenticated call with 302 → its own
700
+ // login page; following it yields a 200 with HTML, which is
701
+ // indistinguishable from an empty result to the caller. With
702
+ // maxRedirects: 0 the 3xx fails axios' status validation and reaches the
703
+ // error interceptor, which turns it into an UpstreamInterceptError.
704
+ maxRedirects: 0
662
705
  });
663
706
  this.setupRequestInterceptor();
664
707
  this.setupResponseInterceptor();
@@ -794,6 +837,30 @@ var HttpClient = class {
794
837
  this.cache.invalidate(path.split("/").filter(Boolean)[0] ?? path);
795
838
  return result;
796
839
  }
840
+ /**
841
+ * Send a GET request and return the raw response body as a string.
842
+ *
843
+ * Intended for file-download endpoints (e.g. `GET /dashboard/export`) that
844
+ * return `text/csv` or `application/json` as a raw file stream rather than a
845
+ * JSON-parsed object. The retry and auth interceptors still apply; the
846
+ * response cache is intentionally bypassed (export payloads are not cacheable
847
+ * at the SDK layer).
848
+ *
849
+ * @param path - URL path relative to the base URL (e.g. `/dashboard/export`).
850
+ * @param params - Optional query parameters.
851
+ * @param signal - Optional {@link AbortSignal} to cancel the request.
852
+ * @returns The raw response body as a string.
853
+ */
854
+ async getText(path, params, signal) {
855
+ return this.retry.execute(async () => {
856
+ const response = await this.axios.get(path, {
857
+ params,
858
+ signal,
859
+ responseType: "text"
860
+ });
861
+ return response.data;
862
+ });
863
+ }
797
864
  /**
798
865
  * Send a DELETE request.
799
866
  *
@@ -849,8 +916,11 @@ var HttpClient = class {
849
916
  */
850
917
  setupResponseInterceptor() {
851
918
  this.axios.interceptors.response.use(
852
- // Success handler -- pass through
853
- (response) => response,
919
+ // Success handler -- pass through, except for a 2xx that is not JSON.
920
+ (response) => {
921
+ assertNotIntercepted(response);
922
+ return response;
923
+ },
854
924
  // Error handler -- normalise into NexusError hierarchy
855
925
  (error) => {
856
926
  if (import_axios.default.isCancel(error)) {
@@ -864,6 +934,11 @@ var HttpClient = class {
864
934
  )
865
935
  );
866
936
  }
937
+ if (error.response && error.response.status >= 300 && error.response.status < 400) {
938
+ return Promise.reject(
939
+ upstreamRedirectError(error.response, error.config?.url)
940
+ );
941
+ }
867
942
  if (error.response) {
868
943
  const apiError = ApiError.fromResponse(error.response);
869
944
  const reqUrl = error.config?.url ?? "";
@@ -1410,6 +1485,35 @@ var ErrorService = class extends BaseService {
1410
1485
  }
1411
1486
  };
1412
1487
 
1488
+ // src/services/dashboard.ts
1489
+ var DashboardService = class extends BaseService {
1490
+ /**
1491
+ * Export a dashboard dataset as raw file content.
1492
+ *
1493
+ * Sends `GET /dashboard/export` with the given query parameters and returns
1494
+ * the raw response body as a string. The string is exactly the file the
1495
+ * server would send for a browser download:
1496
+ * - `format: 'csv'` (default) → comma-separated text
1497
+ * - `format: 'json'` → JSON text (not parsed into an object)
1498
+ *
1499
+ * @param params - Dataset selection and format options.
1500
+ * @param options - Optional request options (e.g. AbortSignal).
1501
+ * @returns Raw file body string.
1502
+ *
1503
+ * @throws {ApiError} HTTP 401 — missing or invalid API key.
1504
+ * @throws {ApiError} HTTP 403 — `target_tenant_id` requires admin scope.
1505
+ * @throws {ApiError} HTTP 422 — `dataset` is not one of the six whitelisted values.
1506
+ */
1507
+ async export(params, options) {
1508
+ const query = { dataset: params.dataset };
1509
+ if (params.format !== void 0) query["format"] = params.format;
1510
+ if (params.target_tenant_id !== void 0) {
1511
+ query["target_tenant_id"] = params.target_tenant_id;
1512
+ }
1513
+ return this.http.getText("/dashboard/export", query, options?.signal);
1514
+ }
1515
+ };
1516
+
1413
1517
  // src/client.ts
1414
1518
  var NexusClient = class {
1415
1519
  /**
@@ -1431,6 +1535,7 @@ var NexusClient = class {
1431
1535
  this.tenants = new TenantService(this.http);
1432
1536
  this.feedback = new FeedbackService(this.http);
1433
1537
  this.errors = new ErrorService(this.http);
1538
+ this.dashboard = new DashboardService(this.http);
1434
1539
  if (resolved.autoErrorReport) {
1435
1540
  this.http.onApiError = (statusCode, method, url, detail) => {
1436
1541
  this.errors.submit({
@@ -1490,6 +1595,7 @@ var apiKeyCreateSchema = import_zod5.z.object({
1490
1595
  RateLimitError,
1491
1596
  TenantService,
1492
1597
  TimeoutError,
1598
+ UpstreamInterceptError,
1493
1599
  ValidationError,
1494
1600
  apiKeyCreateSchema,
1495
1601
  contextRequestSchema,