@seclai/sdk 1.0.7 → 1.1.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Seclai JavaScript SDK
2
2
 
3
- This is the official Seclai JavaScript SDK with TypeScript typings.
3
+ The official JavaScript/TypeScript SDK for the [Seclai](https://seclai.com) API. Provides full typed coverage of all API endpoints, file uploads, SSE streaming, polling helpers, and automatic pagination.
4
+
5
+ Works in Node.js 18+, Deno, Bun, Cloudflare Workers, and any runtime with a `fetch` implementation.
4
6
 
5
7
  ## Install
6
8
 
@@ -8,138 +10,467 @@ This is the official Seclai JavaScript SDK with TypeScript typings.
8
10
  npm install @seclai/sdk
9
11
  ```
10
12
 
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { Seclai } from "@seclai/sdk";
17
+
18
+ const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
19
+
20
+ // List all sources
21
+ const sources = await client.listSources();
22
+
23
+ // Run an agent and stream the result
24
+ const result = await client.runStreamingAgentAndWait(
25
+ "agent_id",
26
+ { input: "Summarize the latest uploads", metadata: {} },
27
+ { timeoutMs: 120_000 },
28
+ );
29
+ console.log(result);
30
+ ```
31
+
32
+ ## Configuration
33
+
34
+ | Option | Environment variable | Default |
35
+ | --- | --- | --- |
36
+ | `apiKey` | `SECLAI_API_KEY` | *required* |
37
+ | `baseUrl` | `SECLAI_API_URL` | `https://api.seclai.com` |
38
+ | `apiKeyHeader` | — | `x-api-key` |
39
+ | `defaultHeaders` | — | `{}` |
40
+ | `fetch` | — | `globalThis.fetch` |
41
+
42
+ ```ts
43
+ const client = new Seclai({
44
+ apiKey: "sk-...",
45
+ baseUrl: "https://staging-api.seclai.com",
46
+ defaultHeaders: { "X-Custom": "value" },
47
+ });
48
+ ```
49
+
11
50
  ## API documentation
12
51
 
13
52
  Online API documentation (latest):
14
53
 
15
- https://seclai.github.io/seclai-javascript/1.0.7/
54
+ https://seclai.github.io/seclai-javascript/1.1.0/
55
+
56
+ ## Resources
16
57
 
17
- ## Usage
58
+ ### Agents
18
59
 
19
60
  ```ts
20
- import { Seclai } from "@seclai/sdk";
61
+ // CRUD
62
+ const agents = await client.listAgents({ page: 1, limit: 20 });
63
+ const agent = await client.createAgent({ name: "My Agent", description: "..." });
64
+ const fetched = await client.getAgent("agent_id");
65
+ const updated = await client.updateAgent("agent_id", { name: "Renamed" });
66
+ await client.deleteAgent("agent_id");
67
+
68
+ // Definition (step workflow)
69
+ const def = await client.getAgentDefinition("agent_id");
70
+ await client.updateAgentDefinition("agent_id", { steps: [...], change_id: def.change_id });
71
+ ```
21
72
 
22
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
73
+ ### Agent runs
23
74
 
24
- const sources = await client.listSources();
25
- console.log(sources.pagination, sources.data);
75
+ ```ts
76
+ // Start a run
77
+ const run = await client.runAgent("agent_id", { input: "Hello" });
78
+
79
+ // List & search runs
80
+ const runs = await client.listAgentRuns("agent_id", { status: "completed" });
81
+ const search = await client.searchAgentRuns({ agent_id: "...", status: ["completed"] });
82
+
83
+ // Fetch run details (optionally with step outputs)
84
+ const detail = await client.getAgentRun("run_id", { includeStepOutputs: true });
85
+
86
+ // Cancel or delete
87
+ await client.cancelAgentRun("run_id");
88
+ await client.deleteAgentRun("run_id");
26
89
  ```
27
90
 
28
- ### Run an agent with SSE streaming (wait for final result)
91
+ ### Streaming
29
92
 
30
- Use the SSE streaming endpoint and block until the final `done` event is received.
93
+ The SDK provides two streaming patterns over the SSE `/runs/stream` endpoint:
31
94
 
32
- If the stream ends before `done` or the timeout is reached, this method throws.
95
+ **Block until done** returns the final `done` payload or throws on timeout:
33
96
 
34
97
  ```ts
35
- import { Seclai } from "@seclai/sdk";
98
+ const result = await client.runStreamingAgentAndWait(
99
+ "agent_id",
100
+ { input: "Hello", metadata: {} },
101
+ { timeoutMs: 60_000 },
102
+ );
103
+ ```
36
104
 
37
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
105
+ **Async iterator** yields every SSE event as `{ event, data }`:
38
106
 
39
- const run = await client.runStreamingAgentAndWait(
107
+ ```ts
108
+ for await (const event of client.runStreamingAgent(
40
109
  "agent_id",
41
- {
42
- input: "Hello from streaming",
43
- metadata: { app: "My App" },
44
- },
45
- { timeoutMs: 60_000 }
110
+ { input: "Hello" },
111
+ { timeoutMs: 120_000 },
112
+ )) {
113
+ console.log(event.event, event.data);
114
+ if (event.event === "done") break;
115
+ }
116
+ ```
117
+
118
+ ### Polling
119
+
120
+ For environments where SSE is not practical, poll for a completed run:
121
+
122
+ ```ts
123
+ const result = await client.runAgentAndPoll(
124
+ "agent_id",
125
+ { input: "Hello" },
126
+ { pollIntervalMs: 2_000, timeoutMs: 120_000 },
46
127
  );
128
+ ```
129
+
130
+ ### Agent input uploads
47
131
 
48
- console.log(run);
132
+ ```ts
133
+ const upload = await client.uploadAgentInput("agent_id", {
134
+ file: new Uint8Array([...]),
135
+ fileName: "input.pdf",
136
+ });
137
+ const status = await client.getAgentInputUploadStatus("agent_id", upload.upload_id);
49
138
  ```
50
139
 
51
- ### Get agent run details
140
+ ### Agent AI assistant
52
141
 
53
- Fetch details for a specific agent run, optionally including per-step outputs:
142
+ ```ts
143
+ const steps = await client.generateAgentSteps("agent_id", { user_input: "Build a RAG pipeline" });
144
+ const config = await client.generateStepConfig("agent_id", { step_type: "llm", user_input: "..." });
145
+
146
+ // Conversation history
147
+ const history = await client.getAgentAiConversationHistory("agent_id");
148
+ await client.markAgentAiSuggestion("agent_id", "conversation_id", { accepted: true });
149
+ ```
150
+
151
+ ### Agent evaluations
54
152
 
55
153
  ```ts
56
- import { Seclai } from "@seclai/sdk";
154
+ const criteria = await client.listEvaluationCriteria("agent_id");
155
+ const created = await client.createEvaluationCriteria("agent_id", { name: "Accuracy", ... });
156
+ const detail = await client.getEvaluationCriteria("criteria_id");
157
+ await client.updateEvaluationCriteria("criteria_id", { ... });
158
+ await client.deleteEvaluationCriteria("criteria_id");
57
159
 
58
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
160
+ // Test a draft
161
+ await client.testDraftEvaluation("agent_id", { criteria: { ... }, run_id: "..." });
59
162
 
60
- // Basic details
61
- const run = await client.getAgentRun("run_id");
62
- console.log(run);
163
+ // Results by criteria
164
+ const results = await client.listEvaluationResults("criteria_id");
165
+ const summary = await client.getEvaluationCriteriaSummary("criteria_id");
166
+ await client.createEvaluationResult("criteria_id", { ... });
63
167
 
64
- // Include per-step outputs with timing, durations, and credits
65
- const runWithSteps = await client.getAgentRun("run_id", {
66
- includeStepOutputs: true,
67
- });
68
- console.log(runWithSteps);
168
+ // Results by run
169
+ const runResults = await client.listRunEvaluationResults("agent_id", "run_id");
170
+
171
+ // Non-manual evaluation summary
172
+ const nonManual = await client.getNonManualEvaluationSummary("agent_id");
173
+
174
+ // Compatible runs for a criteria
175
+ const compatible = await client.listCompatibleRuns("criteria_id");
69
176
  ```
70
177
 
71
- ### Upload a file
178
+ ### Knowledge bases
72
179
 
73
- **Max file size:** 200 MiB.
180
+ ```ts
181
+ const kbs = await client.listKnowledgeBases();
182
+ const kb = await client.createKnowledgeBase({ name: "Docs KB" });
183
+ const fetched = await client.getKnowledgeBase("kb_id");
184
+ await client.updateKnowledgeBase("kb_id", { name: "Renamed KB" });
185
+ await client.deleteKnowledgeBase("kb_id");
186
+ ```
74
187
 
75
- **Supported MIME types:**
76
- - `application/epub+zip`
77
- - `application/json`
78
- - `application/msword`
79
- - `application/pdf`
80
- - `application/vnd.ms-excel`
81
- - `application/vnd.ms-outlook`
82
- - `application/vnd.ms-powerpoint`
83
- - `application/vnd.openxmlformats-officedocument.presentationml.presentation`
84
- - `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`
85
- - `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
86
- - `application/xml`
87
- - `application/zip`
88
- - `audio/flac`, `audio/mp4`, `audio/mpeg`, `audio/ogg`, `audio/wav`
89
- - `image/bmp`, `image/gif`, `image/jpeg`, `image/png`, `image/tiff`, `image/webp`
90
- - `text/csv`, `text/html`, `text/markdown`, `text/x-markdown`, `text/plain`, `text/xml`
91
- - `video/mp4`, `video/quicktime`, `video/x-msvideo`
188
+ ### Memory banks
92
189
 
93
- If the upload is sent as `application/octet-stream`, the server attempts to infer the type from the file extension, so pass `fileName` with a meaningful extension.
190
+ ```ts
191
+ const banks = await client.listMemoryBanks();
192
+ const bank = await client.createMemoryBank({ name: "Chat Memory", type: "conversation" });
193
+ const fetched = await client.getMemoryBank("mb_id");
194
+ await client.updateMemoryBank("mb_id", { name: "Renamed" });
195
+ await client.deleteMemoryBank("mb_id");
196
+
197
+ // Stats & compaction
198
+ const stats = await client.getMemoryBankStats("mb_id");
199
+ await client.compactMemoryBank("mb_id");
200
+
201
+ // Test compaction
202
+ const test = await client.testMemoryBankCompaction("mb_id", { ... });
203
+ const standalone = await client.testCompactionPromptStandalone({ ... });
204
+
205
+ // Templates & agents
206
+ const templates = await client.listMemoryBankTemplates();
207
+ const agents = await client.getAgentsUsingMemoryBank("mb_id");
208
+
209
+ // AI assistant
210
+ const suggestion = await client.generateMemoryBankConfig({ user_input: "..." });
211
+ const history = await client.getMemoryBankAiLastConversation();
212
+ await client.acceptMemoryBankAiSuggestion("conv_id", { ... });
213
+
214
+ // Source management
215
+ await client.deleteMemoryBankSource("mb_id");
216
+ ```
217
+
218
+ ### Sources
94
219
 
95
220
  ```ts
96
- import { Seclai } from "@seclai/sdk";
221
+ const sources = await client.listSources({ page: 1, limit: 20, order: "asc" });
222
+ const source = await client.createSource({ name: "My Source", ... });
223
+ const fetched = await client.getSource("source_id");
224
+ await client.updateSource("source_id", { name: "Renamed" });
225
+ await client.deleteSource("source_id");
226
+ ```
97
227
 
98
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
228
+ ### File uploads
99
229
 
100
- const bytes = new TextEncoder().encode("hello");
101
- const upload = await client.uploadFileToSource("source_connection_id", {
102
- file: bytes,
103
- fileName: "hello.txt",
104
- mimeType: "text/plain",
105
- title: "Hello",
106
- metadata: { category: "docs", author: "Ada" },
230
+ Upload a file to a source (max 200 MiB). The SDK infers MIME type from the file extension when `mimeType` is not provided.
231
+
232
+ ```ts
233
+ import { readFile } from "node:fs/promises";
234
+
235
+ await client.uploadFileToSource("source_id", {
236
+ file: await readFile("document.pdf"),
237
+ fileName: "document.pdf",
238
+ title: "Q4 Report",
239
+ metadata: { department: "finance" },
107
240
  });
108
- console.log(upload);
109
241
  ```
110
242
 
111
- ### Replace an existing content version with a new upload
112
-
113
- If you need to correct or update an uploaded document while keeping references stable,
114
- use `uploadFileToContent` to upload a new file and replace the content behind an existing
115
- `source_connection_content_version`.
243
+ Upload inline text:
116
244
 
117
245
  ```ts
118
- import { Seclai } from "@seclai/sdk";
246
+ await client.uploadInlineTextToSource("source_id", {
247
+ text: "Hello, world!",
248
+ title: "Greeting",
249
+ });
250
+ ```
119
251
 
120
- const client = new Seclai({ apiKey: process.env.SECLAI_API_KEY });
252
+ Replace a content version with a new file:
121
253
 
122
- const replace = await client.uploadFileToContent("content_version_id", {
123
- file: await fetch("https://example.invalid/updated.pdf").then((r) => r.arrayBuffer()),
254
+ ```ts
255
+ await client.uploadFileToContent("content_version_id", {
256
+ file: await readFile("updated.pdf"),
124
257
  fileName: "updated.pdf",
125
258
  mimeType: "application/pdf",
126
- title: "Updated document",
127
- metadata: { revision: 2 },
128
259
  });
260
+ ```
261
+
262
+ ### Source exports
129
263
 
130
- console.log(replace);
264
+ ```ts
265
+ const exports = await client.listSourceExports("source_id");
266
+ const exp = await client.createSourceExport("source_id", { format: "json" });
267
+ const status = await client.getSourceExport("source_id", exp.id);
268
+ const estimate = await client.estimateSourceExport("source_id", {});
269
+ const response = await client.downloadSourceExport("source_id", exp.id);
270
+ await client.deleteSourceExport("source_id", exp.id);
271
+ await client.cancelSourceExport("source_id", exp.id);
131
272
  ```
132
273
 
133
- ## Development
274
+ ### Source embedding migrations
134
275
 
135
- ### Base URL
276
+ ```ts
277
+ const migration = await client.getSourceEmbeddingMigration("source_id");
278
+ await client.startSourceEmbeddingMigration("source_id", { target_model: "..." });
279
+ await client.cancelSourceEmbeddingMigration("source_id");
280
+ ```
136
281
 
137
- Set `SECLAI_API_URL` to point at a different API host (e.g., staging):
282
+ ### Content
138
283
 
139
- ```bash
140
- export SECLAI_API_URL="https://example.invalid"
284
+ ```ts
285
+ const detail = await client.getContentDetail("content_id", { start: 0, end: 1000 });
286
+ const embeddings = await client.listContentEmbeddings("content_id");
287
+ await client.deleteContent("content_id");
288
+
289
+ // Replace content with inline text
290
+ await client.replaceContentWithInlineText("content_id", { text: "Updated text", title: "Updated" });
291
+
292
+ // Upload a replacement file
293
+ await client.uploadFileToContent("content_id", {
294
+ file: await readFile("updated.pdf"),
295
+ fileName: "updated.pdf",
296
+ });
297
+ ```
298
+
299
+ ### Solutions
300
+
301
+ ```ts
302
+ const solutions = await client.listSolutions();
303
+ const sol = await client.createSolution({ name: "My Solution" });
304
+ const fetched = await client.getSolution("solution_id");
305
+ await client.updateSolution("solution_id", { name: "Renamed" });
306
+ await client.deleteSolution("solution_id");
307
+
308
+ // Link / unlink resources
309
+ await client.linkAgentsToSolution("solution_id", { ids: ["agent_id"] });
310
+ await client.unlinkAgentsFromSolution("solution_id", { ids: ["agent_id"] });
311
+ await client.linkKnowledgeBasesToSolution("solution_id", { ids: ["kb_id"] });
312
+ await client.unlinkKnowledgeBasesFromSolution("solution_id", { ids: ["kb_id"] });
313
+ await client.linkSourceConnectionsToSolution("solution_id", { ids: ["source_id"] });
314
+ await client.unlinkSourceConnectionsFromSolution("solution_id", { ids: ["source_id"] });
315
+
316
+ // AI assistant
317
+ const plan = await client.generateSolutionAiPlan("solution_id", { user_input: "Set up a RAG pipeline" });
318
+ await client.acceptSolutionAiPlan("solution_id", "conversation_id", {});
319
+ await client.declineSolutionAiPlan("solution_id", "conversation_id");
320
+
321
+ // AI-generated knowledge base / source within the solution
322
+ await client.generateSolutionAiKnowledgeBase("solution_id", { user_input: "..." });
323
+ await client.generateSolutionAiSource("solution_id", { user_input: "..." });
324
+
325
+ // Conversations
326
+ const convs = await client.listSolutionConversations("solution_id");
327
+ await client.addSolutionConversationTurn("solution_id", { user_input: "..." });
328
+ await client.markSolutionConversationTurn("solution_id", "conversation_id", { ... });
329
+ ```
330
+
331
+ ### Governance AI
332
+
333
+ ```ts
334
+ const plan = await client.generateGovernanceAiPlan({ user_input: "Add a toxicity policy" });
335
+ const convs = await client.listGovernanceAiConversations();
336
+ await client.acceptGovernanceAiPlan("conversation_id");
337
+ await client.declineGovernanceAiPlan("conversation_id");
338
+ ```
339
+
340
+ ### Alerts
341
+
342
+ ```ts
343
+ const alerts = await client.listAlerts({ status: "active" });
344
+ const alert = await client.getAlert("alert_id");
345
+ await client.changeAlertStatus("alert_id", { status: "resolved" });
346
+ await client.addAlertComment("alert_id", { text: "Investigating" });
347
+
348
+ // Subscriptions
349
+ await client.subscribeToAlert("alert_id");
350
+ await client.unsubscribeFromAlert("alert_id");
351
+
352
+ // Alert configs
353
+ const configs = await client.listAlertConfigs();
354
+ await client.createAlertConfig({ ... });
355
+ await client.getAlertConfig("config_id");
356
+ await client.updateAlertConfig("config_id", { ... });
357
+ await client.deleteAlertConfig("config_id");
358
+
359
+ // Organization preferences
360
+ const prefs = await client.listOrganizationAlertPreferences();
361
+ await client.updateOrganizationAlertPreference("org_id", "alert_type", { ... });
362
+ ```
363
+
364
+ ### Models
365
+
366
+ ```ts
367
+ const alerts = await client.listModelAlerts();
368
+ await client.markModelAlertRead("alert_id");
369
+ await client.markAllModelAlertsRead();
370
+ const unread = await client.getUnreadModelAlertCount();
371
+ const recs = await client.getModelRecommendations("model_id");
372
+ ```
373
+
374
+ ### Search
375
+
376
+ ```ts
377
+ const results = await client.search({ query: "quarterly report" });
378
+ const filtered = await client.search({ query: "my agent", entityType: "agent", limit: 5 });
379
+ ```
380
+
381
+ ### Top-level AI assistant
382
+
383
+ ```ts
384
+ // Generate plans for different resource types
385
+ const kb = await client.aiAssistantKnowledgeBase({ user_input: "Create a docs KB" });
386
+ const src = await client.aiAssistantSource({ user_input: "Add a web source" });
387
+ const sol = await client.aiAssistantSolution({ user_input: "Set up monitoring" });
388
+ const mb = await client.aiAssistantMemoryBank({ user_input: "Create a chat memory" });
389
+
390
+ // Accept or decline the generated plan
391
+ await client.acceptAiAssistantPlan("conversation_id", { confirm_deletions: true });
392
+ await client.declineAiAssistantPlan("conversation_id");
393
+
394
+ // Memory bank conversation history
395
+ const history = await client.getAiAssistantMemoryBankHistory();
396
+ await client.acceptAiMemoryBankSuggestion("conversation_id", { ... });
397
+
398
+ // Feedback
399
+ await client.submitAiFeedback({ ... });
400
+ ```
401
+
402
+ ## Pagination helper
403
+
404
+ Automatically iterate through all pages:
405
+
406
+ ```ts
407
+ for await (const source of client.paginate(
408
+ (opts) => client.listSources(opts),
409
+ { limit: 50 },
410
+ )) {
411
+ console.log(source.name);
412
+ }
413
+ ```
414
+
415
+ ## Error handling
416
+
417
+ All errors extend `SeclaiError`:
418
+
419
+ ```ts
420
+ import {
421
+ SeclaiError,
422
+ SeclaiConfigurationError,
423
+ SeclaiAPIStatusError,
424
+ SeclaiAPIValidationError,
425
+ SeclaiStreamingError,
426
+ } from "@seclai/sdk";
427
+
428
+ try {
429
+ await client.getAgent("bad_id");
430
+ } catch (err) {
431
+ if (err instanceof SeclaiAPIValidationError) {
432
+ console.error("Validation:", err.validationError);
433
+ } else if (err instanceof SeclaiAPIStatusError) {
434
+ console.error(`HTTP ${err.statusCode}:`, err.responseText);
435
+ } else if (err instanceof SeclaiStreamingError) {
436
+ console.error("Stream failed for run:", err.runId);
437
+ }
438
+ }
439
+ ```
440
+
441
+ ## Cancellation (AbortSignal)
442
+
443
+ All low-level methods support an `AbortSignal` for request cancellation:
444
+
445
+ ```ts
446
+ const controller = new AbortController();
447
+
448
+ // Cancel after 5 seconds
449
+ setTimeout(() => controller.abort(), 5_000);
450
+
451
+ const data = await client.request("GET", "/agents", {
452
+ signal: controller.signal,
453
+ });
141
454
  ```
142
455
 
456
+ ## Low-level access
457
+
458
+ For endpoints not yet covered by a convenience method, use `request` or `requestRaw`:
459
+
460
+ ```ts
461
+ // JSON request/response
462
+ const data = await client.request("POST", "/custom/endpoint", {
463
+ json: { key: "value" },
464
+ query: { filter: "active" },
465
+ });
466
+
467
+ // Raw Response (e.g. binary downloads)
468
+ const response = await client.requestRaw("GET", "/files/download/123");
469
+ const blob = await response.blob();
470
+ ```
471
+
472
+ ## Development
473
+
143
474
  ### Install dependencies
144
475
 
145
476
  ```bash
@@ -160,6 +491,12 @@ npm run build
160
491
 
161
492
  This also regenerates `src/openapi.ts` from `openapi/seclai.openapi.json`.
162
493
 
494
+ ### Run tests
495
+
496
+ ```bash
497
+ npm test
498
+ ```
499
+
163
500
  ### Generate docs
164
501
 
165
502
  Generate HTML docs into `build/docs/`: