Skip to content

seclai/seclai-javascript

Repository files navigation

Seclai JavaScript SDK

The official JavaScript/TypeScript SDK for the Seclai API. Provides full typed coverage of all API endpoints, file uploads, SSE streaming, polling helpers, and automatic pagination.

Works in Node.js 18+, Deno, Bun, Cloudflare Workers, and any runtime with a fetch implementation.

Install

npm install @seclai/sdk

Quick start

import { Seclai } from "@seclai/sdk";

const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });

// List all sources
const sources = await client.listSources();

// Run an agent and stream the result
const result = await client.runStreamingAgentAndWait(
  "agent_id",
  { input: "Summarize the latest uploads", metadata: {} },
  { timeoutMs: 120_000 },
);
console.log(result);

Configuration

Option Environment variable Default
apiKey SECLAI_API_KEY
accessToken
profile SECLAI_PROFILE "default"
configDir SECLAI_CONFIG_DIR ~/.seclai
autoRefresh true
accountId
baseUrl SECLAI_API_URL https://api.seclai.com
apiKeyHeader x-api-key
defaultHeaders {}
fetch globalThis.fetch

Authentication

Credentials are resolved via a chain (first match wins):

  1. Explicit apiKey option
  2. Explicit accessToken option (string or () => string | Promise<string>)
  3. SECLAI_API_KEY environment variable
  4. SSO — cached tokens from ~/.seclai/sso/cache/ (requires a prior seclai auth login)
// API key
const client = new Seclai({ apiKey: "sk-..." });
// Static bearer token
const client = new Seclai({ accessToken: "eyJhbGciOi..." });
// Dynamic bearer token provider (called per request)
const client = new Seclai({
  accessToken: async () => fetchTokenFromVault(),
});
// SSO profile (uses cached tokens, auto-refreshes)
const client = new Seclai({ profile: "my-profile" });
// Environment variable (no options needed)
// export SECLAI_API_KEY="sk-..."
const client = new Seclai();

SSO authentication

SSO is the default fallback when no explicit credentials are provided. The SDK includes built-in production SSO defaults, so seclai configure sso is not required. You only need to log in once to populate the token cache:

npx @seclai/cli auth login    # authenticate via browser — no prior setup needed

To customize SSO settings (e.g. for a staging environment), use seclai configure sso or set environment variables:

Variable Description Default
SECLAI_SSO_DOMAIN Cognito domain auth.seclai.com
SECLAI_SSO_CLIENT_ID Cognito app client ID 4bgf8v9qmc5puivbaqon9n5lmr
SECLAI_SSO_REGION AWS region us-west-2

API documentation

Online API documentation (latest):

https://seclai.github.io/seclai-javascript/latest/

Resources

Agents

// CRUD
const agents = await client.listAgents({ page: 1, limit: 20 });
const agent = await client.createAgent({ name: "My Agent", description: "..." });
const fetched = await client.getAgent("agent_id");
const updated = await client.updateAgent("agent_id", { name: "Renamed" });
await client.deleteAgent("agent_id");

// Definition (step workflow)
const def = await client.getAgentDefinition("agent_id");
await client.updateAgentDefinition("agent_id", { steps: [...], change_id: def.change_id });

// Export / import an agent
const exported = await client.exportAgent("agent_id");

// Validate the payload first to surface unresolved entity refs in this account
const preview = await client.previewImportAgent({ agent_definition: exported });
const unresolved = (preview.unresolved_refs ?? []) as Array<{ ref_id: string }>;
const entity_remap = Object.fromEntries(
  unresolved.map((ref) => [ref.ref_id, /* picked target uuid */ ""]),
);

// Commit — `entity_remap` substitutes workflow refs before save
const imported = await client.createAgent({
  name: "Imported",
  trigger_type: "dynamic_input",
  agent_definition: exported,
  entity_remap,
});
// `imported.import_warnings` lists any items that couldn't be applied.

Agent runs

// Start a run
const run = await client.runAgent("agent_id", { input: "Hello" });

// List & search runs
const runs = await client.listAgentRuns("agent_id", { status: "completed" });
const search = await client.searchAgentRuns({ agent_id: "...", status: ["completed"] });

// Fetch run details (optionally with step outputs)
const detail = await client.getAgentRun("run_id", { includeStepOutputs: true });

// Cancel or delete
await client.cancelAgentRun("run_id");
await client.deleteAgentRun("run_id");

Streaming

The SDK provides two streaming patterns over the SSE /runs/stream endpoint:

Block until done — returns the final done payload or throws on timeout:

const result = await client.runStreamingAgentAndWait(
  "agent_id",
  { input: "Hello", metadata: {} },
  { timeoutMs: 60_000 },
);

Async iterator — yields every SSE event as { event, data }:

for await (const event of client.runStreamingAgent(
  "agent_id",
  { input: "Hello" },
  { timeoutMs: 120_000 },
)) {
  console.log(event.event, event.data);
  if (event.event === "done") break;
}

Polling

For environments where SSE is not practical, poll for a completed run:

const result = await client.runAgentAndPoll(
  "agent_id",
  { input: "Hello" },
  { pollIntervalMs: 2_000, timeoutMs: 120_000 },
);

Agent input uploads

// Discover which files (if any) the agent expects before staging uploads
const refs = await client.getAgentAttachmentReferences("agent_id");
if (refs.requires_uploads) {
  // refs.agent lists the exact_names / indexes_max / patterns a run batch must satisfy
}

const upload = await client.uploadAgentInput("agent_id", {
  file: new Uint8Array([...]),
  fileName: "input.pdf",
});
const status = await client.getAgentInputUploadStatus("agent_id", upload.upload_id);

Agent run attachments

// Download a file emitted by a step in an agent run. The attachment_id is the
// URL-safe-base64 storage_key surfaced in run output manifests / webhooks.
const resp = await client.downloadAgentRunAttachment("run_id", "attachment_id");
const blob = await resp.blob(); // raw Response — stream or save the bytes

Agent AI assistant

const steps = await client.generateAgentSteps("agent_id", { user_input: "Build a RAG pipeline" });
const config = await client.generateStepConfig("agent_id", { step_type: "llm", user_input: "..." });

// Conversation history
const history = await client.getAgentAiConversationHistory("agent_id");
await client.markAgentAiSuggestion("agent_id", "conversation_id", { accepted: true });

Agent evaluations

const criteria = await client.listEvaluationCriteria("agent_id");
const created = await client.createEvaluationCriteria("agent_id", { name: "Accuracy", ... });
const detail = await client.getEvaluationCriteria("criteria_id");
await client.updateEvaluationCriteria("criteria_id", { ... });
await client.deleteEvaluationCriteria("criteria_id");

// Test a draft
await client.testDraftEvaluation("agent_id", { criteria: { ... }, run_id: "..." });

// Results by criteria
const results = await client.listEvaluationResults("criteria_id");
const summary = await client.getEvaluationCriteriaSummary("criteria_id");
await client.createEvaluationResult("criteria_id", { ... });

// Results by run
const runResults = await client.listRunEvaluationResults("agent_id", "run_id");

// Non-manual evaluation summary
const nonManual = await client.getNonManualEvaluationSummary("agent_id");

// Compatible runs for a criteria
const compatible = await client.listCompatibleRuns("criteria_id");

Knowledge bases

const kbs = await client.listKnowledgeBases();
const kb = await client.createKnowledgeBase({ name: "Docs KB" });
const fetched = await client.getKnowledgeBase("kb_id");
await client.updateKnowledgeBase("kb_id", { name: "Renamed KB" });
await client.deleteKnowledgeBase("kb_id");

Memory banks

const banks = await client.listMemoryBanks();
const bank = await client.createMemoryBank({ name: "Chat Memory", type: "conversation" });
const fetched = await client.getMemoryBank("mb_id");
await client.updateMemoryBank("mb_id", { name: "Renamed" });
await client.deleteMemoryBank("mb_id");

// Stats & compaction
const stats = await client.getMemoryBankStats("mb_id");
await client.compactMemoryBank("mb_id");

// Test compaction
const test = await client.testMemoryBankCompaction("mb_id", { ... });
const standalone = await client.testCompactionPromptStandalone({ ... });

