@yushaw/sanqian-ai-sdk 0.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/dist/index.mjs ADDED
@@ -0,0 +1,2065 @@
1
+ // src/contracts/tools.ts
2
+ function createToolRegistry() {
3
+ const tools = /* @__PURE__ */ new Map();
4
+ return {
5
+ register(definition, handler, metadata) {
6
+ tools.set(definition.name, {
7
+ definition,
8
+ handler,
9
+ metadata: {
10
+ category: metadata?.category ?? "read-only",
11
+ readOnlyHint: metadata?.readOnlyHint,
12
+ destructiveHint: metadata?.destructiveHint,
13
+ idempotentHint: metadata?.idempotentHint
14
+ }
15
+ });
16
+ },
17
+ get(name) {
18
+ return tools.get(name);
19
+ },
20
+ list() {
21
+ return Array.from(tools.values());
22
+ },
23
+ async execute(input) {
24
+ const registration = tools.get(input.name);
25
+ if (!registration) {
26
+ return {
27
+ toolCallId: input.toolCallId,
28
+ success: false,
29
+ error: `Unknown tool: ${input.name}`
30
+ };
31
+ }
32
+ try {
33
+ const result = await registration.handler(input.args, {
34
+ toolCallId: input.toolCallId,
35
+ signal: input.signal
36
+ });
37
+ return {
38
+ toolCallId: input.toolCallId,
39
+ result,
40
+ success: true
41
+ };
42
+ } catch (error) {
43
+ const message = error instanceof Error ? error.message : "Tool execution failed";
44
+ return {
45
+ toolCallId: input.toolCallId,
46
+ success: false,
47
+ error: message
48
+ };
49
+ }
50
+ }
51
+ };
52
+ }
53
+
54
+ // src/contracts/permissions.ts
55
+ function createPermissionGate(policy) {
56
+ const pending = /* @__PURE__ */ new Map();
57
+ const listeners = /* @__PURE__ */ new Set();
58
+ let counter = 0;
59
+ return {
60
+ async requestPermission(input) {
61
+ const decision = await policy.evaluate(input);
62
+ if (decision.type !== "ask") {
63
+ return decision;
64
+ }
65
+ return new Promise((resolve) => {
66
+ counter++;
67
+ const id = `perm-${counter}`;
68
+ const entry = {
69
+ id,
70
+ toolCallId: input.toolCallId,
71
+ toolName: input.name,
72
+ args: input.args,
73
+ createdAt: Date.now(),
74
+ resolve: (userDecision) => {
75
+ pending.delete(id);
76
+ if (userDecision === "approve") {
77
+ resolve({ type: "allow" });
78
+ } else if (userDecision === "deny") {
79
+ resolve({ type: "deny", reason: "User denied" });
80
+ } else {
81
+ resolve({ type: "deny", reason: "Cancelled" });
82
+ }
83
+ }
84
+ };
85
+ pending.set(id, entry);
86
+ for (const listener of listeners) {
87
+ listener(entry);
88
+ }
89
+ });
90
+ },
91
+ resolvePermission(id, decision) {
92
+ const entry = pending.get(id);
93
+ if (entry) {
94
+ entry.resolve(decision);
95
+ }
96
+ },
97
+ cancelAll() {
98
+ for (const entry of pending.values()) {
99
+ entry.resolve("cancel");
100
+ }
101
+ pending.clear();
102
+ },
103
+ listPending() {
104
+ return Array.from(pending.values());
105
+ },
106
+ onPermissionRequest(listener) {
107
+ listeners.add(listener);
108
+ return () => listeners.delete(listener);
109
+ }
110
+ };
111
+ }
112
+ function createDefaultPermissionPolicy() {
113
+ return {
114
+ async evaluate() {
115
+ return { type: "allow" };
116
+ }
117
+ };
118
+ }
119
+ function createCategoryPermissionPolicy(registry) {
120
+ const mutationCategories = [
121
+ "note-mutation",
122
+ "notebook-mutation",
123
+ "local-file-write",
124
+ "dangerous"
125
+ ];
126
+ return {
127
+ async evaluate(input) {
128
+ const tool = registry.get(input.name);
129
+ if (!tool) {
130
+ return { type: "deny", reason: `Unknown tool: ${input.name}` };
131
+ }
132
+ if (mutationCategories.includes(tool.metadata.category)) {
133
+ return {
134
+ type: "ask",
135
+ interrupt: {
136
+ type: "approval_request",
137
+ payload: {
138
+ tool: input.name,
139
+ args: input.args,
140
+ reason: `Tool "${input.name}" requires approval (category: ${tool.metadata.category})`
141
+ }
142
+ }
143
+ };
144
+ }
145
+ return { type: "allow" };
146
+ }
147
+ };
148
+ }
149
+ function redactToolError(error) {
150
+ let redacted = error;
151
+ redacted = redacted.replace(/\n\s+at\s+.+/g, "");
152
+ redacted = redacted.replace(/(?:\/[\w.@-]+){2,}/g, "[path]");
153
+ redacted = redacted.replace(/[A-Z]:\\[\w.\\-]+/g, "[path]");
154
+ redacted = redacted.replace(/SQLITE_\w+|sqlite3?_\w+/gi, "[db-error]");
155
+ return redacted.trim();
156
+ }
157
+
158
+ // src/contracts/conversations.ts
159
+ function createMemoryConversationStore() {
160
+ const conversations = /* @__PURE__ */ new Map();
161
+ let counter = 0;
162
+ return {
163
+ async list(input) {
164
+ let items = Array.from(conversations.values());
165
+ if (input.providerId) {
166
+ items = items.filter((c) => c.providerId === input.providerId);
167
+ }
168
+ if (input.agentId) {
169
+ items = items.filter((c) => c.agentId === input.agentId);
170
+ }
171
+ items.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
172
+ const offset = input.offset ?? 0;
173
+ const limit = input.limit ?? 20;
174
+ return {
175
+ conversations: items.slice(offset, offset + limit),
176
+ total: items.length
177
+ };
178
+ },
179
+ async get(id, input) {
180
+ const conv = conversations.get(id);
181
+ if (!conv) return null;
182
+ if (input?.messageLimit) {
183
+ return {
184
+ ...conv,
185
+ messages: conv.messages.slice(-input.messageLimit)
186
+ };
187
+ }
188
+ return conv;
189
+ },
190
+ async create(input) {
191
+ counter++;
192
+ const id = `conv-${counter}`;
193
+ const now = (/* @__PURE__ */ new Date()).toISOString();
194
+ const detail = {
195
+ id,
196
+ providerId: input.providerId,
197
+ agentId: input.agentId,
198
+ title: input.title ?? "Untitled",
199
+ createdAt: now,
200
+ updatedAt: now,
201
+ messageCount: 0,
202
+ messages: []
203
+ };
204
+ conversations.set(id, detail);
205
+ return detail;
206
+ },
207
+ async appendMessages(id, messages) {
208
+ const conv = conversations.get(id);
209
+ if (!conv) throw new Error(`Conversation not found: ${id}`);
210
+ conv.messages.push(...messages);
211
+ conv.messageCount = conv.messages.length;
212
+ conv.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
213
+ },
214
+ async update(id, patch) {
215
+ const conv = conversations.get(id);
216
+ if (!conv) throw new Error(`Conversation not found: ${id}`);
217
+ if (patch.title !== void 0) conv.title = patch.title;
218
+ conv.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
219
+ },
220
+ async delete(id) {
221
+ conversations.delete(id);
222
+ }
223
+ };
224
+ }
225
+
226
+ // src/contracts/active-work.ts
227
+ function createActiveWorkTracker() {
228
+ const works = /* @__PURE__ */ new Map();
229
+ return {
230
+ register(work) {
231
+ works.set(work.id, work);
232
+ },
233
+ unregister(id) {
234
+ works.delete(id);
235
+ },
236
+ list() {
237
+ return Array.from(works.values());
238
+ },
239
+ listByProvider(providerId) {
240
+ return Array.from(works.values()).filter((w) => w.providerId === providerId);
241
+ },
242
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
243
+ async cancelAll(_reason) {
244
+ for (const work of works.values()) {
245
+ work.abort();
246
+ }
247
+ works.clear();
248
+ },
249
+ async cancelByProvider(providerId) {
250
+ for (const [id, work] of works.entries()) {
251
+ if (work.providerId === providerId) {
252
+ work.abort();
253
+ works.delete(id);
254
+ }
255
+ }
256
+ }
257
+ };
258
+ }
259
+
260
+ // src/contracts/readiness.ts
261
+ var EMPTY_CAPABILITIES = {
262
+ streamingText: false,
263
+ reasoning: false,
264
+ toolCalling: false,
265
+ parallelToolCalls: false,
266
+ toolArgsStreaming: false,
267
+ abort: false,
268
+ hitl: false,
269
+ conversations: false,
270
+ sessionResources: false,
271
+ resourcePicker: false,
272
+ embeddings: false,
273
+ rerank: false
274
+ };
275
+ function createDisabledState() {
276
+ return {
277
+ enabled: false,
278
+ activeProviderId: null,
279
+ activeProviderKind: null,
280
+ ready: false,
281
+ capabilities: { ...EMPTY_CAPABILITIES },
282
+ source: "manual-disabled"
283
+ };
284
+ }
285
+
286
+ // src/contracts/secrets.ts
287
+ function createMemorySecretStore() {
288
+ const secrets = /* @__PURE__ */ new Map();
289
+ let counter = 0;
290
+ return {
291
+ async put(input) {
292
+ counter++;
293
+ const id = `secret-${counter}`;
294
+ const ref = { id, providerInstanceId: input.providerInstanceId };
295
+ const now = Date.now();
296
+ secrets.set(id, {
297
+ value: input.value,
298
+ descriptor: {
299
+ id,
300
+ providerInstanceId: input.providerInstanceId,
301
+ label: input.label,
302
+ maskedValue: input.value.slice(0, 4) + "****",
303
+ createdAt: now,
304
+ updatedAt: now
305
+ }
306
+ });
307
+ return ref;
308
+ },
309
+ async get(ref) {
310
+ const entry = secrets.get(ref.id);
311
+ if (!entry) throw new Error(`Secret not found: ${ref.id}`);
312
+ return entry.value;
313
+ },
314
+ async delete(ref) {
315
+ secrets.delete(ref.id);
316
+ },
317
+ async describe(ref) {
318
+ const entry = secrets.get(ref.id);
319
+ if (!entry) throw new Error(`Secret not found: ${ref.id}`);
320
+ return entry.descriptor;
321
+ }
322
+ };
323
+ }
324
+
325
+ // src/contracts/session.ts
326
+ function createSessionManager(workTracker) {
327
+ let activeProvider = null;
328
+ const listeners = /* @__PURE__ */ new Set();
329
+ function emit(event) {
330
+ for (const listener of listeners) {
331
+ listener(event);
332
+ }
333
+ }
334
+ return {
335
+ getActiveProvider() {
336
+ return activeProvider;
337
+ },
338
+ getActiveDescriptor() {
339
+ if (!activeProvider) return null;
340
+ return {
341
+ id: activeProvider.id,
342
+ kind: activeProvider.kind,
343
+ displayName: activeProvider.displayName,
344
+ capabilities: activeProvider.capabilities
345
+ };
346
+ },
347
+ getRuntimeState() {
348
+ if (!activeProvider) return createDisabledState();
349
+ return {
350
+ enabled: true,
351
+ activeProviderId: activeProvider.id,
352
+ activeProviderKind: activeProvider.kind,
353
+ ready: true,
354
+ capabilities: activeProvider.capabilities,
355
+ source: "runtime-provider"
356
+ };
357
+ },
358
+ async switchProvider(config, options) {
359
+ const previousId = activeProvider?.id ?? null;
360
+ if (options?.ifBusy === "cancel-active-work") {
361
+ await workTracker.cancelAll("provider-switch");
362
+ }
363
+ try {
364
+ await config.provider.ensureReady();
365
+ } catch (error) {
366
+ const message = error instanceof Error ? error.message : "Provider readiness failed";
367
+ return {
368
+ success: false,
369
+ previousProviderId: previousId ?? void 0,
370
+ error: message
371
+ };
372
+ }
373
+ const oldProvider = activeProvider;
374
+ activeProvider = config.provider;
375
+ if (oldProvider?.dispose) {
376
+ try {
377
+ await oldProvider.dispose();
378
+ } catch {
379
+ }
380
+ }
381
+ emit({
382
+ previousProviderId: previousId,
383
+ newProviderId: config.provider.id,
384
+ capabilities: config.provider.capabilities
385
+ });
386
+ return {
387
+ success: true,
388
+ previousProviderId: previousId ?? void 0,
389
+ newProviderId: config.provider.id
390
+ };
391
+ },
392
+ async cancelAll(reason) {
393
+ await workTracker.cancelAll(reason);
394
+ },
395
+ onProviderChanged(listener) {
396
+ listeners.add(listener);
397
+ return () => listeners.delete(listener);
398
+ }
399
+ };
400
+ }
401
+
402
+ // src/contracts/task-pipeline.ts
403
+ function createTaskPipeline(getProvider) {
404
+ let formatterQueue = Promise.resolve();
405
+ return {
406
+ async *executeWithFormatter(input) {
407
+ const provider = getProvider();
408
+ if (!provider) {
409
+ yield { type: "error", error: "No active provider" };
410
+ return;
411
+ }
412
+ yield { type: "start" };
413
+ let resultText = "";
414
+ const contentStream = provider.chatStream({
415
+ agentId: input.contentAgentId,
416
+ messages: input.contentMessages,
417
+ signal: input.signal
418
+ });
419
+ for await (const event of contentStream) {
420
+ if (input.signal?.aborted) {
421
+ yield { type: "cancelled" };
422
+ return;
423
+ }
424
+ if (event.type === "text") {
425
+ resultText += event.content;
426
+ }
427
+ if (event.type === "error") {
428
+ yield event;
429
+ return;
430
+ }
431
+ yield event;
432
+ }
433
+ if (!resultText || input.signal?.aborted) {
434
+ if (input.signal?.aborted) {
435
+ yield { type: "cancelled" };
436
+ }
437
+ return;
438
+ }
439
+ const previousQueue = formatterQueue;
440
+ let releaseSlotFn;
441
+ const currentSlot = new Promise((resolve) => {
442
+ releaseSlotFn = resolve;
443
+ });
444
+ formatterQueue = previousQueue.catch(() => void 0).then(() => currentSlot);
445
+ await previousQueue.catch(() => void 0);
446
+ try {
447
+ if (input.signal?.aborted) {
448
+ yield { type: "cancelled" };
449
+ return;
450
+ }
451
+ const formatterPrompt = buildFormatterPrompt(
452
+ input.contentMessages[input.contentMessages.length - 1]?.content ?? "",
453
+ resultText,
454
+ input.outputFormat
455
+ );
456
+ const formatterStream = provider.chatStream({
457
+ agentId: input.formatterConfig.id,
458
+ messages: [
459
+ { role: "system", content: input.formatterConfig.systemPrompt },
460
+ { role: "user", content: formatterPrompt }
461
+ ],
462
+ tools: input.outputTools,
463
+ signal: input.signal
464
+ });
465
+ for await (const event of formatterStream) {
466
+ if (input.signal?.aborted) {
467
+ yield { type: "cancelled" };
468
+ return;
469
+ }
470
+ yield event;
471
+ }
472
+ yield { type: "done", conversationId: "" };
473
+ } finally {
474
+ releaseSlotFn?.();
475
+ }
476
+ }
477
+ };
478
+ }
479
+ function buildFormatterPrompt(userRequest, content, outputFormat) {
480
+ let request = userRequest;
481
+ if (outputFormat && outputFormat !== "auto") {
482
+ const formatMap = {
483
+ paragraph: "paragraph format",
484
+ list: "list format",
485
+ table: "table format",
486
+ code: "code block format",
487
+ quote: "blockquote format"
488
+ };
489
+ const hint = formatMap[outputFormat] ?? outputFormat;
490
+ request = request ? `${request} (use ${hint})` : `Use ${hint}`;
491
+ }
492
+ return `<user_request>
493
+ ${request || "No specific request"}
494
+ </user_request>
495
+
496
+ <original_content>
497
+ ${content}
498
+ </original_content>`;
499
+ }
500
+
501
+ // src/contracts/embedding.ts
502
+ function createEmbeddingSession() {
503
+ let embeddingProvider = null;
504
+ let rerankProvider = null;
505
+ const listeners = /* @__PURE__ */ new Set();
506
+ function emit(event) {
507
+ for (const listener of listeners) {
508
+ listener(event);
509
+ }
510
+ }
511
+ return {
512
+ getEmbeddingProvider() {
513
+ return embeddingProvider;
514
+ },
515
+ getRerankProvider() {
516
+ return rerankProvider;
517
+ },
518
+ async setEmbeddingProvider(provider) {
519
+ const prev = embeddingProvider;
520
+ await provider.ensureReady();
521
+ const modelChanged = !prev || prev.model !== provider.model || prev.dimensions !== provider.dimensions;
522
+ if (prev?.dispose) {
523
+ await prev.dispose().catch(() => {
524
+ });
525
+ }
526
+ embeddingProvider = provider;
527
+ if (modelChanged) {
528
+ const event = {
529
+ previousModel: prev?.model ?? null,
530
+ previousDimensions: prev?.dimensions ?? null,
531
+ newModel: provider.model,
532
+ newDimensions: provider.dimensions
533
+ };
534
+ emit(event);
535
+ return event;
536
+ }
537
+ return null;
538
+ },
539
+ async setRerankProvider(provider) {
540
+ const prev = rerankProvider;
541
+ await provider.ensureReady();
542
+ if (prev?.dispose) {
543
+ await prev.dispose().catch(() => {
544
+ });
545
+ }
546
+ rerankProvider = provider;
547
+ },
548
+ async clearEmbeddingProvider() {
549
+ const prev = embeddingProvider;
550
+ if (!prev) return null;
551
+ if (prev.dispose) {
552
+ await prev.dispose().catch(() => {
553
+ });
554
+ }
555
+ embeddingProvider = null;
556
+ const event = {
557
+ previousModel: prev.model,
558
+ previousDimensions: prev.dimensions,
559
+ newModel: "",
560
+ newDimensions: 0
561
+ };
562
+ emit(event);
563
+ return event;
564
+ },
565
+ async clearRerankProvider() {
566
+ const prev = rerankProvider;
567
+ if (prev?.dispose) {
568
+ await prev.dispose().catch(() => {
569
+ });
570
+ }
571
+ rerankProvider = null;
572
+ },
573
+ onModelChange(listener) {
574
+ listeners.add(listener);
575
+ return () => listeners.delete(listener);
576
+ }
577
+ };
578
+ }
579
+
580
+ // src/contracts/bridge.ts
581
+ function resolveAiFeaturesFromRuntime(preference, runtimeState, sanqianInstalled) {
582
+ if (preference === "enabled") return true;
583
+ if (preference === "disabled") return false;
584
+ return runtimeState.ready || sanqianInstalled;
585
+ }
586
+
587
+ // src/contracts/bridge-impl.ts
588
+ function createChatRuntimeBridge(config) {
589
+ const { sessionManager, conversationStore, permissionGate } = config;
590
+ const activeStreams = /* @__PURE__ */ new Map();
591
+ return {
592
+ getActiveStreams() {
593
+ return activeStreams;
594
+ },
595
+ async ensureReady() {
596
+ const provider = sessionManager.getActiveProvider();
597
+ if (!provider) throw new Error("No active provider");
598
+ await provider.ensureReady();
599
+ },
600
+ isConnected() {
601
+ return sessionManager.getActiveProvider() !== null;
602
+ },
603
+ getRuntimeState() {
604
+ return sessionManager.getRuntimeState();
605
+ },
606
+ getCapabilities() {
607
+ const provider = sessionManager.getActiveProvider();
608
+ return provider?.capabilities ?? EMPTY_CAPABILITIES;
609
+ },
610
+ async *chatStream(input) {
611
+ const provider = sessionManager.getActiveProvider();
612
+ if (!provider) {
613
+ yield { type: "error", error: "No active provider" };
614
+ return;
615
+ }
616
+ const streamId = `stream-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
617
+ const abortController = new AbortController();
618
+ const streamEntry = {
619
+ streamId,
620
+ runId: null,
621
+ abortController,
622
+ cancelled: false
623
+ };
624
+ activeStreams.set(streamId, streamEntry);
625
+ if (input.signal) {
626
+ input.signal.addEventListener("abort", () => {
627
+ streamEntry.cancelled = true;
628
+ abortController.abort();
629
+ }, { once: true });
630
+ }
631
+ try {
632
+ const stream = provider.chatStream({
633
+ ...input,
634
+ signal: abortController.signal
635
+ });
636
+ for await (const event of stream) {
637
+ if (streamEntry.cancelled) {
638
+ yield { type: "cancelled", runId: streamEntry.runId ?? void 0 };
639
+ return;
640
+ }
641
+ if (event.type === "start" && "runId" in event && event.runId) {
642
+ streamEntry.runId = event.runId;
643
+ }
644
+ yield event;
645
+ }
646
+ } catch (error) {
647
+ if (!streamEntry.cancelled) {
648
+ yield { type: "error", error: error instanceof Error ? error.message : "Stream error" };
649
+ }
650
+ } finally {
651
+ activeStreams.delete(streamId);
652
+ }
653
+ },
654
+ async cancelStream(input) {
655
+ if (input?.streamId) {
656
+ const stream = activeStreams.get(input.streamId);
657
+ if (stream) {
658
+ stream.cancelled = true;
659
+ stream.abortController.abort();
660
+ const provider = sessionManager.getActiveProvider();
661
+ if (provider?.cancel && stream.runId) {
662
+ await provider.cancel(stream.runId);
663
+ }
664
+ return;
665
+ }
666
+ }
667
+ if (input?.runId) {
668
+ const provider = sessionManager.getActiveProvider();
669
+ if (provider?.cancel) {
670
+ await provider.cancel(input.runId);
671
+ }
672
+ for (const stream of activeStreams.values()) {
673
+ if (stream.runId === input.runId) {
674
+ stream.cancelled = true;
675
+ stream.abortController.abort();
676
+ break;
677
+ }
678
+ }
679
+ }
680
+ },
681
+ async sendPermissionResponse(input) {
682
+ if (permissionGate) {
683
+ permissionGate.resolvePermission(input.runId, input.decision);
684
+ return;
685
+ }
686
+ const provider = sessionManager.getActiveProvider();
687
+ if (provider?.sendPermissionResponse) {
688
+ await provider.sendPermissionResponse(input);
689
+ }
690
+ },
691
+ async listConversations(input) {
692
+ return conversationStore.list(input);
693
+ },
694
+ async getConversation(id, input) {
695
+ return conversationStore.get(id, input);
696
+ },
697
+ async deleteConversation(id) {
698
+ return conversationStore.delete(id);
699
+ }
700
+ // Resource picker -- capability-gated, not implemented for non-Sanqian
701
+ // These methods are optional on ChatRuntimeBridge
702
+ };
703
+ }
704
+
705
+ // src/contracts/output-operations.ts
706
+ function validateOutputContent(type, content) {
707
+ if (content === null || content === void 0 || typeof content !== "object") {
708
+ return `${type}: content must be an object`;
709
+ }
710
+ const c = content;
711
+ switch (type) {
712
+ case "paragraph":
713
+ if (!Array.isArray(c.paragraphs)) return "paragraph: paragraphs must be an array";
714
+ break;
715
+ case "list":
716
+ if (!Array.isArray(c.items)) return "list: items must be an array";
717
+ break;
718
+ case "table":
719
+ if (!Array.isArray(c.headers)) return "table: headers must be an array";
720
+ if (!Array.isArray(c.rows)) return "table: rows must be an array";
721
+ break;
722
+ case "heading":
723
+ if (typeof c.text !== "string") return "heading: text must be a string";
724
+ break;
725
+ case "codeBlock":
726
+ if (typeof c.code !== "string") return "codeBlock: code must be a string";
727
+ break;
728
+ case "blockquote":
729
+ if (typeof c.text !== "string") return "blockquote: text must be a string";
730
+ break;
731
+ case "html":
732
+ if (typeof c.html !== "string") return "html: html must be a string";
733
+ break;
734
+ case "noteRef":
735
+ if (typeof c.noteTitle !== "string") return "noteRef: noteTitle must be a string";
736
+ break;
737
+ }
738
+ return null;
739
+ }
740
+ function createOutputOperationsManager() {
741
+ const pending = /* @__PURE__ */ new Map();
742
+ return {
743
+ initTaskOutput(taskId, context) {
744
+ pending.set(taskId, { context, operations: [] });
745
+ },
746
+ queueOp(taskId, type, content) {
747
+ const entry = pending.get(taskId);
748
+ if (!entry) {
749
+ return { success: false, error: "No pending context for task" };
750
+ }
751
+ const validationError = validateOutputContent(type, content);
752
+ if (validationError) {
753
+ return { success: false, error: validationError };
754
+ }
755
+ entry.operations.push({ type, content });
756
+ return { success: true };
757
+ },
758
+ getTaskOutput(taskId) {
759
+ return pending.get(taskId) ?? null;
760
+ },
761
+ commitTaskOutput(taskId) {
762
+ const entry = pending.get(taskId);
763
+ if (!entry || entry.operations.length === 0) {
764
+ pending.delete(taskId);
765
+ return null;
766
+ }
767
+ const data = {
768
+ taskId,
769
+ context: entry.context,
770
+ operations: [...entry.operations]
771
+ };
772
+ pending.delete(taskId);
773
+ return data;
774
+ },
775
+ clearTaskOutput(taskId) {
776
+ pending.delete(taskId);
777
+ }
778
+ };
779
+ }
780
+
781
+ // src/providers/sanqian/event-converter.ts
782
+ function convertSdkStreamEvent(event) {
783
+ switch (event.type) {
784
+ case "start":
785
+ return {
786
+ type: "start",
787
+ runId: event.run_id,
788
+ conversationId: event.conversationId
789
+ };
790
+ case "text":
791
+ return {
792
+ type: "text",
793
+ content: event.content ?? ""
794
+ };
795
+ case "thinking":
796
+ return {
797
+ type: "thinking",
798
+ content: event.content ?? ""
799
+ };
800
+ case "tool_call":
801
+ if (!event.tool_call) return null;
802
+ return {
803
+ type: "tool_call",
804
+ toolCall: {
805
+ id: event.tool_call.id,
806
+ name: event.tool_call.function.name,
807
+ arguments: event.tool_call.function.arguments
808
+ }
809
+ };
810
+ case "tool_args_chunk":
811
+ return {
812
+ type: "tool_args_chunk",
813
+ toolCallId: event.tool_call_id ?? "",
814
+ toolName: event.tool_name ?? "",
815
+ chunk: event.chunk ?? ""
816
+ };
817
+ case "tool_args":
818
+ return {
819
+ type: "tool_args",
820
+ toolCallId: event.tool_call_id ?? "",
821
+ toolName: event.tool_name ?? "",
822
+ args: event.args ?? {}
823
+ };
824
+ case "tool_result":
825
+ return {
826
+ type: "tool_result",
827
+ toolCallId: event.tool_call_id ?? "",
828
+ result: event.result,
829
+ success: event.success,
830
+ error: event.error,
831
+ status: event.status,
832
+ actionRequired: event.action_required,
833
+ settingsTab: event.settings_tab,
834
+ settingsSubTab: event.settings_sub_tab,
835
+ commandExitCode: event.command_exit_code,
836
+ durationMs: event.duration_ms,
837
+ sandboxed: event.sandboxed,
838
+ timedOut: event.timed_out,
839
+ truncated: event.truncated,
840
+ stdoutPath: event.stdout_path,
841
+ stderrPath: event.stderr_path,
842
+ presentation: event.presentation
843
+ };
844
+ case "done":
845
+ return {
846
+ type: "done",
847
+ conversationId: event.conversationId ?? "",
848
+ title: event.title
849
+ };
850
+ case "error":
851
+ return {
852
+ type: "error",
853
+ error: event.error ?? "Unknown error"
854
+ };
855
+ case "cancelled":
856
+ return {
857
+ type: "cancelled",
858
+ runId: event.run_id
859
+ };
860
+ case "interrupt":
861
+ return {
862
+ type: "interrupt",
863
+ runId: event.run_id,
864
+ interrupt: {
865
+ type: event.interrupt_type ?? "",
866
+ payload: event.interrupt_payload
867
+ }
868
+ };
869
+ default:
870
+ return null;
871
+ }
872
+ }
873
+
874
+ // src/providers/sanqian/agent-id-resolver.ts
875
+ function createSanqianAgentIdResolver(appName) {
876
+ const mappings = /* @__PURE__ */ new Map();
877
+ const reverseMappings = /* @__PURE__ */ new Map();
878
+ const prefix = `${appName}:`;
879
+ return {
880
+ setMapping(shortId, registeredId) {
881
+ mappings.set(shortId, registeredId);
882
+ reverseMappings.set(registeredId, shortId);
883
+ },
884
+ clearMappings() {
885
+ mappings.clear();
886
+ reverseMappings.clear();
887
+ },
888
+ toRuntimeAgentId(input) {
889
+ if (!input.includes(":")) return input;
890
+ const short = reverseMappings.get(input);
891
+ if (short) return short;
892
+ if (input.startsWith(prefix)) {
893
+ return input.slice(prefix.length);
894
+ }
895
+ return input;
896
+ },
897
+ async toProviderAgentId(shortId) {
898
+ const registered = mappings.get(shortId);
899
+ if (registered) return registered;
900
+ if (shortId.includes(":")) return shortId;
901
+ return `${prefix}${shortId}`;
902
+ },
903
+ isProviderManagedId(input) {
904
+ return input.includes(":");
905
+ }
906
+ };
907
+ }
908
+
909
+ // src/providers/sanqian/provider.ts
910
+ var SANQIAN_CAPABILITIES = {
911
+ streamingText: true,
912
+ reasoning: true,
913
+ toolCalling: true,
914
+ parallelToolCalls: true,
915
+ toolArgsStreaming: true,
916
+ abort: true,
917
+ hitl: true,
918
+ conversations: true,
919
+ sessionResources: true,
920
+ resourcePicker: true,
921
+ embeddings: true,
922
+ rerank: true
923
+ };
924
+ var SanqianProvider = class {
925
+ id;
926
+ kind = "sanqian-sdk";
927
+ displayName;
928
+ capabilities = SANQIAN_CAPABILITIES;
929
+ sdk;
930
+ appName;
931
+ agentConfigs;
932
+ agentIdResolver;
933
+ disposed = false;
934
+ ready = false;
935
+ connectionStatus;
936
+ connectionError;
937
+ connectionErrorCode;
938
+ connectionLastChangedAt = Date.now();
939
+ connectionListeners = /* @__PURE__ */ new Set();
940
+ handleSdkConnected = () => {
941
+ this.updateConnection("connected");
942
+ };
943
+ handleSdkDisconnected = () => {
944
+ const shouldReconnect = this.ready || this.connectionStatus === "connected" || this.connectionStatus === "reconnecting";
945
+ this.ready = false;
946
+ this.updateConnection(!this.disposed && shouldReconnect ? "reconnecting" : "disconnected");
947
+ };
948
+ handleSdkError = (error) => {
949
+ if (!this.sdk.isConnected()) {
950
+ this.ready = false;
951
+ }
952
+ this.updateConnection("error", normalizeConnectionError(error));
953
+ };
954
+ constructor(config) {
955
+ this.id = config.id ?? "sanqian";
956
+ this.displayName = config.displayName ?? "Sanqian";
957
+ this.sdk = config.sdk;
958
+ this.appName = config.appName;
959
+ this.agentConfigs = config.agents ?? [];
960
+ this.agentIdResolver = createSanqianAgentIdResolver(config.appName);
961
+ this.connectionStatus = this.sdk.isConnected() ? "connected" : "disconnected";
962
+ this.registerConnectionEvents();
963
+ }
964
+ // --- Lifecycle ---
965
+ async ensureReady() {
966
+ if (this.disposed) throw new Error("Provider disposed");
967
+ if (this.ready && this.sdk.isConnected()) return;
968
+ if (this.ready && !this.sdk.isConnected()) {
969
+ this.ready = false;
970
+ }
971
+ this.updateConnection(this.sdk.isConnected() ? "connected" : "connecting");
972
+ try {
973
+ await this.sdk.ensureReady();
974
+ this.updateConnection("connected");
975
+ await this.syncAgents();
976
+ this.ready = true;
977
+ } catch (error) {
978
+ this.ready = false;
979
+ this.updateConnection("error", normalizeConnectionError(error));
980
+ throw error;
981
+ }
982
+ }
983
+ async dispose() {
984
+ if (this.disposed) return;
985
+ this.disposed = true;
986
+ this.ready = false;
987
+ this.agentIdResolver.clearMappings();
988
+ this.unregisterConnectionEvents();
989
+ this.updateConnection("disconnected");
990
+ this.sdk.removeAllListeners();
991
+ await this.sdk.disconnect();
992
+ }
993
+ isDisposed() {
994
+ return this.disposed;
995
+ }
996
+ // --- Agent Sync ---
997
+ async syncAgents() {
998
+ for (const config of this.agentConfigs) {
999
+ const sdkConfig = {
1000
+ agent_id: config.id,
1001
+ name: config.name,
1002
+ description: config.description,
1003
+ system_prompt: config.systemPrompt,
1004
+ tools: config.tools
1005
+ };
1006
+ const result = await this.sdk.createAgent(sdkConfig);
1007
+ this.agentIdResolver.setMapping(config.id, result.agent_id);
1008
+ }
1009
+ }
1010
+ /**
1011
+ * Get the registered Sanqian agent ID for a runtime agent ID.
1012
+ */
1013
+ async resolveAgentId(shortId) {
1014
+ return this.agentIdResolver.toProviderAgentId(shortId);
1015
+ }
1016
+ /**
1017
+ * Get the runtime agent ID for a registered Sanqian agent ID.
1018
+ */
1019
+ normalizeAgentId(registeredId) {
1020
+ return this.agentIdResolver.toRuntimeAgentId(registeredId);
1021
+ }
1022
+ // --- Chat ---
1023
+ async chat(input) {
1024
+ this.ensureNotDisposed();
1025
+ const agentId = await this.resolveAgentId(input.agentId);
1026
+ const messages = input.messages.map((m) => ({ role: m.role, content: m.content }));
1027
+ const response = await this.sdk.chat(agentId, messages, {
1028
+ conversationId: input.conversationId ?? void 0
1029
+ });
1030
+ return {
1031
+ content: response.message.content,
1032
+ conversationId: response.conversationId || void 0,
1033
+ usage: response.usage ? {
1034
+ promptTokens: response.usage.prompt_tokens,
1035
+ completionTokens: response.usage.completion_tokens,
1036
+ totalTokens: response.usage.total_tokens
1037
+ } : void 0
1038
+ };
1039
+ }
1040
+ async *chatStream(input) {
1041
+ this.ensureNotDisposed();
1042
+ const agentId = await this.resolveAgentId(input.agentId);
1043
+ const messages = input.messages.map((m) => ({ role: m.role, content: m.content }));
1044
+ const stream = this.sdk.chatStream(agentId, messages, {
1045
+ conversationId: input.conversationId ?? void 0,
1046
+ signal: input.signal,
1047
+ attachedResources: input.resources?.map((r) => `${r.providerId}:${r.resourceId}`),
1048
+ sessionResources: input.sessionResources?.map((r) => r.id)
1049
+ });
1050
+ for await (const sdkEvent of stream) {
1051
+ if (input.signal?.aborted) {
1052
+ yield { type: "cancelled" };
1053
+ return;
1054
+ }
1055
+ const runtimeEvent = convertSdkStreamEvent(sdkEvent);
1056
+ if (runtimeEvent) {
1057
+ yield runtimeEvent;
1058
+ }
1059
+ }
1060
+ }
1061
+ // --- Cancel / HITL ---
1062
+ async cancel(runId) {
1063
+ this.ensureNotDisposed();
1064
+ this.sdk.cancelRun(runId);
1065
+ }
1066
+ async sendPermissionResponse(input) {
1067
+ this.ensureNotDisposed();
1068
+ this.sdk.sendHitlResponse(input.runId, input.response ?? {
1069
+ approved: input.decision === "approve",
1070
+ cancelled: input.decision === "cancel"
1071
+ });
1072
+ }
1073
+ // --- Conversations ---
1074
+ async listConversations(options) {
1075
+ this.ensureNotDisposed();
1076
+ return this.sdk.listConversations({
1077
+ agentId: options?.agentId,
1078
+ limit: options?.limit,
1079
+ offset: options?.offset
1080
+ });
1081
+ }
1082
+ async getConversation(id, options) {
1083
+ this.ensureNotDisposed();
1084
+ return this.sdk.getConversation(id, options);
1085
+ }
1086
+ async deleteConversation(id) {
1087
+ this.ensureNotDisposed();
1088
+ return this.sdk.deleteConversation(id);
1089
+ }
1090
+ // --- Session Resources ---
1091
+ getSessionResources() {
1092
+ return this.sdk.getSessionResources();
1093
+ }
1094
+ async pushResource(resource) {
1095
+ this.ensureNotDisposed();
1096
+ return this.sdk.pushResource(resource);
1097
+ }
1098
+ async removeResource(resourceId) {
1099
+ this.ensureNotDisposed();
1100
+ return this.sdk.removeResource(resourceId);
1101
+ }
1102
+ // --- Embedding / Rerank ---
1103
+ async getEmbeddingConfig() {
1104
+ try {
1105
+ return await this.sdk.getEmbeddingConfig();
1106
+ } catch {
1107
+ return null;
1108
+ }
1109
+ }
1110
+ async getRerankConfig() {
1111
+ try {
1112
+ return await this.sdk.getRerankConfig();
1113
+ } catch {
1114
+ return null;
1115
+ }
1116
+ }
1117
+ // --- Connection lifecycle ---
1118
+ acquireReconnect() {
1119
+ this.sdk.acquireReconnect();
1120
+ }
1121
+ releaseReconnect() {
1122
+ this.sdk.releaseReconnect();
1123
+ }
1124
+ // --- Connection state ---
1125
+ isConnected() {
1126
+ return this.sdk.isConnected();
1127
+ }
1128
+ getConnectionSnapshot() {
1129
+ return {
1130
+ status: this.connectionStatus,
1131
+ isConnected: this.sdk.isConnected(),
1132
+ error: this.connectionError,
1133
+ errorCode: this.connectionErrorCode,
1134
+ lastChangedAt: this.connectionLastChangedAt
1135
+ };
1136
+ }
1137
+ onConnectionChange(callback) {
1138
+ this.connectionListeners.add(callback);
1139
+ callback(this.getConnectionSnapshot());
1140
+ return () => {
1141
+ this.connectionListeners.delete(callback);
1142
+ };
1143
+ }
1144
+ // --- Events (Sanqian-specific, not on RuntimeProvider interface) ---
1145
+ on(event, handler) {
1146
+ this.sdk.on(event, handler);
1147
+ }
1148
+ off(event, handler) {
1149
+ this.sdk.off(event, handler);
1150
+ }
1151
+ // --- Capability discovery ---
1152
+ async listAvailableAgents() {
1153
+ this.ensureNotDisposed();
1154
+ const sdk = this.sdk;
1155
+ if (typeof sdk.listAvailableAgents === "function") {
1156
+ return sdk.listAvailableAgents();
1157
+ }
1158
+ return [];
1159
+ }
1160
+ // --- Internal ---
1161
+ ensureNotDisposed() {
1162
+ if (this.disposed) throw new Error("Provider disposed");
1163
+ }
1164
+ registerConnectionEvents() {
1165
+ this.sdk.on("connected", this.handleSdkConnected);
1166
+ this.sdk.on("disconnected", this.handleSdkDisconnected);
1167
+ this.sdk.on("error", this.handleSdkError);
1168
+ }
1169
+ unregisterConnectionEvents() {
1170
+ this.sdk.off("connected", this.handleSdkConnected);
1171
+ this.sdk.off("disconnected", this.handleSdkDisconnected);
1172
+ this.sdk.off("error", this.handleSdkError);
1173
+ }
1174
+ updateConnection(status, options = {}) {
1175
+ const nextError = status === "error" ? options.error : void 0;
1176
+ const nextErrorCode = status === "error" ? options.errorCode : void 0;
1177
+ if (this.connectionStatus === status && this.connectionError === nextError && this.connectionErrorCode === nextErrorCode) {
1178
+ return;
1179
+ }
1180
+ this.connectionStatus = status;
1181
+ this.connectionError = nextError;
1182
+ this.connectionErrorCode = nextErrorCode;
1183
+ this.connectionLastChangedAt = Date.now();
1184
+ const snapshot = this.getConnectionSnapshot();
1185
+ for (const listener of this.connectionListeners) {
1186
+ listener(snapshot);
1187
+ }
1188
+ }
1189
+ };
1190
+ function normalizeConnectionError(error) {
1191
+ if (error instanceof Error) {
1192
+ const code = error.code;
1193
+ return {
1194
+ error: error.message || "Connection failed",
1195
+ errorCode: typeof code === "string" && code.trim() ? code : "CONNECTION_FAILED"
1196
+ };
1197
+ }
1198
+ if (error && typeof error === "object") {
1199
+ const record = error;
1200
+ const message = typeof record.message === "string" ? record.message : "Connection failed";
1201
+ const code = typeof record.code === "string" && record.code.trim() ? record.code : "CONNECTION_FAILED";
1202
+ return { error: message, errorCode: code };
1203
+ }
1204
+ if (typeof error === "string" && error.trim()) {
1205
+ return { error, errorCode: "CONNECTION_FAILED" };
1206
+ }
1207
+ return { error: "Connection failed", errorCode: "CONNECTION_FAILED" };
1208
+ }
1209
+
1210
+ // src/providers/sanqian/mock-sdk.ts
1211
+ var MockSanqianSdk = class {
1212
+ appName;
1213
+ connected;
1214
+ readinessError;
1215
+ scenarios;
1216
+ agents = /* @__PURE__ */ new Map();
1217
+ conversations = /* @__PURE__ */ new Map();
1218
+ sessionResources = [];
1219
+ listeners = /* @__PURE__ */ new Map();
1220
+ embeddingConfig;
1221
+ rerankConfig;
1222
+ // Tracking for assertions
1223
+ connectCalls = [];
1224
+ disconnectCalls = [];
1225
+ ensureReadyCalls = [];
1226
+ createAgentCalls = [];
1227
+ chatCalls = [];
1228
+ chatStreamCalls = [];
1229
+ cancelRunCalls = [];
1230
+ hitlResponseCalls = [];
1231
+ constructor(config = {}) {
1232
+ this.appName = config.appName ?? "test-app";
1233
+ this.connected = config.connected ?? false;
1234
+ this.readinessError = config.readinessError ?? null;
1235
+ this.scenarios = config.scenarios ?? [];
1236
+ this.embeddingConfig = config.embeddingConfig ?? { available: false };
1237
+ this.rerankConfig = config.rerankConfig ?? { available: false };
1238
+ }
1239
+ // --- Connection ---
1240
+ async connect() {
1241
+ this.connectCalls.push(Date.now());
1242
+ if (this.readinessError) throw new Error(this.readinessError);
1243
+ this.connected = true;
1244
+ this.emit("connected");
1245
+ }
1246
+ async disconnect() {
1247
+ this.disconnectCalls.push(Date.now());
1248
+ this.connected = false;
1249
+ this.emit("disconnected");
1250
+ }
1251
+ async ensureReady() {
1252
+ this.ensureReadyCalls.push(Date.now());
1253
+ if (this.readinessError) throw new Error(this.readinessError);
1254
+ if (!this.connected) {
1255
+ await this.connect();
1256
+ }
1257
+ }
1258
+ isConnected() {
1259
+ return this.connected;
1260
+ }
1261
+ acquireReconnect() {
1262
+ }
1263
+ releaseReconnect() {
1264
+ }
1265
+ // --- Events ---
1266
+ on(event, handler) {
1267
+ if (!this.listeners.has(event)) {
1268
+ this.listeners.set(event, /* @__PURE__ */ new Set());
1269
+ }
1270
+ this.listeners.get(event).add(handler);
1271
+ }
1272
+ off(event, handler) {
1273
+ this.listeners.get(event)?.delete(handler);
1274
+ }
1275
+ removeAllListeners() {
1276
+ this.listeners.clear();
1277
+ }
1278
+ emit(event, ...args) {
1279
+ const handlers = this.listeners.get(event);
1280
+ if (handlers) {
1281
+ for (const handler of handlers) {
1282
+ handler(...args);
1283
+ }
1284
+ }
1285
+ }
1286
+ // --- Agents ---
1287
+ async createAgent(config) {
1288
+ this.createAgentCalls.push(config);
1289
+ const registeredId = `${this.appName}:${config.agent_id}`;
1290
+ this.agents.set(config.agent_id, { agent_id: registeredId });
1291
+ return { agent_id: registeredId };
1292
+ }
1293
+ // --- Chat ---
1294
+ async chat(agentId, messages, options) {
1295
+ this.chatCalls.push({ agentId, messages, options });
1296
+ const scenario = this.findScenario(agentId);
1297
+ if (scenario?.error) throw new Error(scenario.error);
1298
+ const content = scenario?.chatResponse ?? "Mock response";
1299
+ return {
1300
+ message: { content, role: "assistant" },
1301
+ conversationId: options?.conversationId ?? "conv-mock",
1302
+ usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 }
1303
+ };
1304
+ }
1305
+ async *chatStream(agentId, messages, options) {
1306
+ this.chatStreamCalls.push({ agentId, messages, options });
1307
+ const scenario = this.findScenario(agentId);
1308
+ if (scenario?.error) {
1309
+ yield { type: "error", error: scenario.error };
1310
+ return;
1311
+ }
1312
+ if (scenario?.streamEvents) {
1313
+ for (const event of scenario.streamEvents) {
1314
+ if (options?.signal?.aborted) {
1315
+ yield { type: "cancelled", run_id: "run-mock" };
1316
+ return;
1317
+ }
1318
+ yield event;
1319
+ }
1320
+ return;
1321
+ }
1322
+ if (options?.signal?.aborted) {
1323
+ yield { type: "cancelled", run_id: "run-mock" };
1324
+ return;
1325
+ }
1326
+ yield { type: "start", run_id: "run-mock" };
1327
+ if (options?.signal?.aborted) {
1328
+ yield { type: "cancelled", run_id: "run-mock" };
1329
+ return;
1330
+ }
1331
+ yield { type: "text", content: scenario?.chatResponse ?? "Mock streamed response" };
1332
+ yield { type: "done", conversationId: options?.conversationId ?? "conv-mock" };
1333
+ }
1334
+ // --- Cancel / HITL ---
1335
+ cancelRun(runId) {
1336
+ this.cancelRunCalls.push(runId);
1337
+ }
1338
+ sendHitlResponse(runId, response) {
1339
+ this.hitlResponseCalls.push({ runId, response });
1340
+ }
1341
+ // --- Conversations ---
1342
+ async listConversations(options) {
1343
+ const all = Array.from(this.conversations.values());
1344
+ const filtered = options.agentId ? all.filter((c) => c.agent_id === options.agentId) : all;
1345
+ const offset = options.offset ?? 0;
1346
+ const limit = options.limit ?? 20;
1347
+ return {
1348
+ conversations: filtered.slice(offset, offset + limit),
1349
+ total: filtered.length
1350
+ };
1351
+ }
1352
+ async getConversation(id) {
1353
+ const conv = this.conversations.get(id);
1354
+ if (!conv) throw new Error(`Conversation not found: ${id}`);
1355
+ return conv;
1356
+ }
1357
+ async deleteConversation(id) {
1358
+ this.conversations.delete(id);
1359
+ }
1360
+ /** Test helper: seed a conversation */
1361
+ seedConversation(detail) {
1362
+ this.conversations.set(detail.conversation_id, detail);
1363
+ }
1364
+ // --- Session Resources ---
1365
+ getSessionResources() {
1366
+ return [...this.sessionResources];
1367
+ }
1368
+ async pushResource(resource) {
1369
+ const stored = {
1370
+ ...resource,
1371
+ fullId: `${this.appName}:${resource.id ?? `res-${Date.now()}`}`,
1372
+ appName: this.appName,
1373
+ pushedAt: (/* @__PURE__ */ new Date()).toISOString()
1374
+ };
1375
+ this.sessionResources.push(stored);
1376
+ this.emit("resourcePushed", stored);
1377
+ return stored;
1378
+ }
1379
+ async removeResource(resourceId) {
1380
+ this.sessionResources = this.sessionResources.filter((r) => r.fullId !== resourceId);
1381
+ this.emit("resourceRemoved", resourceId);
1382
+ }
1383
+ async fetchSessionResources() {
1384
+ return [...this.sessionResources];
1385
+ }
1386
+ // --- Embedding / Rerank ---
1387
+ async getEmbeddingConfig() {
1388
+ return this.embeddingConfig;
1389
+ }
1390
+ async getRerankConfig() {
1391
+ return this.rerankConfig;
1392
+ }
1393
+ // --- Helpers ---
1394
+ findScenario(agentId) {
1395
+ return this.scenarios.find((s) => !s.agentId || s.agentId === agentId);
1396
+ }
1397
+ /** Simulate readiness failure for testing rollback */
1398
+ setReadinessError(error) {
1399
+ this.readinessError = error;
1400
+ }
1401
+ /** Force connection state for testing */
1402
+ setConnected(connected) {
1403
+ this.connected = connected;
1404
+ }
1405
+ };
1406
+
1407
+ // src/providers/sanqian/embedding-provider.ts
1408
+ var DEFAULT_BATCH_SIZE = 50;
1409
+ var DEFAULT_TIMEOUT = 3e4;
1410
+ var SanqianEmbeddingProvider = class {
1411
+ id;
1412
+ model;
1413
+ dimensions;
1414
+ config;
1415
+ disposed = false;
1416
+ constructor(config, id) {
1417
+ this.config = config;
1418
+ this.id = id ?? "sanqian-embedding";
1419
+ this.model = config.modelName;
1420
+ this.dimensions = config.dimensions;
1421
+ }
1422
+ async ensureReady() {
1423
+ if (this.disposed) throw new Error("Provider disposed");
1424
+ if (!this.config.apiUrl) throw new Error("Embedding API URL not configured");
1425
+ if (this.config.apiType !== "local" && !this.config.apiKey) {
1426
+ throw new Error("Embedding API Key not configured");
1427
+ }
1428
+ }
1429
+ async dispose() {
1430
+ this.disposed = true;
1431
+ }
1432
+ async embedMany(texts, options) {
1433
+ if (this.disposed) throw new Error("Provider disposed");
1434
+ if (texts.length === 0) return [];
1435
+ if (texts.length <= DEFAULT_BATCH_SIZE) {
1436
+ return this.callApi(texts, options?.signal);
1437
+ }
1438
+ const allEmbeddings = [];
1439
+ for (let i = 0; i < texts.length; i += DEFAULT_BATCH_SIZE) {
1440
+ if (options?.signal?.aborted) throw new Error("Embedding request aborted");
1441
+ const batch = texts.slice(i, i + DEFAULT_BATCH_SIZE);
1442
+ const batchEmbeddings = await this.callApi(batch, options?.signal);
1443
+ allEmbeddings.push(...batchEmbeddings);
1444
+ }
1445
+ return allEmbeddings;
1446
+ }
1447
+ async embed(text, options) {
1448
+ const [result] = await this.embedMany([text], options);
1449
+ if (!result) throw new Error("Failed to generate embedding");
1450
+ return result;
1451
+ }
1452
+ async test() {
1453
+ try {
1454
+ const embedding = await this.embed("Hello, this is a test.");
1455
+ return { success: true, dimensions: embedding.length };
1456
+ } catch (error) {
1457
+ return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
1458
+ }
1459
+ }
1460
+ async callApi(texts, signal) {
1461
+ const { apiUrl, apiKey, modelName, apiType } = this.config;
1462
+ if (apiType === "local") {
1463
+ return this.callOllama(texts, signal);
1464
+ }
1465
+ const headers = {
1466
+ "Content-Type": "application/json",
1467
+ "Authorization": `Bearer ${apiKey}`
1468
+ };
1469
+ const response = await fetch(apiUrl, {
1470
+ method: "POST",
1471
+ headers,
1472
+ body: JSON.stringify({ input: texts, model: modelName }),
1473
+ signal: signal ?? AbortSignal.timeout(DEFAULT_TIMEOUT)
1474
+ });
1475
+ if (!response.ok) {
1476
+ const errorText = await response.text();
1477
+ throw new Error(`Embedding API error: ${response.status} - ${errorText.slice(0, 100)}`);
1478
+ }
1479
+ const data = await response.json();
1480
+ const sorted = data.data.sort((a, b) => a.index - b.index);
1481
+ const embeddings = sorted.map((item) => item.embedding);
1482
+ if (embeddings.length !== texts.length) {
1483
+ throw new Error(`Embedding count mismatch: expected ${texts.length}, got ${embeddings.length}`);
1484
+ }
1485
+ return embeddings;
1486
+ }
1487
+ async callOllama(texts, signal) {
1488
+ const { apiUrl, apiKey, modelName } = this.config;
1489
+ const headers = { "Content-Type": "application/json" };
1490
+ if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
1491
+ const embeddings = [];
1492
+ for (const text of texts) {
1493
+ if (signal?.aborted) throw new Error("Embedding request aborted");
1494
+ const response = await fetch(apiUrl, {
1495
+ method: "POST",
1496
+ headers,
1497
+ body: JSON.stringify({ model: modelName, prompt: text }),
1498
+ signal: signal ?? AbortSignal.timeout(DEFAULT_TIMEOUT)
1499
+ });
1500
+ if (!response.ok) throw new Error(`Ollama API error: ${response.status}`);
1501
+ const data = await response.json();
1502
+ embeddings.push(data.embedding);
1503
+ }
1504
+ return embeddings;
1505
+ }
1506
+ };
1507
+
1508
+ // src/providers/sanqian/rerank-provider.ts
1509
+ var SanqianRerankProvider = class {
1510
+ id;
1511
+ model;
1512
+ config;
1513
+ disposed = false;
1514
+ constructor(config, id) {
1515
+ this.id = id ?? "sanqian-rerank";
1516
+ this.model = config.modelName;
1517
+ this.config = config;
1518
+ }
1519
+ async ensureReady() {
1520
+ if (this.disposed) throw new Error("Provider disposed");
1521
+ if (!this.config.apiUrl) throw new Error("Rerank API URL not configured");
1522
+ if (!this.config.apiKey) throw new Error("Rerank API Key not configured");
1523
+ }
1524
+ async dispose() {
1525
+ this.disposed = true;
1526
+ }
1527
+ async rerank(input, options) {
1528
+ if (this.disposed) throw new Error("Provider disposed");
1529
+ if (input.documents.length === 0) return { results: [] };
1530
+ const { apiUrl, apiKey, modelName } = this.config;
1531
+ const response = await fetch(apiUrl, {
1532
+ method: "POST",
1533
+ headers: {
1534
+ "Authorization": `Bearer ${apiKey}`,
1535
+ "Content-Type": "application/json"
1536
+ },
1537
+ body: JSON.stringify({
1538
+ model: modelName,
1539
+ query: input.query,
1540
+ documents: input.documents.map((d) => d.text),
1541
+ top_n: input.topN ?? input.documents.length,
1542
+ return_documents: false
1543
+ }),
1544
+ signal: options?.signal
1545
+ });
1546
+ if (!response.ok) {
1547
+ const errorText = await response.text();
1548
+ throw new Error(`Rerank API error: ${response.status} - ${errorText.slice(0, 100)}`);
1549
+ }
1550
+ const data = await response.json();
1551
+ if (!data.results?.length) return { results: [] };
1552
+ const results = data.results.filter((r) => r.index >= 0 && r.index < input.documents.length).sort((a, b) => b.relevance_score - a.relevance_score).map((r) => ({
1553
+ id: input.documents[r.index].id,
1554
+ score: r.relevance_score,
1555
+ index: r.index
1556
+ }));
1557
+ return { results };
1558
+ }
1559
+ async test() {
1560
+ try {
1561
+ const result = await this.rerank({
1562
+ query: "test",
1563
+ documents: [{ id: "1", text: "doc one" }, { id: "2", text: "doc two" }],
1564
+ topN: 2
1565
+ });
1566
+ return { success: result.results.length > 0 };
1567
+ } catch (error) {
1568
+ return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
1569
+ }
1570
+ }
1571
+ };
1572
+
1573
+ // src/providers/vercel/provider.ts
1574
+ import { streamText, generateText, stepCountIs } from "ai";
1575
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
1576
+
1577
+ // src/providers/vercel/tool-converter.ts
1578
+ import { z } from "zod";
1579
+ function jsonSchemaToZod(schema) {
1580
+ switch (schema.type) {
1581
+ case "string":
1582
+ return z.string();
1583
+ case "number":
1584
+ return z.number();
1585
+ case "integer":
1586
+ return z.number().int();
1587
+ case "boolean":
1588
+ return z.boolean();
1589
+ case "array": {
1590
+ const items = schema.items;
1591
+ return z.array(items ? jsonSchemaToZod(items) : z.unknown());
1592
+ }
1593
+ case "object":
1594
+ break;
1595
+ }
1596
+ const properties = schema.properties;
1597
+ const required = schema.required ?? [];
1598
+ if (!properties || Object.keys(properties).length === 0) {
1599
+ return z.object({});
1600
+ }
1601
+ const shape = {};
1602
+ for (const [key, prop] of Object.entries(properties)) {
1603
+ let field;
1604
+ switch (prop.type) {
1605
+ case "string":
1606
+ field = z.string();
1607
+ break;
1608
+ case "number":
1609
+ case "integer":
1610
+ field = z.number();
1611
+ break;
1612
+ case "boolean":
1613
+ field = z.boolean();
1614
+ break;
1615
+ case "array": {
1616
+ const items = prop.items;
1617
+ field = z.array(items ? jsonSchemaToZod(items) : z.unknown());
1618
+ break;
1619
+ }
1620
+ case "object":
1621
+ field = jsonSchemaToZod(prop);
1622
+ break;
1623
+ default:
1624
+ field = z.unknown();
1625
+ }
1626
+ if (prop.description) {
1627
+ field = field.describe(prop.description);
1628
+ }
1629
+ if (!required.includes(key)) {
1630
+ field = field.optional();
1631
+ }
1632
+ shape[key] = field;
1633
+ }
1634
+ return z.object(shape);
1635
+ }
1636
+ function convertToolsForVercel(tools, registry, options) {
1637
+ const result = {};
1638
+ for (const tool of tools) {
1639
+ const zodSchema = jsonSchemaToZod(tool.parameters);
1640
+ if (registry) {
1641
+ const reg = registry;
1642
+ result[tool.name] = {
1643
+ type: "function",
1644
+ description: tool.description,
1645
+ inputSchema: zodSchema,
1646
+ execute: async (args) => {
1647
+ const execResult = await reg.execute({
1648
+ toolCallId: `tc-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
1649
+ name: tool.name,
1650
+ args,
1651
+ signal: options?.signal
1652
+ });
1653
+ if (!execResult.success) {
1654
+ throw new Error(execResult.error ?? "Tool execution failed");
1655
+ }
1656
+ return execResult.result;
1657
+ }
1658
+ };
1659
+ } else {
1660
+ result[tool.name] = {
1661
+ type: "function",
1662
+ description: tool.description,
1663
+ inputSchema: zodSchema
1664
+ };
1665
+ }
1666
+ }
1667
+ return result;
1668
+ }
1669
+
1670
+ // src/providers/vercel/provider.ts
1671
+ var VERCEL_CAPABILITIES = {
1672
+ streamingText: true,
1673
+ reasoning: true,
1674
+ toolCalling: true,
1675
+ parallelToolCalls: false,
1676
+ // serialize mutations for safety
1677
+ toolArgsStreaming: false,
1678
+ abort: true,
1679
+ hitl: false,
1680
+ // local permission policy, not backend HITL
1681
+ conversations: false,
1682
+ // needs local ConversationStore
1683
+ sessionResources: false,
1684
+ resourcePicker: false,
1685
+ embeddings: false,
1686
+ rerank: false
1687
+ };
1688
+ var DEFAULT_MAX_STEPS = 5;
1689
+ var TOOL_STEP_LIMIT_ERROR = "Tool step limit reached before the model produced a final response.";
1690
+ var TOOL_STEP_LIMIT_ERROR_CODE = "TOOL_STEP_LIMIT";
1691
+ var VercelProvider = class {
1692
+ id;
1693
+ kind = "vercel-ai-sdk";
1694
+ displayName;
1695
+ capabilities = VERCEL_CAPABILITIES;
1696
+ apiKey;
1697
+ baseURL;
1698
+ modelName;
1699
+ providerName;
1700
+ agentConfigs;
1701
+ toolRegistry;
1702
+ maxSteps;
1703
+ disposed = false;
1704
+ ready = false;
1705
+ constructor(config) {
1706
+ this.id = config.id ?? "vercel";
1707
+ this.displayName = config.displayName ?? config.providerName ?? "Vercel AI";
1708
+ this.apiKey = config.apiKey;
1709
+ this.baseURL = config.baseURL;
1710
+ this.modelName = config.model;
1711
+ this.providerName = config.providerName ?? "openai-compatible";
1712
+ this.toolRegistry = config.toolRegistry ?? null;
1713
+ this.maxSteps = config.maxSteps ?? DEFAULT_MAX_STEPS;
1714
+ this.agentConfigs = /* @__PURE__ */ new Map();
1715
+ for (const agent of config.agents ?? []) {
1716
+ this.agentConfigs.set(agent.id, agent);
1717
+ }
1718
+ }
1719
+ // --- Lifecycle ---
1720
+ async ensureReady() {
1721
+ if (this.disposed) throw new Error("Provider disposed");
1722
+ if (this.ready) return;
1723
+ if (!this.apiKey) throw new Error("API key is required");
1724
+ if (!this.baseURL) throw new Error("Base URL is required");
1725
+ if (!this.modelName) throw new Error("Model name is required");
1726
+ const provider = this.createProvider();
1727
+ try {
1728
+ await generateText({
1729
+ model: provider.chatModel(this.modelName),
1730
+ prompt: "Reply with OK",
1731
+ maxOutputTokens: 32,
1732
+ abortSignal: AbortSignal.timeout(15e3)
1733
+ });
1734
+ } catch (error) {
1735
+ const message = error instanceof Error ? error.message : "Provider readiness check failed";
1736
+ const redacted = message.replace(new RegExp(this.apiKey.slice(0, 8), "g"), "****");
1737
+ throw new Error(`Provider readiness failed: ${redacted}`);
1738
+ }
1739
+ this.ready = true;
1740
+ }
1741
+ async dispose() {
1742
+ this.disposed = true;
1743
+ this.ready = false;
1744
+ }
1745
+ isDisposed() {
1746
+ return this.disposed;
1747
+ }
1748
+ // --- Chat ---
1749
+ async chat(input) {
1750
+ this.ensureNotDisposed();
1751
+ const agentConfig = this.agentConfigs.get(input.agentId);
1752
+ const provider = this.createProvider();
1753
+ const model = provider.chatModel(this.modelName);
1754
+ const { system, userMessages } = this.buildMessages(input.messages, agentConfig);
1755
+ const tools = this.resolveTools(input, agentConfig);
1756
+ const maxSteps = tools ? this.maxSteps : 1;
1757
+ const result = await generateText({
1758
+ model,
1759
+ system,
1760
+ messages: userMessages,
1761
+ tools,
1762
+ stopWhen: stepCountIs(maxSteps),
1763
+ abortSignal: input.signal
1764
+ });
1765
+ const resultWithSteps = result;
1766
+ if (tools && resultWithSteps.finishReason === "tool-calls" && (resultWithSteps.steps?.length ?? 0) >= maxSteps) {
1767
+ throw new Error(TOOL_STEP_LIMIT_ERROR);
1768
+ }
1769
+ const usage = result.usage;
1770
+ return {
1771
+ content: result.text,
1772
+ usage: usage ? {
1773
+ promptTokens: usage.inputTokens ?? 0,
1774
+ completionTokens: usage.outputTokens ?? 0,
1775
+ totalTokens: (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0)
1776
+ } : void 0
1777
+ };
1778
+ }
1779
+ async *chatStream(input) {
1780
+ this.ensureNotDisposed();
1781
+ const agentConfig = this.agentConfigs.get(input.agentId);
1782
+ const provider = this.createProvider();
1783
+ const model = provider.chatModel(this.modelName);
1784
+ const { system, userMessages } = this.buildMessages(input.messages, agentConfig);
1785
+ const tools = this.resolveTools(input, agentConfig);
1786
+ const maxSteps = tools ? this.maxSteps : 1;
1787
+ const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1788
+ yield { type: "start", runId };
1789
+ try {
1790
+ let completedSteps = 0;
1791
+ let finishReason;
1792
+ const result = streamText({
1793
+ model,
1794
+ system,
1795
+ messages: userMessages,
1796
+ tools,
1797
+ stopWhen: stepCountIs(maxSteps),
1798
+ abortSignal: input.signal
1799
+ });
1800
+ for await (const part of result.fullStream) {
1801
+ if (input.signal?.aborted) {
1802
+ yield { type: "cancelled", runId };
1803
+ return;
1804
+ }
1805
+ const partType = part.type;
1806
+ switch (partType) {
1807
+ case "text-delta": {
1808
+ const textPart = part;
1809
+ if (textPart.text) {
1810
+ yield { type: "text", content: textPart.text };
1811
+ }
1812
+ break;
1813
+ }
1814
+ case "reasoning-delta": {
1815
+ const reasoningPart = part;
1816
+ if (reasoningPart.text) {
1817
+ yield { type: "thinking", content: reasoningPart.text };
1818
+ }
1819
+ break;
1820
+ }
1821
+ case "tool-call": {
1822
+ const toolPart = part;
1823
+ yield {
1824
+ type: "tool_call",
1825
+ toolCall: {
1826
+ id: toolPart.toolCallId,
1827
+ name: toolPart.toolName,
1828
+ arguments: JSON.stringify(toolPart.input)
1829
+ }
1830
+ };
1831
+ break;
1832
+ }
1833
+ case "tool-result": {
1834
+ const resultPart = part;
1835
+ yield {
1836
+ type: "tool_result",
1837
+ toolCallId: resultPart.toolCallId,
1838
+ result: resultPart.output,
1839
+ success: true
1840
+ };
1841
+ break;
1842
+ }
1843
+ case "finish-step": {
1844
+ const finishPart = part;
1845
+ completedSteps += 1;
1846
+ finishReason = finishPart.finishReason;
1847
+ break;
1848
+ }
1849
+ case "finish": {
1850
+ const finishPart = part;
1851
+ finishReason = finishPart.finishReason;
1852
+ break;
1853
+ }
1854
+ case "tool-error": {
1855
+ const errorPart = part;
1856
+ const errMsg = errorPart.error instanceof Error ? errorPart.error.message : String(errorPart.error);
1857
+ yield {
1858
+ type: "tool_result",
1859
+ toolCallId: errorPart.id ?? errorPart.toolCallId ?? "",
1860
+ success: false,
1861
+ error: this.redactSecrets(errMsg)
1862
+ };
1863
+ break;
1864
+ }
1865
+ case "error": {
1866
+ const errPart = part;
1867
+ const errorMessage = errPart.error instanceof Error ? errPart.error.message : String(errPart.error ?? "Unknown error");
1868
+ yield { type: "error", error: this.redactSecrets(errorMessage) };
1869
+ return;
1870
+ }
1871
+ // text-start, text-end, reasoning-start, reasoning-end,
1872
+ // tool-input-start, tool-input-end, tool-input-delta,
1873
+ // start-step, source, file - skip
1874
+ default:
1875
+ break;
1876
+ }
1877
+ }
1878
+ if (tools && finishReason === "tool-calls" && completedSteps >= maxSteps) {
1879
+ yield {
1880
+ type: "error",
1881
+ error: TOOL_STEP_LIMIT_ERROR,
1882
+ code: TOOL_STEP_LIMIT_ERROR_CODE
1883
+ };
1884
+ return;
1885
+ }
1886
+ yield { type: "done", conversationId: input.conversationId ?? "" };
1887
+ } catch (error) {
1888
+ if (input.signal?.aborted) {
1889
+ yield { type: "cancelled", runId };
1890
+ return;
1891
+ }
1892
+ const message = error instanceof Error ? error.message : "Stream error";
1893
+ yield { type: "error", error: this.redactSecrets(message) };
1894
+ }
1895
+ }
1896
+ // --- Cancel ---
1897
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1898
+ async cancel(_runId) {
1899
+ }
1900
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1901
+ async sendPermissionResponse(_input) {
1902
+ }
1903
+ // --- Internal ---
1904
+ createProvider() {
1905
+ return createOpenAICompatible({
1906
+ name: this.providerName,
1907
+ apiKey: this.apiKey,
1908
+ baseURL: this.baseURL
1909
+ });
1910
+ }
1911
+ buildMessages(messages, agentConfig) {
1912
+ const systemParts = [];
1913
+ if (agentConfig?.systemPrompt) {
1914
+ systemParts.push(agentConfig.systemPrompt);
1915
+ }
1916
+ const userMessages = [];
1917
+ for (const msg of messages) {
1918
+ if (msg.role === "system") {
1919
+ if (!agentConfig?.systemPrompt || msg.content !== agentConfig.systemPrompt) {
1920
+ systemParts.push(msg.content);
1921
+ }
1922
+ } else {
1923
+ userMessages.push({
1924
+ role: msg.role,
1925
+ content: msg.content
1926
+ });
1927
+ }
1928
+ }
1929
+ return {
1930
+ system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
1931
+ userMessages
1932
+ };
1933
+ }
1934
+ resolveTools(input, agentConfig) {
1935
+ const toolDefs = input.tools;
1936
+ if (toolDefs && toolDefs.length > 0) {
1937
+ return convertToolsForVercel(toolDefs, this.toolRegistry, { signal: input.signal });
1938
+ }
1939
+ if (agentConfig?.tools && agentConfig.tools.length > 0 && this.toolRegistry) {
1940
+ const registeredTools = agentConfig.tools.map((name) => this.toolRegistry.get(name)).filter((t) => t !== void 0).map((t) => t.definition);
1941
+ if (registeredTools.length > 0) {
1942
+ return convertToolsForVercel(registeredTools, this.toolRegistry, { signal: input.signal });
1943
+ }
1944
+ }
1945
+ return void 0;
1946
+ }
1947
+ redactSecrets(message) {
1948
+ if (this.apiKey && this.apiKey.length > 8) {
1949
+ const prefix = this.apiKey.slice(0, 8);
1950
+ return message.replace(new RegExp(prefix, "g"), "****");
1951
+ }
1952
+ return message;
1953
+ }
1954
+ ensureNotDisposed() {
1955
+ if (this.disposed) throw new Error("Provider disposed");
1956
+ }
1957
+ };
1958
+
1959
+ // src/providers/vercel/embedding-provider.ts
1960
+ import { embedMany as aiEmbedMany, embed as aiEmbed } from "ai";
1961
+ import { createOpenAICompatible as createOpenAICompatible2 } from "@ai-sdk/openai-compatible";
1962
+ var VercelEmbeddingProvider = class {
1963
+ id;
1964
+ model;
1965
+ dimensions;
1966
+ config;
1967
+ disposed = false;
1968
+ constructor(config, id) {
1969
+ this.id = id ?? "vercel-embedding";
1970
+ this.model = config.model;
1971
+ this.dimensions = config.dimensions;
1972
+ this.config = config;
1973
+ }
1974
+ async ensureReady() {
1975
+ if (this.disposed) throw new Error("Provider disposed");
1976
+ if (!this.config.apiKey) throw new Error("Embedding API key is required");
1977
+ if (!this.config.baseURL) throw new Error("Embedding base URL is required");
1978
+ try {
1979
+ const provider = this.createProvider();
1980
+ const result = await aiEmbed({
1981
+ model: provider.textEmbeddingModel(this.config.model),
1982
+ value: "test",
1983
+ abortSignal: AbortSignal.timeout(15e3)
1984
+ });
1985
+ if (!result.embedding?.length) throw new Error("Empty embedding result");
1986
+ } catch (error) {
1987
+ const msg = error instanceof Error ? error.message : "Embedding readiness failed";
1988
+ throw new Error(`Embedding provider not ready: ${this.redact(msg)}`);
1989
+ }
1990
+ }
1991
+ async dispose() {
1992
+ this.disposed = true;
1993
+ }
1994
+ async embedMany(texts, options) {
1995
+ if (this.disposed) throw new Error("Provider disposed");
1996
+ if (texts.length === 0) return [];
1997
+ const provider = this.createProvider();
1998
+ const result = await aiEmbedMany({
1999
+ model: provider.textEmbeddingModel(this.config.model),
2000
+ values: texts,
2001
+ abortSignal: options?.signal
2002
+ });
2003
+ return result.embeddings;
2004
+ }
2005
+ async embed(text, options) {
2006
+ if (this.disposed) throw new Error("Provider disposed");
2007
+ const provider = this.createProvider();
2008
+ const result = await aiEmbed({
2009
+ model: provider.textEmbeddingModel(this.config.model),
2010
+ value: text,
2011
+ abortSignal: options?.signal
2012
+ });
2013
+ return result.embedding;
2014
+ }
2015
+ async test() {
2016
+ try {
2017
+ const embedding = await this.embed("Hello, this is a test.");
2018
+ return { success: true, dimensions: embedding.length };
2019
+ } catch (error) {
2020
+ return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
2021
+ }
2022
+ }
2023
+ createProvider() {
2024
+ return createOpenAICompatible2({
2025
+ name: this.config.providerName ?? "embedding-provider",
2026
+ apiKey: this.config.apiKey,
2027
+ baseURL: this.config.baseURL
2028
+ });
2029
+ }
2030
+ redact(message) {
2031
+ if (this.config.apiKey?.length > 8) {
2032
+ return message.replace(new RegExp(this.config.apiKey.slice(0, 8), "g"), "****");
2033
+ }
2034
+ return message;
2035
+ }
2036
+ };
2037
+ export {
2038
+ EMPTY_CAPABILITIES,
2039
+ MockSanqianSdk,
2040
+ SanqianEmbeddingProvider,
2041
+ SanqianProvider,
2042
+ SanqianRerankProvider,
2043
+ VercelEmbeddingProvider,
2044
+ VercelProvider,
2045
+ convertSdkStreamEvent,
2046
+ convertToolsForVercel,
2047
+ createActiveWorkTracker,
2048
+ createCategoryPermissionPolicy,
2049
+ createChatRuntimeBridge,
2050
+ createDefaultPermissionPolicy,
2051
+ createDisabledState,
2052
+ createEmbeddingSession,
2053
+ createMemoryConversationStore,
2054
+ createMemorySecretStore,
2055
+ createOutputOperationsManager,
2056
+ createPermissionGate,
2057
+ createSanqianAgentIdResolver,
2058
+ createSessionManager,
2059
+ createTaskPipeline,
2060
+ createToolRegistry,
2061
+ redactToolError,
2062
+ resolveAiFeaturesFromRuntime,
2063
+ validateOutputContent
2064
+ };
2065
+ //# sourceMappingURL=index.mjs.map