dominus-sdk-nodejs 1.2.44 → 1.3.1

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.
@@ -0,0 +1,683 @@
1
+ /**
2
+ * AI Namespace - Unified agent-runtime operations.
3
+ *
4
+ * Provides agent execution, LLM completions, RAG corpus management,
5
+ * artifacts, speech services, and workflow orchestration.
6
+ *
7
+ * All operations go through /api/* endpoints on the agent-runtime service,
8
+ * proxied via the gateway at /svc/*.
9
+ *
10
+ * Usage:
11
+ * import { dominus } from 'dominus-sdk-nodejs';
12
+ *
13
+ * // Agent execution
14
+ * const result = await dominus.ai.runAgent({
15
+ * conversationId: "conv-123",
16
+ * systemPrompt: "You are helpful.",
17
+ * userPrompt: "Hello!"
18
+ * });
19
+ *
20
+ * // Streaming agent execution
21
+ * for await (const chunk of dominus.ai.streamAgent({
22
+ * conversationId: "conv-123",
23
+ * systemPrompt: "You are helpful.",
24
+ * userPrompt: "Tell me a story"
25
+ * })) {
26
+ * process.stdout.write(chunk.content || '');
27
+ * }
28
+ *
29
+ * // LLM completion
30
+ * const result = await dominus.ai.complete({
31
+ * messages: [{ role: "user", content: "Hello" }],
32
+ * provider: "claude",
33
+ * model: "claude-sonnet-4-5"
34
+ * });
35
+ *
36
+ * // RAG operations
37
+ * await dominus.ai.rag.ensure("my-corpus");
38
+ * await dominus.ai.rag.upsert("my-corpus", "doc-1", { content: "Important info" });
39
+ * const results = await dominus.ai.rag.search("my-corpus", "important");
40
+ *
41
+ * // Speech
42
+ * const text = await dominus.ai.stt(audioBuffer, { format: "wav" });
43
+ * const audio = await dominus.ai.tts("Hello world", { voice: "nova" });
44
+ */
45
+ // ============================================================================
46
+ // Helper Functions
47
+ // ============================================================================
48
+ /**
49
+ * Convert snake_case keys to camelCase in an object (shallow)
50
+ */
51
+ function snakeToCamel(str) {
52
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
53
+ }
54
+ /**
55
+ * Normalize response object keys from snake_case to camelCase
56
+ */
57
+ function normalizeResponse(obj) {
58
+ const result = {};
59
+ for (const [key, value] of Object.entries(obj)) {
60
+ const camelKey = snakeToCamel(key);
61
+ result[camelKey] = value;
62
+ // Also keep original key for backward compatibility
63
+ if (camelKey !== key) {
64
+ result[key] = value;
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ // ============================================================================
70
+ // RAG Sub-namespace
71
+ // ============================================================================
72
+ export class RagSubNamespace {
73
+ client;
74
+ constructor(client) {
75
+ this.client = client;
76
+ }
77
+ /** List all corpora */
78
+ async list() {
79
+ const result = await this.client.request({
80
+ endpoint: '/api/rag',
81
+ method: 'GET',
82
+ useGateway: true,
83
+ });
84
+ if (Array.isArray(result))
85
+ return result;
86
+ return result.corpora || [];
87
+ }
88
+ /** Ensure corpus exists (create if not) */
89
+ async ensure(slug, options) {
90
+ const body = {};
91
+ if (options?.description)
92
+ body.description = options.description;
93
+ if (options?.embeddingModel)
94
+ body.embedding_model = options.embeddingModel;
95
+ return this.client.request({
96
+ endpoint: `/api/rag/${slug}/ensure`,
97
+ body,
98
+ useGateway: true,
99
+ });
100
+ }
101
+ /** Get corpus statistics */
102
+ async stats(slug) {
103
+ return this.client.request({
104
+ endpoint: `/api/rag/${slug}/stats`,
105
+ method: 'GET',
106
+ useGateway: true,
107
+ });
108
+ }
109
+ /** Drop/delete a corpus */
110
+ async drop(slug) {
111
+ return this.client.request({
112
+ endpoint: `/api/rag/${slug}`,
113
+ method: 'DELETE',
114
+ useGateway: true,
115
+ });
116
+ }
117
+ /** List entries in a corpus */
118
+ async entries(slug, options) {
119
+ return this.client.request({
120
+ endpoint: `/api/rag/${slug}/entries`,
121
+ body: {
122
+ limit: options?.limit ?? 100,
123
+ offset: options?.offset ?? 0,
124
+ ...(options?.category && { category: options.category }),
125
+ },
126
+ useGateway: true,
127
+ });
128
+ }
129
+ /** Upsert a single entry */
130
+ async upsert(slug, identifier, options) {
131
+ const body = {
132
+ content_markdown: options.content, // Transform: content -> content_markdown
133
+ };
134
+ if (options.name)
135
+ body.name = options.name;
136
+ if (options.description)
137
+ body.description = options.description;
138
+ if (options.category)
139
+ body.category = options.category;
140
+ if (options.subcategory)
141
+ body.subcategory = options.subcategory;
142
+ if (options.sourceReference)
143
+ body.source_reference = options.sourceReference;
144
+ if (options.metadata)
145
+ body.metadata = options.metadata;
146
+ return this.client.request({
147
+ endpoint: `/api/rag/${slug}/${identifier}`,
148
+ method: 'PUT',
149
+ body,
150
+ useGateway: true,
151
+ });
152
+ }
153
+ /** Get a specific entry */
154
+ async get(slug, identifier) {
155
+ return this.client.request({
156
+ endpoint: `/api/rag/${slug}/${identifier}`,
157
+ method: 'GET',
158
+ useGateway: true,
159
+ });
160
+ }
161
+ /** Delete an entry */
162
+ async delete(slug, identifier) {
163
+ return this.client.request({
164
+ endpoint: `/api/rag/${slug}/${identifier}`,
165
+ method: 'DELETE',
166
+ useGateway: true,
167
+ });
168
+ }
169
+ /** Bulk upsert entries */
170
+ async bulkUpsert(slug, entries) {
171
+ // Transform 'content' to 'content_markdown' for API compatibility
172
+ const transformed = entries.map((entry) => {
173
+ const e = { ...entry };
174
+ if (e.content && !e.content_markdown) {
175
+ e.content_markdown = e.content;
176
+ delete e.content;
177
+ }
178
+ return e;
179
+ });
180
+ return this.client.request({
181
+ endpoint: `/api/rag/${slug}/bulk`,
182
+ body: { entries: transformed },
183
+ useGateway: true,
184
+ });
185
+ }
186
+ /** Semantic search */
187
+ async search(slug, query, options) {
188
+ const body = {
189
+ query,
190
+ limit: options?.limit ?? 10,
191
+ };
192
+ if (options?.threshold !== undefined)
193
+ body.threshold = options.threshold;
194
+ if (options?.category)
195
+ body.category = options.category;
196
+ if (options?.filters)
197
+ body.filters = options.filters;
198
+ const result = await this.client.request({
199
+ endpoint: `/api/rag/${slug}/search`,
200
+ body,
201
+ useGateway: true,
202
+ });
203
+ if (Array.isArray(result))
204
+ return result;
205
+ return result.results || [];
206
+ }
207
+ /** Semantic search with reranking */
208
+ async searchRerank(slug, query, options) {
209
+ const body = {
210
+ query,
211
+ limit: options?.limit ?? 10,
212
+ };
213
+ if (options?.rerankModel)
214
+ body.rerank_model = options.rerankModel;
215
+ const result = await this.client.request({
216
+ endpoint: `/api/rag/${slug}/search/rerank`,
217
+ body,
218
+ useGateway: true,
219
+ });
220
+ if (Array.isArray(result))
221
+ return result;
222
+ return result.results || [];
223
+ }
224
+ /** Ingest a document (PDF, DOCX, etc.) */
225
+ async ingest(slug, content, filename, options) {
226
+ return this.client.binaryUpload({
227
+ endpoint: `/api/rag/${slug}/ingest`,
228
+ fileBytes: content,
229
+ filename,
230
+ contentType: options?.contentType || 'application/octet-stream',
231
+ additionalFields: {
232
+ chunk_size: String(options?.chunkSize ?? 1000),
233
+ chunk_overlap: String(options?.chunkOverlap ?? 200),
234
+ },
235
+ useGateway: true,
236
+ });
237
+ }
238
+ /** Delete a document and all its chunks */
239
+ async deleteDocument(slug, documentId) {
240
+ return this.client.request({
241
+ endpoint: `/api/rag/${slug}/document/${documentId}`,
242
+ method: 'DELETE',
243
+ useGateway: true,
244
+ });
245
+ }
246
+ }
247
+ // ============================================================================
248
+ // Artifacts Sub-namespace
249
+ // ============================================================================
250
+ export class ArtifactsSubNamespace {
251
+ client;
252
+ constructor(client) {
253
+ this.client = client;
254
+ }
255
+ /** Get artifact by ID */
256
+ async get(artifactId, conversationId) {
257
+ return this.client.request({
258
+ endpoint: `/api/agent/artifacts/${artifactId}?conversation_id=${conversationId}`,
259
+ method: 'GET',
260
+ useGateway: true,
261
+ });
262
+ }
263
+ /** Create a new artifact */
264
+ async create(options) {
265
+ return this.client.request({
266
+ endpoint: '/api/agent/artifacts',
267
+ body: {
268
+ key: options.name, // Transform: name -> key
269
+ content: options.content,
270
+ conversation_id: options.conversationId,
271
+ content_type: options.artifactType || 'text/plain', // Transform: artifactType -> content_type
272
+ is_base64: options.isBase64 || false,
273
+ },
274
+ useGateway: true,
275
+ });
276
+ }
277
+ /** List artifacts for a conversation */
278
+ async list(conversationId) {
279
+ const result = await this.client.request({
280
+ endpoint: `/api/agent/artifacts?conversation_id=${conversationId}`,
281
+ method: 'GET',
282
+ useGateway: true,
283
+ });
284
+ if (Array.isArray(result))
285
+ return result;
286
+ return result.artifacts || [];
287
+ }
288
+ /** Delete an artifact */
289
+ async delete(artifactId, conversationId) {
290
+ return this.client.request({
291
+ endpoint: `/api/agent/artifacts/${artifactId}?conversation_id=${conversationId}`,
292
+ method: 'DELETE',
293
+ useGateway: true,
294
+ });
295
+ }
296
+ }
297
+ // ============================================================================
298
+ // Results Sub-namespace
299
+ // ============================================================================
300
+ export class ResultsSubNamespace {
301
+ client;
302
+ constructor(client) {
303
+ this.client = client;
304
+ }
305
+ /** Get async result by key */
306
+ async get(resultKey) {
307
+ return this.client.request({
308
+ endpoint: `/api/results/${resultKey}`,
309
+ method: 'GET',
310
+ useGateway: true,
311
+ });
312
+ }
313
+ /** Poll for result until completion or timeout */
314
+ async poll(resultKey, options) {
315
+ const interval = options?.interval ?? 1000;
316
+ const timeout = options?.timeout ?? 300000;
317
+ const start = Date.now();
318
+ while (Date.now() - start < timeout) {
319
+ const result = await this.get(resultKey);
320
+ if (result.status === 'completed') {
321
+ return result;
322
+ }
323
+ if (result.status === 'failed') {
324
+ throw new Error(`Operation failed: ${result.error || 'Unknown error'}`);
325
+ }
326
+ await new Promise((resolve) => setTimeout(resolve, interval));
327
+ }
328
+ throw new Error(`Result polling timed out after ${timeout}ms`);
329
+ }
330
+ }
331
+ // ============================================================================
332
+ // Workflow Sub-namespace
333
+ // ============================================================================
334
+ export class WorkflowSubNamespace {
335
+ client;
336
+ constructor(client) {
337
+ this.client = client;
338
+ }
339
+ /** Execute a multi-agent workflow */
340
+ async execute(options) {
341
+ const body = {
342
+ workflow_id: options.workflowId,
343
+ input: options.input,
344
+ mode: options.mode || 'blocking',
345
+ };
346
+ if (options.conversationId)
347
+ body.conversation_id = options.conversationId;
348
+ if (options.webhookUrl)
349
+ body.webhook_url = options.webhookUrl;
350
+ return this.client.request({
351
+ endpoint: '/api/orchestration/execute',
352
+ body,
353
+ useGateway: true,
354
+ });
355
+ }
356
+ /** Validate a workflow definition */
357
+ async validate(workflowDefinition) {
358
+ return this.client.request({
359
+ endpoint: '/api/orchestration/validate',
360
+ body: { definition: workflowDefinition },
361
+ useGateway: true,
362
+ });
363
+ }
364
+ /** Get messages from an execution */
365
+ async messages(executionId, options) {
366
+ const result = await this.client.request({
367
+ endpoint: `/api/orchestration/messages/${executionId}`,
368
+ body: { limit: options?.limit ?? 100, offset: options?.offset ?? 0 },
369
+ useGateway: true,
370
+ });
371
+ if (Array.isArray(result))
372
+ return result;
373
+ return result.messages || [];
374
+ }
375
+ /** Replay events from an execution */
376
+ async events(executionId, fromTimestamp) {
377
+ const body = {};
378
+ if (fromTimestamp)
379
+ body.from = fromTimestamp;
380
+ const result = await this.client.request({
381
+ endpoint: `/api/orchestration/events/${executionId}`,
382
+ body: Object.keys(body).length > 0 ? body : undefined,
383
+ useGateway: true,
384
+ });
385
+ if (Array.isArray(result))
386
+ return result;
387
+ return result.events || [];
388
+ }
389
+ /** Get execution status */
390
+ async status(executionId) {
391
+ return this.client.request({
392
+ endpoint: `/api/orchestration/status/${executionId}`,
393
+ method: 'GET',
394
+ useGateway: true,
395
+ });
396
+ }
397
+ /** Get final execution output */
398
+ async output(executionId) {
399
+ return this.client.request({
400
+ endpoint: `/api/orchestration/output/${executionId}`,
401
+ method: 'GET',
402
+ useGateway: true,
403
+ });
404
+ }
405
+ }
406
+ // ============================================================================
407
+ // Main AI Namespace
408
+ // ============================================================================
409
+ export class AiNamespace {
410
+ client;
411
+ /** RAG corpus management */
412
+ rag;
413
+ /** Conversation artifacts */
414
+ artifacts;
415
+ /** Async result polling */
416
+ results;
417
+ /** Multi-agent workflow orchestration */
418
+ workflow;
419
+ constructor(client) {
420
+ this.client = client;
421
+ this.rag = new RagSubNamespace(client);
422
+ this.artifacts = new ArtifactsSubNamespace(client);
423
+ this.results = new ResultsSubNamespace(client);
424
+ this.workflow = new WorkflowSubNamespace(client);
425
+ }
426
+ // ========================================
427
+ // Agent Execution
428
+ // ========================================
429
+ /** Execute agent with blocking wait */
430
+ async runAgent(options) {
431
+ const body = {
432
+ conversation_id: options.conversationId,
433
+ system_prompt: options.systemPrompt,
434
+ user_prompt: options.userPrompt,
435
+ history_source: options.historySource || 'conversation_id',
436
+ model: options.model || 'claude-sonnet-4-5',
437
+ };
438
+ if (options.inlineHistory)
439
+ body.inline_history = options.inlineHistory;
440
+ if (options.preloadedContext)
441
+ body.preloaded_context = options.preloadedContext;
442
+ if (options.artifactRefs)
443
+ body.artifact_refs = options.artifactRefs;
444
+ if (options.toolAllowlist)
445
+ body.tool_allowlist = options.toolAllowlist;
446
+ if (options.guardrails)
447
+ body.guardrails = options.guardrails;
448
+ if (options.outputSchema)
449
+ body.output_schema = options.outputSchema;
450
+ if (options.toolEndpoint)
451
+ body.tool_endpoint = options.toolEndpoint;
452
+ const result = await this.client.request({
453
+ endpoint: '/api/agent/run',
454
+ body,
455
+ timeout: options.timeout,
456
+ useGateway: true,
457
+ });
458
+ // Normalize snake_case keys to camelCase
459
+ return normalizeResponse(result);
460
+ }
461
+ /** Execute agent with SSE streaming */
462
+ async *streamAgent(options) {
463
+ const body = {
464
+ conversation_id: options.conversationId,
465
+ system_prompt: options.systemPrompt,
466
+ user_prompt: options.userPrompt,
467
+ history_source: options.historySource || 'conversation_id',
468
+ model: options.model || 'claude-sonnet-4-5',
469
+ };
470
+ if (options.inlineHistory)
471
+ body.inline_history = options.inlineHistory;
472
+ if (options.preloadedContext)
473
+ body.preloaded_context = options.preloadedContext;
474
+ if (options.artifactRefs)
475
+ body.artifact_refs = options.artifactRefs;
476
+ if (options.toolAllowlist)
477
+ body.tool_allowlist = options.toolAllowlist;
478
+ if (options.guardrails)
479
+ body.guardrails = options.guardrails;
480
+ if (options.outputSchema)
481
+ body.output_schema = options.outputSchema;
482
+ if (options.toolEndpoint)
483
+ body.tool_endpoint = options.toolEndpoint;
484
+ for await (const chunk of this.client.streamRequest({
485
+ endpoint: '/api/agent/stream',
486
+ body,
487
+ onChunk: options.onChunk,
488
+ timeout: options.timeout || 300000,
489
+ useGateway: true,
490
+ })) {
491
+ // Normalize chunk and yield
492
+ const normalized = normalizeResponse(chunk);
493
+ yield normalized;
494
+ }
495
+ }
496
+ /** Fire-and-forget async execution */
497
+ async runAgentAsync(options) {
498
+ const body = {
499
+ conversation_id: options.conversationId,
500
+ system_prompt: options.systemPrompt,
501
+ user_prompt: options.userPrompt,
502
+ result_key: options.resultKey,
503
+ history_source: options.historySource || 'conversation_id',
504
+ model: options.model || 'claude-sonnet-4-5',
505
+ };
506
+ if (options.inlineHistory)
507
+ body.inline_history = options.inlineHistory;
508
+ if (options.preloadedContext)
509
+ body.preloaded_context = options.preloadedContext;
510
+ if (options.artifactRefs)
511
+ body.artifact_refs = options.artifactRefs;
512
+ if (options.toolAllowlist)
513
+ body.tool_allowlist = options.toolAllowlist;
514
+ if (options.guardrails)
515
+ body.guardrails = options.guardrails;
516
+ if (options.outputSchema)
517
+ body.output_schema = options.outputSchema;
518
+ if (options.toolEndpoint)
519
+ body.tool_endpoint = options.toolEndpoint;
520
+ if (options.webhookUrl)
521
+ body.webhook_url = options.webhookUrl;
522
+ return this.client.request({
523
+ endpoint: '/api/agent/run-async',
524
+ body,
525
+ useGateway: true,
526
+ });
527
+ }
528
+ // ========================================
529
+ // Conversation History
530
+ // ========================================
531
+ /** Get conversation history */
532
+ async history(conversationId, limit = 50) {
533
+ const result = await this.client.request({
534
+ endpoint: `/api/agent/history/${conversationId}?limit=${limit}`,
535
+ method: 'GET',
536
+ useGateway: true,
537
+ });
538
+ if (Array.isArray(result))
539
+ return result;
540
+ return result.messages || [];
541
+ }
542
+ // ========================================
543
+ // LLM Completions
544
+ // ========================================
545
+ /** Blocking LLM completion */
546
+ async complete(options) {
547
+ let systemPrompt = options.systemPrompt;
548
+ let userPrompt = options.userPrompt;
549
+ // Convert messages to system_prompt/user_prompt if needed
550
+ if (options.messages && !userPrompt) {
551
+ for (const msg of options.messages) {
552
+ if (msg.role === 'system') {
553
+ systemPrompt = msg.content;
554
+ }
555
+ else if (msg.role === 'user') {
556
+ userPrompt = msg.content;
557
+ }
558
+ }
559
+ }
560
+ if (!systemPrompt) {
561
+ systemPrompt = 'You are a helpful assistant.';
562
+ }
563
+ if (!userPrompt) {
564
+ throw new Error("Either 'messages' with a user message or 'userPrompt' is required");
565
+ }
566
+ const body = {
567
+ provider: options.provider || 'claude',
568
+ model: options.model || 'claude-sonnet-4-5',
569
+ system_prompt: systemPrompt,
570
+ user_prompt: userPrompt,
571
+ temperature: options.temperature ?? 0.7,
572
+ };
573
+ if (options.conversationId)
574
+ body.conversation_id = options.conversationId;
575
+ if (options.maxTokens)
576
+ body.max_tokens = options.maxTokens;
577
+ return this.client.request({
578
+ endpoint: '/api/llm/complete',
579
+ body,
580
+ timeout: options.timeout,
581
+ useGateway: true,
582
+ });
583
+ }
584
+ /** Streaming LLM completion */
585
+ async *completeStream(options) {
586
+ let systemPrompt = options.systemPrompt;
587
+ let userPrompt = options.userPrompt;
588
+ // Convert messages to system_prompt/user_prompt if needed
589
+ if (options.messages && !userPrompt) {
590
+ for (const msg of options.messages) {
591
+ if (msg.role === 'system') {
592
+ systemPrompt = msg.content;
593
+ }
594
+ else if (msg.role === 'user') {
595
+ userPrompt = msg.content;
596
+ }
597
+ }
598
+ }
599
+ if (!systemPrompt) {
600
+ systemPrompt = 'You are a helpful assistant.';
601
+ }
602
+ if (!userPrompt) {
603
+ throw new Error("Either 'messages' with a user message or 'userPrompt' is required");
604
+ }
605
+ const body = {
606
+ provider: options.provider || 'claude',
607
+ model: options.model || 'claude-sonnet-4-5',
608
+ system_prompt: systemPrompt,
609
+ user_prompt: userPrompt,
610
+ temperature: options.temperature ?? 0.7,
611
+ };
612
+ if (options.conversationId)
613
+ body.conversation_id = options.conversationId;
614
+ if (options.maxTokens)
615
+ body.max_tokens = options.maxTokens;
616
+ for await (const chunk of this.client.streamRequest({
617
+ endpoint: '/api/llm/stream',
618
+ body,
619
+ onChunk: options.onChunk,
620
+ timeout: options.timeout || 120000,
621
+ useGateway: true,
622
+ })) {
623
+ // Normalize chunk and yield
624
+ const normalized = normalizeResponse(chunk);
625
+ yield normalized;
626
+ }
627
+ }
628
+ // ========================================
629
+ // Speech Services
630
+ // ========================================
631
+ /** Speech-to-text conversion */
632
+ async stt(audio, options) {
633
+ const format = options?.format || 'wav';
634
+ const additionalFields = {};
635
+ if (options?.language)
636
+ additionalFields.language = options.language;
637
+ const result = await this.client.binaryUpload({
638
+ endpoint: '/api/agent/stt',
639
+ fileBytes: audio,
640
+ filename: `audio.${format}`,
641
+ contentType: `audio/${format}`,
642
+ additionalFields: Object.keys(additionalFields).length > 0 ? additionalFields : undefined,
643
+ useGateway: true,
644
+ });
645
+ return result;
646
+ }
647
+ /** Text-to-speech conversion */
648
+ async tts(text, options) {
649
+ return this.client.binaryDownload({
650
+ endpoint: '/api/agent/tts',
651
+ body: {
652
+ text,
653
+ voice: options?.voice || 'nova',
654
+ format: options?.format || 'mp3',
655
+ speed: options?.speed ?? 1.0,
656
+ },
657
+ useGateway: true,
658
+ });
659
+ }
660
+ // ========================================
661
+ // Pre-flight Setup
662
+ // ========================================
663
+ /** Pre-flight session setup */
664
+ async setup(options) {
665
+ const body = {
666
+ conversation_id: options.conversationId,
667
+ };
668
+ if (options.artifacts)
669
+ body.artifacts = options.artifacts;
670
+ if (options.ragCorpus)
671
+ body.rag_corpus = options.ragCorpus;
672
+ if (options.context)
673
+ body.context = options.context;
674
+ const result = await this.client.request({
675
+ endpoint: '/api/session/setup',
676
+ body,
677
+ useGateway: true,
678
+ });
679
+ // Normalize snake_case keys to camelCase
680
+ return normalizeResponse(result);
681
+ }
682
+ }
683
+ //# sourceMappingURL=ai.js.map