// Templates & agents
const templates = await client.listMemoryBankTemplates();
const agents = await client.getAgentsUsingMemoryBank("mb_id");

// AI assistant
const suggestion = await client.generateMemoryBankConfig({ user_input: "..." });
const history = await client.getMemoryBankAiLastConversation();
await client.acceptMemoryBankAiSuggestion("conv_id", { ... });

// Source management
await client.deleteMemoryBankSource("mb_id");

Sources

const sources = await client.listSources({ page: 1, limit: 20, order: "asc" });
const source = await client.createSource({ name: "My Source", ... });
const fetched = await client.getSource("source_id");
await client.updateSource("source_id", { name: "Renamed" });
await client.deleteSource("source_id");

File uploads

Upload a file to a source (max 200 MiB). The SDK infers MIME type from the file extension when mimeType is not provided.

import { readFile } from "node:fs/promises";

await client.uploadFileToSource("source_id", {
  file: await readFile("document.pdf"),
  fileName: "document.pdf",
  title: "Q4 Report",
  metadata: { department: "finance" },
});

Upload inline text:

await client.uploadInlineTextToSource("source_id", {
  text: "Hello, world!",
  title: "Greeting",
});

Replace a content version with a new file:

await client.uploadFileToContent("content_version_id", {
  file: await readFile("updated.pdf"),
  fileName: "updated.pdf",
  mimeType: "application/pdf",
});

Source exports

const exports = await client.listSourceExports("source_id");
const exp = await client.createSourceExport("source_id", { format: "json" });
const status = await client.getSourceExport("source_id", exp.id);
const estimate = await client.estimateSourceExport("source_id", {});
const response = await client.downloadSourceExport("source_id", exp.id);
await client.deleteSourceExport("source_id", exp.id);
await client.cancelSourceExport("source_id", exp.id);

Source embedding migrations

const migration = await client.getSourceEmbeddingMigration("source_id");
await client.startSourceEmbeddingMigration("source_id", { target_model: "..." });
await client.cancelSourceEmbeddingMigration("source_id");

Content

const detail = await client.getContentDetail("content_id", { start: 0, end: 1000 });
const embeddings = await client.listContentEmbeddings("content_id");
await client.deleteContent("content_id");

// Replace content with inline text
await client.replaceContentWithInlineText("content_id", { text: "Updated text", title: "Updated" });

// Upload a replacement file
await client.uploadFileToContent("content_id", {
  file: await readFile("updated.pdf"),
  fileName: "updated.pdf",
});

Solutions

const solutions = await client.listSolutions();
const sol = await client.createSolution({ name: "My Solution" });
const fetched = await client.getSolution("solution_id");
await client.updateSolution("solution_id", { name: "Renamed" });
await client.deleteSolution("solution_id");

// Link / unlink resources
await client.linkAgentsToSolution("solution_id", { ids: ["agent_id"] });
await client.unlinkAgentsFromSolution("solution_id", { ids: ["agent_id"] });
await client.linkKnowledgeBasesToSolution("solution_id", { ids: ["kb_id"] });
await client.unlinkKnowledgeBasesFromSolution("solution_id", { ids: ["kb_id"] });
await client.linkSourceConnectionsToSolution("solution_id", { ids: ["source_id"] });
await client.unlinkSourceConnectionsFromSolution("solution_id", { ids: ["source_id"] });

// AI assistant
const plan = await client.generateSolutionAiPlan("solution_id", { user_input: "Set up a RAG pipeline" });
await client.acceptSolutionAiPlan("solution_id", "conversation_id", {});
await client.declineSolutionAiPlan("solution_id", "conversation_id");

// AI-generated knowledge base / source within the solution
await client.generateSolutionAiKnowledgeBase("solution_id", { user_input: "..." });
await client.generateSolutionAiSource("solution_id", { user_input: "..." });

// Conversations
const convs = await client.listSolutionConversations("solution_id");
await client.addSolutionConversationTurn("solution_id", { user_input: "..." });
await client.markSolutionConversationTurn("solution_id", "conversation_id", { ... });

Governance AI

const plan = await client.generateGovernanceAiPlan({ user_input: "Add a toxicity policy" });
const convs = await client.listGovernanceAiConversations();
await client.acceptGovernanceAiPlan("conversation_id");
await client.declineGovernanceAiPlan("conversation_id");

Alerts

const alerts = await client.listAlerts({ status: "active" });
const alert = await client.getAlert("alert_id");
await client.changeAlertStatus("alert_id", { status: "resolved" });
await client.addAlertComment("alert_id", { text: "Investigating" });

// Subscriptions
await client.subscribeToAlert("alert_id");
await client.unsubscribeFromAlert("alert_id");

// Alert configs
const configs = await client.listAlertConfigs();
await client.createAlertConfig({ ... });
await client.getAlertConfig("config_id");
await client.updateAlertConfig("config_id", { ... });
await client.deleteAlertConfig("config_id");

// Organization preferences
const prefs = await client.listOrganizationAlertPreferences();
await client.updateOrganizationAlertPreference("org_id", "alert_type", { ... });

Models

const alerts = await client.listModelAlerts();
await client.markModelAlertRead("alert_id");
await client.markAllModelAlertsRead();
const unread = await client.getUnreadModelAlertCount();
const recs = await client.getModelRecommendations("model_id");

// Model playground experiments
const experiment = await client.createExperiment({ model_ids: ["model_id"], prompt: "..." });
const experiments = await client.listExperiments();
const detail = await client.getExperiment("experiment_id");
await client.cancelExperiment("experiment_id");
await client.deleteExperiment("experiment_id"); // soft-delete, preserves audit history

Search

const results = await client.search({ query: "quarterly report" });
const filtered = await client.search({ query: "my agent", entityType: "agent", limit: 5 });

Top-level AI assistant

// Generate plans for different resource types
const kb = await client.aiAssistantKnowledgeBase({ user_input: "Create a docs KB" });
const src = await client.aiAssistantSource({ user_input: "Add a web source" });
const sol = await client.aiAssistantSolution({ user_input: "Set up monitoring" });
const mb = await client.aiAssistantMemoryBank({ user_input: "Create a chat memory" });

// Accept or decline the generated plan
await client.acceptAiAssistantPlan("conversation_id", { confirm_deletions: true });
await client.declineAiAssistantPlan("conversation_id");

// Memory bank conversation history
const history = await client.getAiAssistantMemoryBankHistory();
await client.acceptAiMemoryBankSuggestion("conversation_id", { ... });

// Feedback
await client.submitAiFeedback({ ... });

Pagination helper

Automatically iterate through all pages:

for await (const source of client.paginate(
  (opts) => client.listSources(opts),
  { limit: 50 },
)) {
  console.log(source.name);
}

Error handling

All errors extend SeclaiError:

import {
  SeclaiError,
  SeclaiConfigurationError,
  SeclaiAPIStatusError,
  SeclaiAPIValidationError,
  SeclaiStreamingError,
} from "@seclai/sdk";

try {
  await client.getAgent("bad_id");
} catch (err) {
  if (err instanceof SeclaiAPIValidationError) {
    console.error("Validation:", err.validationError);
  } else if (err instanceof SeclaiAPIStatusError) {
    console.error(`HTTP ${err.statusCode}:`, err.responseText);
  } else if (err instanceof SeclaiStreamingError) {
    console.error("Stream failed for run:", err.runId);
  }
}

Cancellation (AbortSignal)

All low-level methods support an AbortSignal for request cancellation:

const controller = new AbortController();

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5_000);

const data = await client.request("GET", "/agents", {
  signal: controller.signal,
});

Low-level access

For endpoints not yet covered by a convenience method, use request or requestRaw:

// JSON request/response
const data = await client.request("POST", "/custom/endpoint", {
  json: { key: "value" },
  query: { filter: "active" },
});

// Raw Response (e.g. binary downloads)
const response = await client.requestRaw("GET", "/files/download/123");
const blob = await response.blob();

Development

Install dependencies

npm install

Type checking

npm run typecheck

Build

npm run build

This also regenerates src/openapi.ts from openapi/seclai.openapi.json.

Run tests

npm test

Generate docs

Generate HTML docs into build/docs/:

npm run docs