@swarmvaultai/engine 3.7.1 → 3.7.3

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,1690 @@
1
+ // src/providers/registry.ts
2
+ import path5 from "path";
3
+ import { pathToFileURL } from "url";
4
+ import { z as z5 } from "zod";
5
+
6
+ // src/config.ts
7
+ import fs2 from "fs/promises";
8
+ import path2 from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { z as z2 } from "zod";
11
+
12
+ // src/types.ts
13
+ import { z } from "zod";
14
+ var providerCapabilitySchema = z.enum([
15
+ "responses",
16
+ "chat",
17
+ "structured",
18
+ "tools",
19
+ "vision",
20
+ "embeddings",
21
+ "streaming",
22
+ "local",
23
+ "image_generation",
24
+ "audio"
25
+ ]);
26
+ var providerTypeSchema = z.enum([
27
+ "heuristic",
28
+ "openai",
29
+ "ollama",
30
+ "anthropic",
31
+ "gemini",
32
+ "openai-compatible",
33
+ "openrouter",
34
+ "groq",
35
+ "together",
36
+ "xai",
37
+ "cerebras",
38
+ "local-whisper",
39
+ "custom"
40
+ ]);
41
+ var agentTypeSchema = z.enum([
42
+ "codex",
43
+ "claude",
44
+ "cursor",
45
+ "goose",
46
+ "pi",
47
+ "gemini",
48
+ "opencode",
49
+ "aider",
50
+ "copilot",
51
+ "trae",
52
+ "claw",
53
+ "droid",
54
+ "kiro",
55
+ "hermes",
56
+ "antigravity",
57
+ "vscode",
58
+ "amp",
59
+ "augment",
60
+ "adal",
61
+ "bob",
62
+ "cline",
63
+ "codebuddy",
64
+ "command-code",
65
+ "continue",
66
+ "cortex",
67
+ "crush",
68
+ "deepagents",
69
+ "firebender",
70
+ "iflow",
71
+ "junie",
72
+ "kilo-code",
73
+ "kimi",
74
+ "kode",
75
+ "mcpjam",
76
+ "mistral-vibe",
77
+ "mux",
78
+ "neovate",
79
+ "openclaw",
80
+ "openhands",
81
+ "pochi",
82
+ "qoder",
83
+ "qwen-code",
84
+ "replit",
85
+ "roo-code",
86
+ "trae-cn",
87
+ "warp",
88
+ "windsurf",
89
+ "zencoder"
90
+ ]);
91
+ var webSearchProviderTypeSchema = z.enum(["http-json", "custom"]);
92
+
93
+ // src/utils.ts
94
+ import crypto from "crypto";
95
+ import fs from "fs/promises";
96
+ import path from "path";
97
+ function slugify(value) {
98
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "item";
99
+ }
100
+ function sha256(value) {
101
+ return crypto.createHash("sha256").update(value).digest("hex");
102
+ }
103
+ async function ensureDir(dirPath) {
104
+ await fs.mkdir(dirPath, { recursive: true });
105
+ }
106
+ async function fileExists(filePath) {
107
+ try {
108
+ await fs.access(filePath);
109
+ return true;
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+ async function readJsonFile(filePath) {
115
+ try {
116
+ const content = await fs.readFile(filePath, "utf8");
117
+ return JSON.parse(content);
118
+ } catch (error) {
119
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
120
+ return null;
121
+ }
122
+ throw error;
123
+ }
124
+ }
125
+ async function writeJsonFile(filePath, value) {
126
+ await ensureDir(path.dirname(filePath));
127
+ await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}
128
+ `, "utf8");
129
+ }
130
+ async function appendJsonLine(filePath, value) {
131
+ await ensureDir(path.dirname(filePath));
132
+ await fs.appendFile(filePath, `${JSON.stringify(value)}
133
+ `, "utf8");
134
+ }
135
+ async function writeFileIfChanged(filePath, content) {
136
+ await ensureDir(path.dirname(filePath));
137
+ if (await fileExists(filePath)) {
138
+ const existing = await fs.readFile(filePath, "utf8");
139
+ if (existing === content) {
140
+ return false;
141
+ }
142
+ }
143
+ await fs.writeFile(filePath, content, "utf8");
144
+ return true;
145
+ }
146
+ function toPosix(value) {
147
+ return value.split(path.sep).join(path.posix.sep);
148
+ }
149
+ function isPathWithin(rootDir, candidate) {
150
+ const normalizedRoot = path.resolve(rootDir);
151
+ const normalizedCandidate = path.resolve(candidate);
152
+ if (normalizedCandidate === normalizedRoot) {
153
+ return true;
154
+ }
155
+ const withSep = normalizedRoot.endsWith(path.sep) ? normalizedRoot : normalizedRoot + path.sep;
156
+ return normalizedCandidate.startsWith(withSep);
157
+ }
158
+ function firstSentences(value, count = 3) {
159
+ const sentences = value.replace(/\s+/g, " ").split(/(?<=[.!?])\s+/).filter(Boolean);
160
+ return sentences.slice(0, count).join(" ").trim();
161
+ }
162
+ function uniqueBy(items, key) {
163
+ const seen = /* @__PURE__ */ new Set();
164
+ const result = [];
165
+ for (const item of items) {
166
+ const itemKey = key(item);
167
+ if (seen.has(itemKey)) {
168
+ continue;
169
+ }
170
+ seen.add(itemKey);
171
+ result.push(item);
172
+ }
173
+ return result;
174
+ }
175
+ function extractJson(text) {
176
+ const fencedMatch = text.match(/```json\s*([\s\S]*?)```/i);
177
+ if (fencedMatch) {
178
+ return fencedMatch[1].trim();
179
+ }
180
+ const start = text.indexOf("{");
181
+ if (start !== -1) {
182
+ let end = text.lastIndexOf("}");
183
+ while (end > start) {
184
+ const candidate = text.slice(start, end + 1);
185
+ try {
186
+ JSON.parse(candidate);
187
+ return candidate;
188
+ } catch {
189
+ end = text.lastIndexOf("}", end - 1);
190
+ }
191
+ }
192
+ }
193
+ throw new Error("Could not locate JSON object in provider response.");
194
+ }
195
+ function normalizeWhitespace(value) {
196
+ return value.replace(/\s+/g, " ").trim();
197
+ }
198
+ function safeFrontmatter(value) {
199
+ return JSON.parse(JSON.stringify(value));
200
+ }
201
+ function truncate(value, maxLength) {
202
+ if (value.length <= maxLength) {
203
+ return value;
204
+ }
205
+ if (maxLength < 4) {
206
+ return value.slice(0, maxLength);
207
+ }
208
+ return `${value.slice(0, maxLength - 3)}...`;
209
+ }
210
+ async function listFilesRecursive(rootDir) {
211
+ const entries = await fs.readdir(rootDir, { withFileTypes: true }).catch(() => []);
212
+ const files = [];
213
+ for (const entry of entries) {
214
+ const absolutePath = path.join(rootDir, entry.name);
215
+ if (entry.isDirectory()) {
216
+ files.push(...await listFilesRecursive(absolutePath));
217
+ continue;
218
+ }
219
+ if (entry.isFile()) {
220
+ files.push(absolutePath);
221
+ }
222
+ }
223
+ return files;
224
+ }
225
+
226
+ // src/config.ts
227
+ var PRIMARY_CONFIG_FILENAME = "swarmvault.config.json";
228
+ var PRIMARY_SCHEMA_FILENAME = "swarmvault.schema.md";
229
+ var moduleDir = path2.dirname(fileURLToPath(import.meta.url));
230
+ var viewerDistDir = path2.basename(moduleDir) === "src" ? path2.resolve(moduleDir, "../../viewer/dist") : path2.resolve(moduleDir, "viewer");
231
+ var providerConfigSchema = z2.object({
232
+ type: providerTypeSchema,
233
+ model: z2.string().min(1),
234
+ baseUrl: z2.string().url().optional(),
235
+ apiKeyEnv: z2.string().min(1).optional(),
236
+ headers: z2.record(z2.string(), z2.string()).optional(),
237
+ module: z2.string().min(1).optional(),
238
+ capabilities: z2.array(providerCapabilitySchema).optional(),
239
+ apiStyle: z2.enum(["responses", "chat"]).optional(),
240
+ binaryPath: z2.string().min(1).optional(),
241
+ modelPath: z2.string().min(1).optional(),
242
+ extraArgs: z2.array(z2.string()).optional(),
243
+ threads: z2.number().int().positive().optional()
244
+ });
245
+ var sourceClassSchema = z2.enum(["first_party", "third_party", "resource", "generated"]);
246
+ var vaultProfilePresetSchema = z2.enum(["reader", "timeline", "diligence", "thesis"]);
247
+ var vaultDashboardPackSchema = z2.enum(["default", "reader", "diligence"]);
248
+ var guidedSessionModeSchema = z2.enum(["insights_only", "canonical_review"]);
249
+ var neo4jGraphSinkConfigSchema = z2.object({
250
+ uri: z2.string().min(1),
251
+ username: z2.string().min(1),
252
+ passwordEnv: z2.string().min(1),
253
+ database: z2.string().min(1).optional(),
254
+ vaultId: z2.string().min(1).optional(),
255
+ includeClasses: z2.array(sourceClassSchema).optional(),
256
+ batchSize: z2.number().int().positive().optional()
257
+ });
258
+ var scheduleTriggerSchema = z2.object({
259
+ cron: z2.string().min(1).optional(),
260
+ every: z2.string().min(1).optional()
261
+ }).refine((value) => Boolean(value.cron || value.every), {
262
+ message: "Schedule triggers require `cron` or `every`."
263
+ });
264
+ var scheduledTaskSchema = z2.discriminatedUnion("type", [
265
+ z2.object({
266
+ type: z2.literal("compile"),
267
+ approve: z2.boolean().optional()
268
+ }),
269
+ z2.object({
270
+ type: z2.literal("lint"),
271
+ deep: z2.boolean().optional(),
272
+ web: z2.boolean().optional()
273
+ }),
274
+ z2.object({
275
+ type: z2.literal("query"),
276
+ question: z2.string().min(1),
277
+ format: z2.enum(["markdown", "report", "slides", "chart", "image"]).optional(),
278
+ save: z2.boolean().optional()
279
+ }),
280
+ z2.object({
281
+ type: z2.literal("explore"),
282
+ question: z2.string().min(1),
283
+ steps: z2.number().int().positive().optional(),
284
+ format: z2.enum(["markdown", "report", "slides", "chart", "image"]).optional()
285
+ }),
286
+ z2.object({
287
+ type: z2.literal("consolidate"),
288
+ dryRun: z2.boolean().optional()
289
+ })
290
+ ]);
291
+ var roleExecutorConfigSchema = z2.discriminatedUnion("type", [
292
+ z2.object({
293
+ type: z2.literal("provider"),
294
+ provider: z2.string().min(1)
295
+ }),
296
+ z2.object({
297
+ type: z2.literal("command"),
298
+ command: z2.array(z2.string().min(1)).min(1),
299
+ cwd: z2.string().min(1).optional(),
300
+ env: z2.record(z2.string(), z2.string()).optional(),
301
+ timeoutMs: z2.number().int().positive().optional()
302
+ })
303
+ ]);
304
+ var webSearchProviderConfigSchema = z2.object({
305
+ type: webSearchProviderTypeSchema,
306
+ endpoint: z2.string().url().optional(),
307
+ method: z2.enum(["GET", "POST"]).optional(),
308
+ apiKeyEnv: z2.string().min(1).optional(),
309
+ apiKeyHeader: z2.string().min(1).optional(),
310
+ apiKeyPrefix: z2.string().optional(),
311
+ headers: z2.record(z2.string(), z2.string()).optional(),
312
+ queryParam: z2.string().min(1).optional(),
313
+ limitParam: z2.string().min(1).optional(),
314
+ resultsPath: z2.string().min(1).optional(),
315
+ titleField: z2.string().min(1).optional(),
316
+ urlField: z2.string().min(1).optional(),
317
+ snippetField: z2.string().min(1).optional(),
318
+ module: z2.string().min(1).optional()
319
+ });
320
+ var vaultProfileConfigSchema = z2.object({
321
+ presets: z2.array(vaultProfilePresetSchema).default([]),
322
+ dashboardPack: vaultDashboardPackSchema.default("default"),
323
+ guidedSessionMode: guidedSessionModeSchema.default("insights_only"),
324
+ dataviewBlocks: z2.boolean().default(false),
325
+ guidedIngestDefault: z2.boolean().default(false),
326
+ deepLintDefault: z2.boolean().default(false)
327
+ });
328
+ var WORKSPACE_DIR_DEFAULTS = {
329
+ rawDir: "raw",
330
+ wikiDir: "wiki",
331
+ stateDir: "state",
332
+ agentDir: "agent",
333
+ inboxDir: "inbox"
334
+ };
335
+ var SWARMVAULT_OUT_ENV = "SWARMVAULT_OUT";
336
+ var vaultConfigSchema = z2.object({
337
+ workspace: z2.object({
338
+ rawDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.rawDir),
339
+ wikiDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.wikiDir),
340
+ stateDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.stateDir),
341
+ agentDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.agentDir),
342
+ inboxDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.inboxDir)
343
+ }).default(WORKSPACE_DIR_DEFAULTS),
344
+ providers: z2.record(z2.string(), providerConfigSchema),
345
+ tasks: z2.object({
346
+ compileProvider: z2.string().min(1),
347
+ queryProvider: z2.string().min(1),
348
+ lintProvider: z2.string().min(1),
349
+ visionProvider: z2.string().min(1),
350
+ imageProvider: z2.string().min(1).optional(),
351
+ embeddingProvider: z2.string().min(1).optional(),
352
+ audioProvider: z2.string().min(1).optional()
353
+ }),
354
+ viewer: z2.object({
355
+ port: z2.number().int().positive().default(4123)
356
+ }).default({ port: 4123 }),
357
+ profile: vaultProfileConfigSchema.default({
358
+ presets: [],
359
+ dashboardPack: "default",
360
+ guidedSessionMode: "insights_only",
361
+ dataviewBlocks: false,
362
+ guidedIngestDefault: false,
363
+ deepLintDefault: false
364
+ }),
365
+ projects: z2.record(
366
+ z2.string(),
367
+ z2.object({
368
+ roots: z2.array(z2.string().min(1)).min(1),
369
+ schemaPath: z2.string().min(1).optional()
370
+ })
371
+ ).optional(),
372
+ agents: z2.array(agentTypeSchema).default(["codex", "claude", "cursor"]),
373
+ schedules: z2.record(z2.string(), z2.object({ enabled: z2.boolean().optional(), when: scheduleTriggerSchema, task: scheduledTaskSchema })).optional(),
374
+ orchestration: z2.object({
375
+ maxParallelRoles: z2.number().int().positive().optional(),
376
+ compilePostPass: z2.boolean().optional(),
377
+ roles: z2.object({
378
+ research: z2.object({ executor: roleExecutorConfigSchema }).optional(),
379
+ audit: z2.object({ executor: roleExecutorConfigSchema }).optional(),
380
+ context: z2.object({ executor: roleExecutorConfigSchema }).optional(),
381
+ safety: z2.object({ executor: roleExecutorConfigSchema }).optional()
382
+ }).optional()
383
+ }).optional(),
384
+ benchmark: z2.object({
385
+ enabled: z2.boolean().optional(),
386
+ questions: z2.array(z2.string().min(1)).optional(),
387
+ maxQuestions: z2.number().int().positive().optional()
388
+ }).optional(),
389
+ repoAnalysis: z2.object({
390
+ classifyGlobs: z2.partialRecord(sourceClassSchema, z2.array(z2.string().min(1))).optional(),
391
+ extractClasses: z2.array(sourceClassSchema).optional()
392
+ }).optional(),
393
+ graphSinks: z2.object({
394
+ neo4j: neo4jGraphSinkConfigSchema.optional()
395
+ }).optional(),
396
+ graph: z2.object({
397
+ communityResolution: z2.number().positive().optional(),
398
+ similarityIdfFloor: z2.number().min(0).optional(),
399
+ similarityEdgeCap: z2.number().int().positive().optional(),
400
+ godNodeLimit: z2.number().int().positive().optional(),
401
+ foldCommunitiesBelow: z2.number().int().positive().optional()
402
+ }).optional(),
403
+ retrieval: z2.object({
404
+ backend: z2.literal("sqlite").optional(),
405
+ shardSize: z2.number().int().positive().optional(),
406
+ hybrid: z2.boolean().optional(),
407
+ rerank: z2.boolean().optional(),
408
+ embeddingProvider: z2.string().min(1).optional(),
409
+ maxIndexedRows: z2.number().int().positive().optional()
410
+ }).optional(),
411
+ webSearch: z2.object({
412
+ providers: z2.record(z2.string(), webSearchProviderConfigSchema),
413
+ tasks: z2.object({
414
+ deepLintProvider: z2.string().min(1),
415
+ queryProvider: z2.string().min(1).optional(),
416
+ exploreProvider: z2.string().min(1).optional()
417
+ })
418
+ }).optional(),
419
+ search: z2.object({
420
+ hybrid: z2.boolean().optional(),
421
+ rerank: z2.boolean().optional()
422
+ }).optional(),
423
+ autoCommit: z2.boolean().optional(),
424
+ candidate: z2.object({
425
+ autoPromote: z2.object({
426
+ enabled: z2.boolean().default(false),
427
+ minSources: z2.number().int().min(1).default(3),
428
+ minConfidence: z2.number().min(0).max(1).default(0.8),
429
+ minAgreement: z2.number().min(0).max(1).default(0.7),
430
+ minDegree: z2.number().int().min(0).default(2),
431
+ minAgeHours: z2.number().int().min(0).default(24),
432
+ maxPerRun: z2.number().int().positive().default(25),
433
+ dryRun: z2.boolean().default(false)
434
+ }).optional()
435
+ }).optional(),
436
+ redaction: z2.object({
437
+ enabled: z2.boolean().optional(),
438
+ placeholder: z2.string().min(1).optional(),
439
+ useDefaults: z2.boolean().optional(),
440
+ patterns: z2.array(
441
+ z2.object({
442
+ id: z2.string().min(1),
443
+ pattern: z2.string().min(1),
444
+ flags: z2.string().optional(),
445
+ placeholder: z2.string().min(1).optional(),
446
+ description: z2.string().optional()
447
+ })
448
+ ).optional()
449
+ }).optional(),
450
+ freshness: z2.object({
451
+ defaultHalfLifeDays: z2.number().positive().optional(),
452
+ staleThreshold: z2.number().min(0).max(1).optional(),
453
+ halfLifeDaysBySourceClass: z2.object({
454
+ first_party: z2.number().positive().optional(),
455
+ third_party: z2.number().positive().optional(),
456
+ resource: z2.number().positive().optional(),
457
+ generated: z2.number().positive().optional()
458
+ }).optional()
459
+ }).optional(),
460
+ consolidation: z2.object({
461
+ enabled: z2.boolean().optional(),
462
+ workingToEpisodic: z2.object({
463
+ minPages: z2.number().int().min(1).optional(),
464
+ sessionWindowHours: z2.number().positive().optional(),
465
+ minSharedNodeRatio: z2.number().min(0).max(1).optional()
466
+ }).optional(),
467
+ episodicToSemantic: z2.object({
468
+ minOccurrences: z2.number().int().min(1).optional()
469
+ }).optional(),
470
+ semanticToProcedural: z2.object({
471
+ minWorkflowSteps: z2.number().int().min(1).optional()
472
+ }).optional()
473
+ }).optional(),
474
+ watch: z2.object({
475
+ repoRoots: z2.array(z2.string().min(1)).optional(),
476
+ excludeRepoRoots: z2.array(z2.string().min(1)).optional()
477
+ }).optional()
478
+ });
479
+ function normalizeProfilePresets(presets) {
480
+ return [...new Set(presets)];
481
+ }
482
+ function inferDashboardPackFromPresets(presets) {
483
+ if (presets.includes("diligence") && !presets.includes("reader")) {
484
+ return "diligence";
485
+ }
486
+ return presets.length ? "reader" : "default";
487
+ }
488
+ function inferGuidedSessionModeFromPresets(presets) {
489
+ return presets.length ? "canonical_review" : "insights_only";
490
+ }
491
+ function defaultVaultProfileConfig() {
492
+ return {
493
+ presets: [],
494
+ dashboardPack: "default",
495
+ guidedSessionMode: "insights_only",
496
+ dataviewBlocks: false,
497
+ guidedIngestDefault: false,
498
+ deepLintDefault: false
499
+ };
500
+ }
501
+ function personalResearchProfileConfig() {
502
+ return {
503
+ presets: ["reader", "timeline", "thesis"],
504
+ dashboardPack: "reader",
505
+ guidedSessionMode: "canonical_review",
506
+ dataviewBlocks: true,
507
+ guidedIngestDefault: true,
508
+ deepLintDefault: true
509
+ };
510
+ }
511
+ function normalizeVaultProfileConfig(profile) {
512
+ const defaults = defaultVaultProfileConfig();
513
+ const presets = normalizeProfilePresets(profile?.presets ?? defaults.presets);
514
+ return {
515
+ presets,
516
+ dashboardPack: profile?.dashboardPack ?? inferDashboardPackFromPresets(presets),
517
+ guidedSessionMode: profile?.guidedSessionMode ?? inferGuidedSessionModeFromPresets(presets),
518
+ dataviewBlocks: profile?.dataviewBlocks ?? presets.length > 0,
519
+ guidedIngestDefault: profile?.guidedIngestDefault ?? false,
520
+ deepLintDefault: profile?.deepLintDefault ?? false
521
+ };
522
+ }
523
+ function resolveInitProfile(profile) {
524
+ const value = profile?.trim();
525
+ if (!value || value === "default") {
526
+ return {
527
+ alias: "default",
528
+ profile: defaultVaultProfileConfig()
529
+ };
530
+ }
531
+ if (value === "personal-research") {
532
+ return {
533
+ alias: "personal-research",
534
+ profile: personalResearchProfileConfig()
535
+ };
536
+ }
537
+ const presets = normalizeProfilePresets(
538
+ value.split(",").map((item) => item.trim()).filter(Boolean).map((item) => {
539
+ const parsed = vaultProfilePresetSchema.safeParse(item);
540
+ if (!parsed.success) {
541
+ throw new Error(
542
+ `Unknown init profile or preset: ${item}. Use \`default\`, \`personal-research\`, or a comma-separated list of presets: reader,timeline,diligence,thesis.`
543
+ );
544
+ }
545
+ return parsed.data;
546
+ })
547
+ );
548
+ return {
549
+ alias: presets.join(","),
550
+ profile: normalizeVaultProfileConfig({
551
+ presets
552
+ })
553
+ };
554
+ }
555
+ function defaultVaultConfig(profile = defaultVaultProfileConfig()) {
556
+ return {
557
+ workspace: { ...WORKSPACE_DIR_DEFAULTS },
558
+ providers: {
559
+ local: {
560
+ type: "heuristic",
561
+ model: "heuristic-v1",
562
+ capabilities: ["chat", "structured", "vision", "local"]
563
+ }
564
+ },
565
+ tasks: {
566
+ compileProvider: "local",
567
+ queryProvider: "local",
568
+ lintProvider: "local",
569
+ visionProvider: "local",
570
+ imageProvider: "local",
571
+ audioProvider: void 0
572
+ },
573
+ viewer: {
574
+ port: 4123
575
+ },
576
+ profile,
577
+ projects: {},
578
+ agents: ["codex", "claude", "cursor"],
579
+ schedules: {},
580
+ orchestration: {
581
+ maxParallelRoles: 2,
582
+ compilePostPass: false,
583
+ roles: {}
584
+ },
585
+ benchmark: {
586
+ enabled: true,
587
+ questions: [],
588
+ maxQuestions: 3
589
+ },
590
+ repoAnalysis: {
591
+ classifyGlobs: {},
592
+ extractClasses: ["first_party"]
593
+ },
594
+ graphSinks: {},
595
+ retrieval: {
596
+ backend: "sqlite",
597
+ shardSize: 25e3,
598
+ hybrid: true,
599
+ rerank: false
600
+ }
601
+ };
602
+ }
603
+ function defaultVaultSchema(profile = "default") {
604
+ const resolvedProfile = typeof profile === "string" ? resolveInitProfile(profile).profile : normalizeVaultProfileConfig(profile);
605
+ const isResearchProfile = resolvedProfile.presets.length > 0 || resolvedProfile.guidedSessionMode === "canonical_review" || resolvedProfile.dataviewBlocks;
606
+ if (isResearchProfile) {
607
+ const presetLines = [];
608
+ if (resolvedProfile.presets.includes("reader")) {
609
+ presetLines.push("- Keep source pages and source guides optimized for rereading, synthesis, and durable summaries.");
610
+ }
611
+ if (resolvedProfile.presets.includes("timeline")) {
612
+ presetLines.push("- Preserve chronology, dates, and source progression so timeline dashboards stay meaningful.");
613
+ }
614
+ if (resolvedProfile.presets.includes("diligence")) {
615
+ presetLines.push("- Track evidence quality, explicit contradictions, and unresolved judgment calls instead of smoothing them away.");
616
+ }
617
+ if (resolvedProfile.presets.includes("thesis")) {
618
+ presetLines.push("- Maintain explicit thesis, hub, or recurring-question pages that evolve as new evidence arrives.");
619
+ }
620
+ return [
621
+ "# SwarmVault Schema",
622
+ "",
623
+ "Edit this file to teach SwarmVault how this research vault should be organized and maintained.",
624
+ "",
625
+ "## Vault Purpose",
626
+ "",
627
+ "- Track a personal research domain, reading program, or evolving thesis.",
628
+ "- Prefer source-grounded summaries that help you revisit what mattered and what changed your mind.",
629
+ "",
630
+ "## Working Style",
631
+ "",
632
+ "- Favor one-source-at-a-time guided ingest and explicit review before treating a claim as canonical.",
633
+ "- Preserve uncertainty, contradictions, and open questions instead of forcing synthesis too early.",
634
+ "- Save useful summaries, briefs, and source guides back into the wiki so they become durable context.",
635
+ ...resolvedProfile.guidedSessionMode === "canonical_review" ? ["- Stage approval-queued updates to canonical source, concept, and entity pages when the evidence is strong enough."] : ["- Prefer insight pages for exploratory integration until you are ready to promote changes into canonical pages."],
636
+ ...presetLines.length ? ["", "## Profile Emphasis", "", ...presetLines] : [],
637
+ "",
638
+ "## Naming Conventions",
639
+ "",
640
+ "- Prefer stable, descriptive page titles.",
641
+ "- Keep concept, thesis, and entity names specific to the subject area.",
642
+ "- Use source pages for grounded notes, concept/entity pages for accumulated understanding, and outputs for guided integration artifacts.",
643
+ "",
644
+ "## Page Structure Rules",
645
+ "",
646
+ "- Source pages should stay grounded in the original material.",
647
+ "- Concept and entity pages should aggregate source-backed claims instead of inventing new ones.",
648
+ "- Summaries should call out what is new, what is reinforcing, and what is conflicting.",
649
+ "- Preserve contradictions instead of smoothing them away.",
650
+ "",
651
+ "## Categories",
652
+ "",
653
+ "- List domain-specific concept categories here.",
654
+ "- Add thesis pages, recurring themes, or reading tracks that should act as canonical hubs.",
655
+ "",
656
+ "## Relationship Types",
657
+ "",
658
+ "- Mentions",
659
+ "- Supports",
660
+ "- Contradicts",
661
+ "- Builds On",
662
+ "- Questions",
663
+ "",
664
+ "## Dashboard Priorities",
665
+ "",
666
+ "- Recent source guides should surface active reading and ingestion progress.",
667
+ "- Open questions should stay visible until resolved or explicitly archived.",
668
+ "- Contradictions and follow-up sources should be easy to scan from dashboards.",
669
+ ...resolvedProfile.dataviewBlocks ? ["- Keep frontmatter and page titles friendly to Dataview queries, but make every dashboard usable as plain markdown first."] : [],
670
+ ""
671
+ ].join("\n");
672
+ }
673
+ return [
674
+ "# SwarmVault Schema",
675
+ "",
676
+ "Edit this file to teach SwarmVault how this vault should be organized and maintained.",
677
+ "",
678
+ "## Vault Purpose",
679
+ "",
680
+ "- Describe the domain this vault covers.",
681
+ "- Note the intended audience and the kinds of questions the vault should answer well.",
682
+ "",
683
+ "## Naming Conventions",
684
+ "",
685
+ "- Prefer stable, descriptive page titles.",
686
+ "- Keep concept and entity names specific to the domain.",
687
+ "",
688
+ "## Page Structure Rules",
689
+ "",
690
+ "- Source pages should stay grounded in the original material.",
691
+ "- Concept and entity pages should aggregate source-backed claims instead of inventing new ones.",
692
+ "- Preserve contradictions instead of smoothing them away.",
693
+ "",
694
+ "## Categories",
695
+ "",
696
+ "- List domain-specific concept categories here.",
697
+ "- List important entity types here.",
698
+ "",
699
+ "## Relationship Types",
700
+ "",
701
+ "- Mentions",
702
+ "- Supports",
703
+ "- Contradicts",
704
+ "- Depends on",
705
+ "",
706
+ "## Grounding Rules",
707
+ "",
708
+ "- Prefer raw sources over summaries.",
709
+ "- Cite source ids whenever claims are stated.",
710
+ "- Do not treat the wiki as a source of truth when the raw material disagrees.",
711
+ "",
712
+ "## Exclusions",
713
+ "",
714
+ "- List topics, claims, or page types the compiler should avoid generating.",
715
+ ""
716
+ ].join("\n");
717
+ }
718
+ async function findConfigPath(rootDir) {
719
+ const primaryPath = path2.join(rootDir, PRIMARY_CONFIG_FILENAME);
720
+ return primaryPath;
721
+ }
722
+ async function findSchemaPath(rootDir) {
723
+ const primaryPath = path2.join(rootDir, PRIMARY_SCHEMA_FILENAME);
724
+ return primaryPath;
725
+ }
726
+ function resolvePaths(rootDir, config, configPath = path2.join(rootDir, PRIMARY_CONFIG_FILENAME), schemaPath = path2.join(rootDir, PRIMARY_SCHEMA_FILENAME)) {
727
+ const effective = config ?? defaultVaultConfig();
728
+ const artifactRootDir = resolveArtifactRootDir(rootDir);
729
+ const resolveWorkspaceDir = (value) => path2.isAbsolute(value) ? path2.resolve(value) : path2.resolve(artifactRootDir, value);
730
+ const rawDir = resolveWorkspaceDir(effective.workspace.rawDir);
731
+ const rawSourcesDir = path2.join(rawDir, "sources");
732
+ const rawAssetsDir = path2.join(rawDir, "assets");
733
+ const wikiDir = resolveWorkspaceDir(effective.workspace.wikiDir);
734
+ const outputsAssetsDir = path2.join(wikiDir, "outputs", "assets");
735
+ const projectsDir = path2.join(wikiDir, "projects");
736
+ const candidatesDir = path2.join(wikiDir, "candidates");
737
+ const stateDir = resolveWorkspaceDir(effective.workspace.stateDir);
738
+ const retrievalDir = path2.join(stateDir, "retrieval");
739
+ const schedulesDir = path2.join(stateDir, "schedules");
740
+ const watchDir = path2.join(stateDir, "watch");
741
+ const managedSourcesDir = path2.join(stateDir, "sources");
742
+ const agentDir = resolveWorkspaceDir(effective.workspace.agentDir);
743
+ const inboxDir = resolveWorkspaceDir(effective.workspace.inboxDir);
744
+ return {
745
+ rootDir,
746
+ artifactRootDir,
747
+ schemaPath,
748
+ rawDir,
749
+ rawSourcesDir,
750
+ rawAssetsDir,
751
+ wikiDir,
752
+ outputsAssetsDir,
753
+ projectsDir,
754
+ candidatesDir,
755
+ candidateConceptsDir: path2.join(candidatesDir, "concepts"),
756
+ candidateEntitiesDir: path2.join(candidatesDir, "entities"),
757
+ stateDir,
758
+ retrievalDir,
759
+ retrievalManifestPath: path2.join(retrievalDir, "manifest.json"),
760
+ schedulesDir,
761
+ agentDir,
762
+ inboxDir,
763
+ manifestsDir: path2.join(stateDir, "manifests"),
764
+ extractsDir: path2.join(stateDir, "extracts"),
765
+ analysesDir: path2.join(stateDir, "analyses"),
766
+ viewerDistDir,
767
+ graphPath: path2.join(stateDir, "graph.json"),
768
+ searchDbPath: path2.join(retrievalDir, "fts-000.sqlite"),
769
+ compileStatePath: path2.join(stateDir, "compile-state.json"),
770
+ codeIndexPath: path2.join(stateDir, "code-index.json"),
771
+ embeddingsPath: path2.join(stateDir, "embeddings.json"),
772
+ benchmarkPath: path2.join(stateDir, "benchmark.json"),
773
+ jobsLogPath: path2.join(stateDir, "jobs.ndjson"),
774
+ sessionsDir: path2.join(stateDir, "sessions"),
775
+ sourceSessionsDir: path2.join(stateDir, "source-sessions"),
776
+ approvalsDir: path2.join(stateDir, "approvals"),
777
+ watchDir,
778
+ watchStatusPath: path2.join(watchDir, "status.json"),
779
+ pendingSemanticRefreshPath: path2.join(watchDir, "pending-semantic-refresh.json"),
780
+ managedSourcesPath: path2.join(stateDir, "sources.json"),
781
+ managedSourcesDir,
782
+ configPath
783
+ };
784
+ }
785
+ function resolveArtifactRootDir(rootDir) {
786
+ const override = process.env[SWARMVAULT_OUT_ENV]?.trim();
787
+ if (!override) {
788
+ return path2.resolve(rootDir);
789
+ }
790
+ return path2.isAbsolute(override) ? path2.resolve(override) : path2.resolve(rootDir, override);
791
+ }
792
+ async function loadVaultConfig(rootDir) {
793
+ const configPath = await findConfigPath(rootDir);
794
+ const schemaPath = await findSchemaPath(rootDir);
795
+ const raw = await readJsonFile(configPath);
796
+ const parsed = vaultConfigSchema.parse(raw ?? defaultVaultConfig());
797
+ const config = {
798
+ ...parsed,
799
+ profile: normalizeVaultProfileConfig(parsed.profile)
800
+ };
801
+ return {
802
+ config,
803
+ paths: resolvePaths(rootDir, config, configPath, schemaPath)
804
+ };
805
+ }
806
+ async function initWorkspace(rootDir, options = {}) {
807
+ const configPath = await findConfigPath(rootDir);
808
+ const schemaPath = await findSchemaPath(rootDir);
809
+ const initProfile = resolveInitProfile(options.profile);
810
+ const config = await fileExists(configPath) ? (await loadVaultConfig(rootDir)).config : defaultVaultConfig(initProfile.profile);
811
+ const paths = resolvePaths(rootDir, config, configPath, schemaPath);
812
+ const primarySchemaPath = path2.join(rootDir, PRIMARY_SCHEMA_FILENAME);
813
+ await Promise.all([
814
+ ensureDir(paths.rawDir),
815
+ ensureDir(paths.wikiDir),
816
+ ensureDir(paths.outputsAssetsDir),
817
+ ensureDir(paths.projectsDir),
818
+ ensureDir(paths.candidatesDir),
819
+ ensureDir(paths.candidateConceptsDir),
820
+ ensureDir(paths.candidateEntitiesDir),
821
+ ensureDir(paths.stateDir),
822
+ ensureDir(paths.retrievalDir),
823
+ ensureDir(paths.schedulesDir),
824
+ ensureDir(paths.watchDir),
825
+ ensureDir(paths.managedSourcesDir),
826
+ ensureDir(paths.sessionsDir),
827
+ ensureDir(paths.sourceSessionsDir),
828
+ ensureDir(paths.approvalsDir),
829
+ ensureDir(paths.agentDir),
830
+ ensureDir(paths.inboxDir),
831
+ ensureDir(paths.manifestsDir),
832
+ ensureDir(paths.extractsDir),
833
+ ensureDir(paths.analysesDir),
834
+ ensureDir(paths.rawSourcesDir),
835
+ ensureDir(paths.rawAssetsDir)
836
+ ]);
837
+ if (!await fileExists(configPath)) {
838
+ await writeJsonFile(configPath, config);
839
+ }
840
+ if (!await fileExists(primarySchemaPath)) {
841
+ await ensureDir(path2.dirname(primarySchemaPath));
842
+ await fs2.writeFile(primarySchemaPath, defaultVaultSchema(config.profile), "utf8");
843
+ }
844
+ return { config, paths };
845
+ }
846
+
847
+ // src/providers/base.ts
848
+ import fs3 from "fs/promises";
849
+ import { z as z3 } from "zod";
850
+ var BaseProviderAdapter = class {
851
+ constructor(id, type, model, capabilities) {
852
+ this.id = id;
853
+ this.type = type;
854
+ this.model = model;
855
+ this.capabilities = new Set(capabilities);
856
+ }
857
+ id;
858
+ type;
859
+ model;
860
+ capabilities;
861
+ async embedTexts(_texts) {
862
+ throw new Error(`Provider ${this.id} does not support embeddings.`);
863
+ }
864
+ async generateImage(_request) {
865
+ throw new Error(`Provider ${this.id} does not support image generation.`);
866
+ }
867
+ async transcribeAudio(_request) {
868
+ throw new Error(`Provider ${this.id} does not support audio transcription.`);
869
+ }
870
+ async generateStructured(request, schema) {
871
+ const schemaDescription = JSON.stringify(z3.toJSONSchema(schema), null, 2);
872
+ const response = await this.generateText({
873
+ ...request,
874
+ prompt: `${request.prompt}
875
+
876
+ Return JSON only. Follow this JSON Schema exactly:
877
+ ${schemaDescription}`
878
+ });
879
+ const parsed = JSON.parse(extractJson(response.text));
880
+ return schema.parse(parsed);
881
+ }
882
+ async encodeAttachments(attachments = []) {
883
+ return Promise.all(
884
+ attachments.map(async (attachment) => ({
885
+ mimeType: attachment.mimeType,
886
+ base64: await fs3.readFile(attachment.filePath, "base64")
887
+ }))
888
+ );
889
+ }
890
+ };
891
+
892
+ // src/providers/anthropic.ts
893
+ var AnthropicProviderAdapter = class extends BaseProviderAdapter {
894
+ apiKey;
895
+ headers;
896
+ baseUrl;
897
+ constructor(id, model, options) {
898
+ super(id, "anthropic", model, ["chat", "structured", "tools", "vision", "streaming"]);
899
+ this.apiKey = options.apiKey;
900
+ this.headers = options.headers;
901
+ this.baseUrl = (options.baseUrl ?? "https://api.anthropic.com").replace(/\/+$/, "");
902
+ }
903
+ async generateText(request) {
904
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
905
+ const content = [
906
+ { type: "text", text: request.prompt },
907
+ ...encodedAttachments.map((item) => ({
908
+ type: "image",
909
+ source: {
910
+ type: "base64",
911
+ media_type: item.mimeType,
912
+ data: item.base64
913
+ }
914
+ }))
915
+ ];
916
+ const response = await fetch(`${this.baseUrl}/v1/messages`, {
917
+ method: "POST",
918
+ headers: {
919
+ "content-type": "application/json",
920
+ "anthropic-version": "2023-06-01",
921
+ ...this.apiKey ? { "x-api-key": this.apiKey } : {},
922
+ ...this.headers
923
+ },
924
+ body: JSON.stringify({
925
+ model: this.model,
926
+ max_tokens: request.maxOutputTokens ?? 1200,
927
+ system: request.system,
928
+ messages: [
929
+ {
930
+ role: "user",
931
+ content
932
+ }
933
+ ]
934
+ })
935
+ });
936
+ if (!response.ok) {
937
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
938
+ }
939
+ const payload = await response.json();
940
+ return {
941
+ text: payload.content?.filter((item) => item.type === "text").map((item) => item.text ?? "").join("\n") ?? "",
942
+ usage: payload.usage ? { inputTokens: payload.usage.input_tokens, outputTokens: payload.usage.output_tokens } : void 0
943
+ };
944
+ }
945
+ };
946
+
947
+ // src/providers/gemini.ts
948
+ var GeminiProviderAdapter = class extends BaseProviderAdapter {
949
+ apiKey;
950
+ baseUrl;
951
+ constructor(id, model, options) {
952
+ super(id, "gemini", model, ["chat", "structured", "vision", "tools", "streaming"]);
953
+ this.apiKey = options.apiKey;
954
+ this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(/\/+$/, "");
955
+ }
956
+ async generateText(request) {
957
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
958
+ const parts = [
959
+ ...request.system ? [{ text: `System instructions:
960
+ ${request.system}` }] : [],
961
+ { text: request.prompt },
962
+ ...encodedAttachments.map((item) => ({
963
+ inline_data: {
964
+ mime_type: item.mimeType,
965
+ data: item.base64
966
+ }
967
+ }))
968
+ ];
969
+ const response = await fetch(`${this.baseUrl}/models/${this.model}:generateContent`, {
970
+ method: "POST",
971
+ headers: {
972
+ "content-type": "application/json",
973
+ ...this.apiKey ? { "x-goog-api-key": this.apiKey } : {}
974
+ },
975
+ body: JSON.stringify({
976
+ contents: [
977
+ {
978
+ role: "user",
979
+ parts
980
+ }
981
+ ],
982
+ generationConfig: {
983
+ maxOutputTokens: request.maxOutputTokens ?? 1200
984
+ }
985
+ })
986
+ });
987
+ if (!response.ok) {
988
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
989
+ }
990
+ const payload = await response.json();
991
+ const text = payload.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("\n") ?? "";
992
+ return {
993
+ text,
994
+ usage: payload.usageMetadata ? { inputTokens: payload.usageMetadata.promptTokenCount, outputTokens: payload.usageMetadata.candidatesTokenCount } : void 0
995
+ };
996
+ }
997
+ };
998
+
999
+ // src/providers/heuristic.ts
1000
+ function summarizePrompt(prompt) {
1001
+ const cleaned = normalizeWhitespace(prompt);
1002
+ if (!cleaned) {
1003
+ return "No prompt content provided.";
1004
+ }
1005
+ return firstSentences(cleaned, 2) || cleaned.slice(0, 280);
1006
+ }
1007
+ var HeuristicProviderAdapter = class extends BaseProviderAdapter {
1008
+ constructor(id, model) {
1009
+ super(id, "heuristic", model, ["chat", "structured", "vision", "local"]);
1010
+ }
1011
+ async generateText(request) {
1012
+ const attachmentHint = request.attachments?.length ? ` Attachments: ${request.attachments.length}.` : "";
1013
+ return {
1014
+ text: `Heuristic provider response.${attachmentHint} ${summarizePrompt(request.prompt)}`.trim()
1015
+ };
1016
+ }
1017
+ };
1018
+
1019
+ // src/providers/local-whisper.ts
1020
+ import { spawn } from "child_process";
1021
+ import { constants as fsConstants } from "fs";
1022
+ import fs4 from "fs/promises";
1023
+ import os from "os";
1024
+ import path3 from "path";
1025
+ var DEFAULT_MODEL = "base.en";
1026
+ var BINARY_CANDIDATES = ["whisper-cli", "whisper-cpp", "whisper"];
1027
+ var MIME_TO_EXT = {
1028
+ "audio/wav": "wav",
1029
+ "audio/x-wav": "wav",
1030
+ "audio/wave": "wav",
1031
+ "audio/mpeg": "mp3",
1032
+ "audio/mp3": "mp3",
1033
+ "audio/mp4": "m4a",
1034
+ "audio/x-m4a": "m4a",
1035
+ "audio/flac": "flac",
1036
+ "audio/x-flac": "flac",
1037
+ "audio/ogg": "ogg",
1038
+ "audio/webm": "webm"
1039
+ };
1040
+ var LocalWhisperProviderAdapter = class extends BaseProviderAdapter {
1041
+ options;
1042
+ constructor(id, model, options = {}) {
1043
+ super(id, "local-whisper", model, ["audio", "local"]);
1044
+ this.options = options;
1045
+ }
1046
+ async generateText(_request) {
1047
+ throw new Error(`Provider ${this.id} (local-whisper) only supports audio transcription.`);
1048
+ }
1049
+ async transcribeAudio(request) {
1050
+ const binaryPath = await this.resolveBinaryPath();
1051
+ const modelPath = await this.resolveModelPath();
1052
+ const tmpDir = this.options.tmpDir ?? os.tmpdir();
1053
+ const extension = this.extensionForRequest(request);
1054
+ const stem = `swarmvault-whisper-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
1055
+ const audioPath = path3.join(tmpDir, `${stem}.${extension}`);
1056
+ await fs4.writeFile(audioPath, request.bytes);
1057
+ const args = ["-m", modelPath, "-f", audioPath, "-nt"];
1058
+ if (request.corpusHint) {
1059
+ args.push("--prompt", request.corpusHint);
1060
+ }
1061
+ if (this.options.threads !== void 0) {
1062
+ args.push("-t", String(this.options.threads));
1063
+ }
1064
+ if (request.language) {
1065
+ args.push("-l", request.language);
1066
+ }
1067
+ if (this.options.extraArgs?.length) {
1068
+ args.push(...this.options.extraArgs);
1069
+ }
1070
+ const runner = this.options.runner ?? defaultWhisperRunner;
1071
+ try {
1072
+ const result = await runner({ binaryPath, args });
1073
+ if (result.code !== 0) {
1074
+ const tail = truncate2(result.stderr.trim() || result.stdout.trim(), 240);
1075
+ throw new Error(`whisper.cpp exited with code ${result.code}: ${tail}`);
1076
+ }
1077
+ return { text: normalizeTranscript(result.stdout) };
1078
+ } finally {
1079
+ await fs4.unlink(audioPath).catch(() => void 0);
1080
+ }
1081
+ }
1082
+ extensionForRequest(request) {
1083
+ const fromFile = request.fileName ? path3.extname(request.fileName).slice(1).toLowerCase() : "";
1084
+ if (fromFile) return fromFile;
1085
+ return MIME_TO_EXT[request.mimeType.toLowerCase()] ?? "wav";
1086
+ }
1087
+ async resolveBinaryPath() {
1088
+ if (this.options.binaryPath) return this.options.binaryPath;
1089
+ const env = this.options.env ?? process.env;
1090
+ if (env.SWARMVAULT_WHISPER_BINARY) return env.SWARMVAULT_WHISPER_BINARY;
1091
+ const pathValue = env.PATH ?? "";
1092
+ for (const dir of pathValue.split(path3.delimiter)) {
1093
+ if (!dir) continue;
1094
+ for (const candidate of BINARY_CANDIDATES) {
1095
+ const full = path3.join(dir, candidate);
1096
+ if (await isExecutable(full)) return full;
1097
+ }
1098
+ }
1099
+ throw new Error(
1100
+ 'Local whisper binary not found. Install whisper.cpp (e.g. "brew install whisper-cpp" or "apt install whisper.cpp") or set "localWhisper.binaryPath" in swarmvault.config.json.'
1101
+ );
1102
+ }
1103
+ async resolveModelPath() {
1104
+ if (this.options.modelPath) return this.options.modelPath;
1105
+ const home = this.options.homeDir ?? (this.options.env ?? process.env).HOME ?? os.homedir();
1106
+ const modelName = this.options.model ?? this.model ?? DEFAULT_MODEL;
1107
+ const candidate = path3.join(home, ".swarmvault", "models", `ggml-${modelName}.bin`);
1108
+ if (await fileExists2(candidate)) return candidate;
1109
+ throw new Error(
1110
+ `Whisper model "${modelName}" not found at ${candidate}. Run "swarmvault provider setup --local-whisper" to download it.`
1111
+ );
1112
+ }
1113
+ };
1114
+ var defaultWhisperRunner = ({ binaryPath, args }) => new Promise((resolve, reject) => {
1115
+ const child = spawn(binaryPath, args, { stdio: ["ignore", "pipe", "pipe"] });
1116
+ let stdout = "";
1117
+ let stderr = "";
1118
+ child.stdout.on("data", (chunk) => {
1119
+ stdout += chunk.toString("utf8");
1120
+ });
1121
+ child.stderr.on("data", (chunk) => {
1122
+ stderr += chunk.toString("utf8");
1123
+ });
1124
+ child.on("error", reject);
1125
+ child.on("close", (code) => {
1126
+ resolve({ code: code ?? 0, stdout, stderr });
1127
+ });
1128
+ });
1129
+ function normalizeTranscript(stdout) {
1130
+ return stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join(" ").replace(/\s+/g, " ").trim();
1131
+ }
1132
+ async function fileExists2(p) {
1133
+ try {
1134
+ await fs4.access(p);
1135
+ return true;
1136
+ } catch {
1137
+ return false;
1138
+ }
1139
+ }
1140
+ async function isExecutable(p) {
1141
+ try {
1142
+ await fs4.access(p, fsConstants.X_OK);
1143
+ return true;
1144
+ } catch {
1145
+ return false;
1146
+ }
1147
+ }
1148
+ function truncate2(text, maxLen) {
1149
+ if (text.length <= maxLen) return text;
1150
+ return `${text.slice(0, maxLen - 1)}\u2026`;
1151
+ }
1152
+
1153
+ // src/providers/openai-compatible.ts
1154
+ import path4 from "path";
1155
+ import { z as z4 } from "zod";
1156
+ function buildAuthHeaders(apiKey) {
1157
+ return apiKey ? { Authorization: `Bearer ${apiKey}` } : {};
1158
+ }
1159
+ function extractResponsesText(payload) {
1160
+ if (typeof payload.output_text === "string" && payload.output_text.trim()) {
1161
+ return payload.output_text;
1162
+ }
1163
+ return payload.output?.flatMap((item) => item.content ?? []).filter((item) => item.type === "output_text" && typeof item.text === "string").map((item) => item.text ?? "").join("\n").trim() ?? "";
1164
+ }
1165
+ function isJsonSchemaObject(value) {
1166
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1167
+ }
1168
+ function allowNullInSchema(schema) {
1169
+ if (Array.isArray(schema.type)) {
1170
+ return schema.type.includes("null") ? schema : { ...schema, type: [...schema.type, "null"] };
1171
+ }
1172
+ if (typeof schema.type === "string") {
1173
+ return schema.type === "null" ? schema : { ...schema, type: [schema.type, "null"] };
1174
+ }
1175
+ if (Array.isArray(schema.enum)) {
1176
+ return schema.enum.includes(null) ? schema : { ...schema, enum: [...schema.enum, null] };
1177
+ }
1178
+ if (Array.isArray(schema.anyOf)) {
1179
+ return schema.anyOf.some((item) => isJsonSchemaObject(item) && item.type === "null") ? schema : { ...schema, anyOf: [...schema.anyOf, { type: "null" }] };
1180
+ }
1181
+ return { anyOf: [schema, { type: "null" }] };
1182
+ }
1183
+ function toOpenAiStrictJsonSchema(schema) {
1184
+ if (Array.isArray(schema)) {
1185
+ return schema.map((item) => toOpenAiStrictJsonSchema(item));
1186
+ }
1187
+ if (!isJsonSchemaObject(schema)) {
1188
+ return schema;
1189
+ }
1190
+ const normalizedEntries = Object.entries(schema).filter(([key]) => key !== "$schema").map(([key, value]) => [key, toOpenAiStrictJsonSchema(value)]);
1191
+ const normalizedSchema = Object.fromEntries(normalizedEntries);
1192
+ if (isJsonSchemaObject(normalizedSchema.properties)) {
1193
+ const properties = normalizedSchema.properties;
1194
+ const originalRequired = Array.isArray(normalizedSchema.required) ? normalizedSchema.required.filter((item) => typeof item === "string") : [];
1195
+ const requiredSet = new Set(originalRequired);
1196
+ const propertyEntries = Object.entries(properties).map(([key, value]) => {
1197
+ const normalizedProperty = isJsonSchemaObject(value) ? value : {};
1198
+ return [key, requiredSet.has(key) ? normalizedProperty : allowNullInSchema(normalizedProperty)];
1199
+ });
1200
+ return {
1201
+ ...normalizedSchema,
1202
+ properties: Object.fromEntries(propertyEntries),
1203
+ required: Object.keys(properties),
1204
+ additionalProperties: false
1205
+ };
1206
+ }
1207
+ return normalizedSchema;
1208
+ }
1209
+ function stripNullObjectProperties(value) {
1210
+ if (Array.isArray(value)) {
1211
+ return value.map((item) => stripNullObjectProperties(item));
1212
+ }
1213
+ if (!isJsonSchemaObject(value)) {
1214
+ return value;
1215
+ }
1216
+ const entries = Object.entries(value).filter(([, item]) => item !== null).map(([key, item]) => [key, stripNullObjectProperties(item)]);
1217
+ return Object.fromEntries(entries);
1218
+ }
1219
+ function buildStructuredFormat(schema) {
1220
+ return {
1221
+ type: "json_schema",
1222
+ name: "swarmvault_response",
1223
+ schema: toOpenAiStrictJsonSchema(z4.toJSONSchema(schema)),
1224
+ strict: true
1225
+ };
1226
+ }
1227
+ var OpenAiCompatibleProviderAdapter = class extends BaseProviderAdapter {
1228
+ baseUrl;
1229
+ apiKey;
1230
+ headers;
1231
+ apiStyle;
1232
+ constructor(id, type, model, options) {
1233
+ super(id, type, model, options.capabilities);
1234
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
1235
+ this.apiKey = options.apiKey;
1236
+ this.headers = options.headers;
1237
+ this.apiStyle = options.apiStyle ?? "responses";
1238
+ }
1239
+ async generateText(request) {
1240
+ if (this.apiStyle === "chat") {
1241
+ return this.generateViaChatCompletions(request);
1242
+ }
1243
+ return this.generateViaResponses(request);
1244
+ }
1245
+ async generateStructured(request, schema) {
1246
+ if (this.type !== "openai") {
1247
+ return super.generateStructured(request, schema);
1248
+ }
1249
+ const structuredFormat = buildStructuredFormat(schema);
1250
+ const text = this.apiStyle === "chat" ? await this.generateStructuredViaChatCompletions(
1251
+ {
1252
+ ...request
1253
+ },
1254
+ structuredFormat
1255
+ ) : await this.generateStructuredViaResponses(
1256
+ {
1257
+ ...request
1258
+ },
1259
+ structuredFormat
1260
+ );
1261
+ return schema.parse(stripNullObjectProperties(JSON.parse(extractJson(text))));
1262
+ }
1263
+ async embedTexts(texts) {
1264
+ if (!texts.length) {
1265
+ return [];
1266
+ }
1267
+ const response = await fetch(`${this.baseUrl}/embeddings`, {
1268
+ method: "POST",
1269
+ headers: {
1270
+ "content-type": "application/json",
1271
+ ...buildAuthHeaders(this.apiKey),
1272
+ ...this.headers
1273
+ },
1274
+ body: JSON.stringify({
1275
+ model: this.model,
1276
+ input: texts
1277
+ })
1278
+ });
1279
+ if (!response.ok) {
1280
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1281
+ }
1282
+ const payload = await response.json();
1283
+ const vectors = payload.data?.map((item) => item.embedding ?? []) ?? [];
1284
+ if (vectors.length !== texts.length || vectors.some((vector) => !Array.isArray(vector) || vector.length === 0)) {
1285
+ throw new Error(`Provider ${this.id} returned invalid embedding data.`);
1286
+ }
1287
+ return vectors;
1288
+ }
1289
+ async generateImage(request) {
1290
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
1291
+ const response = await fetch(`${this.baseUrl}/images/generations`, {
1292
+ method: "POST",
1293
+ headers: {
1294
+ "content-type": "application/json",
1295
+ ...buildAuthHeaders(this.apiKey),
1296
+ ...this.headers
1297
+ },
1298
+ body: JSON.stringify({
1299
+ model: this.model,
1300
+ prompt: request.prompt,
1301
+ size: request.width && request.height ? `${Math.max(256, Math.round(request.width))}x${Math.max(256, Math.round(request.height))}` : void 0,
1302
+ response_format: "b64_json",
1303
+ ...encodedAttachments.length ? {
1304
+ input_image: encodedAttachments.map((item) => `data:${item.mimeType};base64,${item.base64}`)
1305
+ } : {}
1306
+ })
1307
+ });
1308
+ if (!response.ok) {
1309
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1310
+ }
1311
+ const payload = await response.json();
1312
+ const image = payload.data?.[0];
1313
+ if (!image?.b64_json) {
1314
+ throw new Error(`Provider ${this.id} returned no image data.`);
1315
+ }
1316
+ return {
1317
+ mimeType: "image/png",
1318
+ bytes: Buffer.from(image.b64_json, "base64"),
1319
+ width: request.width,
1320
+ height: request.height,
1321
+ revisedPrompt: image.revised_prompt
1322
+ };
1323
+ }
1324
+ async transcribeAudio(request) {
1325
+ const extension = request.mimeType.split("/")[1]?.split("+")[0] ?? "bin";
1326
+ const fileName = request.fileName ?? `audio.${extension}`;
1327
+ const formData = new FormData();
1328
+ formData.append("file", new File([new Uint8Array(request.bytes)], path4.basename(fileName), { type: request.mimeType }));
1329
+ formData.append("model", this.model);
1330
+ formData.append("response_format", "verbose_json");
1331
+ if (request.language) {
1332
+ formData.append("language", request.language);
1333
+ }
1334
+ if (request.corpusHint) {
1335
+ formData.append("prompt", request.corpusHint);
1336
+ }
1337
+ const response = await fetch(`${this.baseUrl}/audio/transcriptions`, {
1338
+ method: "POST",
1339
+ headers: {
1340
+ ...buildAuthHeaders(this.apiKey),
1341
+ ...this.headers
1342
+ },
1343
+ body: formData
1344
+ });
1345
+ if (!response.ok) {
1346
+ throw new Error(`Provider ${this.id} audio transcription failed: ${response.status} ${response.statusText}`);
1347
+ }
1348
+ const payload = await response.json();
1349
+ return {
1350
+ text: payload.text ?? "",
1351
+ duration: payload.duration,
1352
+ language: payload.language
1353
+ };
1354
+ }
1355
+ async generateViaResponses(request) {
1356
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
1357
+ const input = encodedAttachments.length ? [
1358
+ {
1359
+ role: "user",
1360
+ content: [
1361
+ { type: "input_text", text: request.prompt },
1362
+ ...encodedAttachments.map((item) => ({
1363
+ type: "input_image",
1364
+ image_url: `data:${item.mimeType};base64,${item.base64}`
1365
+ }))
1366
+ ]
1367
+ }
1368
+ ] : request.prompt;
1369
+ const response = await fetch(`${this.baseUrl}/responses`, {
1370
+ method: "POST",
1371
+ headers: {
1372
+ "content-type": "application/json",
1373
+ ...buildAuthHeaders(this.apiKey),
1374
+ ...this.headers
1375
+ },
1376
+ body: JSON.stringify({
1377
+ model: this.model,
1378
+ input,
1379
+ instructions: request.system,
1380
+ max_output_tokens: request.maxOutputTokens
1381
+ })
1382
+ });
1383
+ if (!response.ok) {
1384
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1385
+ }
1386
+ const payload = await response.json();
1387
+ return {
1388
+ text: extractResponsesText(payload),
1389
+ usage: payload.usage ? { inputTokens: payload.usage.input_tokens, outputTokens: payload.usage.output_tokens } : void 0
1390
+ };
1391
+ }
1392
+ async generateStructuredViaResponses(request, format) {
1393
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
1394
+ const input = encodedAttachments.length ? [
1395
+ {
1396
+ role: "user",
1397
+ content: [
1398
+ { type: "input_text", text: request.prompt },
1399
+ ...encodedAttachments.map((item) => ({
1400
+ type: "input_image",
1401
+ image_url: `data:${item.mimeType};base64,${item.base64}`
1402
+ }))
1403
+ ]
1404
+ }
1405
+ ] : request.prompt;
1406
+ const response = await fetch(`${this.baseUrl}/responses`, {
1407
+ method: "POST",
1408
+ headers: {
1409
+ "content-type": "application/json",
1410
+ ...buildAuthHeaders(this.apiKey),
1411
+ ...this.headers
1412
+ },
1413
+ body: JSON.stringify({
1414
+ model: this.model,
1415
+ input,
1416
+ instructions: request.system,
1417
+ max_output_tokens: request.maxOutputTokens,
1418
+ text: {
1419
+ format
1420
+ }
1421
+ })
1422
+ });
1423
+ if (!response.ok) {
1424
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1425
+ }
1426
+ const payload = await response.json();
1427
+ return extractResponsesText(payload);
1428
+ }
1429
+ async generateViaChatCompletions(request) {
1430
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
1431
+ const content = encodedAttachments.length ? [
1432
+ { type: "text", text: request.prompt },
1433
+ ...encodedAttachments.map((item) => ({
1434
+ type: "image_url",
1435
+ image_url: {
1436
+ url: `data:${item.mimeType};base64,${item.base64}`
1437
+ }
1438
+ }))
1439
+ ] : request.prompt;
1440
+ const messages = [...request.system ? [{ role: "system", content: request.system }] : [], { role: "user", content }];
1441
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
1442
+ method: "POST",
1443
+ headers: {
1444
+ "content-type": "application/json",
1445
+ ...buildAuthHeaders(this.apiKey),
1446
+ ...this.headers
1447
+ },
1448
+ body: JSON.stringify({
1449
+ model: this.model,
1450
+ messages,
1451
+ max_tokens: request.maxOutputTokens
1452
+ })
1453
+ });
1454
+ if (!response.ok) {
1455
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1456
+ }
1457
+ const payload = await response.json();
1458
+ const contentValue = payload.choices?.[0]?.message?.content;
1459
+ const text = Array.isArray(contentValue) ? contentValue.map((item) => item.text ?? "").join("\n") : contentValue ?? "";
1460
+ return {
1461
+ text,
1462
+ usage: payload.usage ? { inputTokens: payload.usage.prompt_tokens, outputTokens: payload.usage.completion_tokens } : void 0
1463
+ };
1464
+ }
1465
+ async generateStructuredViaChatCompletions(request, format) {
1466
+ const encodedAttachments = await this.encodeAttachments(request.attachments);
1467
+ const content = encodedAttachments.length ? [
1468
+ { type: "text", text: request.prompt },
1469
+ ...encodedAttachments.map((item) => ({
1470
+ type: "image_url",
1471
+ image_url: {
1472
+ url: `data:${item.mimeType};base64,${item.base64}`
1473
+ }
1474
+ }))
1475
+ ] : request.prompt;
1476
+ const messages = [...request.system ? [{ role: "system", content: request.system }] : [], { role: "user", content }];
1477
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
1478
+ method: "POST",
1479
+ headers: {
1480
+ "content-type": "application/json",
1481
+ ...buildAuthHeaders(this.apiKey),
1482
+ ...this.headers
1483
+ },
1484
+ body: JSON.stringify({
1485
+ model: this.model,
1486
+ messages,
1487
+ max_tokens: request.maxOutputTokens,
1488
+ response_format: {
1489
+ type: "json_schema",
1490
+ json_schema: format
1491
+ }
1492
+ })
1493
+ });
1494
+ if (!response.ok) {
1495
+ throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1496
+ }
1497
+ const payload = await response.json();
1498
+ const contentValue = payload.choices?.[0]?.message?.content;
1499
+ return Array.isArray(contentValue) ? contentValue.map((item) => item.text ?? "").join("\n") : contentValue ?? "";
1500
+ }
1501
+ };
1502
+
1503
+ // src/providers/registry.ts
1504
+ var customModuleSchema = z5.object({
1505
+ createAdapter: z5.function({
1506
+ input: [z5.string(), z5.custom(), z5.string()],
1507
+ output: z5.promise(z5.custom())
1508
+ })
1509
+ });
1510
+ function resolveCapabilities(config, fallback) {
1511
+ return config.capabilities?.length ? config.capabilities : fallback;
1512
+ }
1513
+ function envOrUndefined(name) {
1514
+ return name ? process.env[name] : void 0;
1515
+ }
1516
+ function createOpenAiCompatiblePreset(id, type, config, defaults) {
1517
+ return new OpenAiCompatibleProviderAdapter(id, type, config.model, {
1518
+ baseUrl: config.baseUrl ?? defaults.baseUrl,
1519
+ apiKey: envOrUndefined(config.apiKeyEnv ?? defaults.apiKeyEnv),
1520
+ headers: config.headers,
1521
+ apiStyle: config.apiStyle ?? defaults.apiStyle ?? "chat",
1522
+ capabilities: resolveCapabilities(config, defaults.capabilities)
1523
+ });
1524
+ }
1525
+ async function createProvider(id, config, rootDir) {
1526
+ switch (config.type) {
1527
+ case "heuristic":
1528
+ return new HeuristicProviderAdapter(id, config.model);
1529
+ case "openai":
1530
+ return new OpenAiCompatibleProviderAdapter(id, "openai", config.model, {
1531
+ baseUrl: config.baseUrl ?? "https://api.openai.com/v1",
1532
+ apiKey: envOrUndefined(config.apiKeyEnv),
1533
+ headers: config.headers,
1534
+ apiStyle: config.apiStyle ?? "responses",
1535
+ capabilities: resolveCapabilities(config, [
1536
+ "responses",
1537
+ "chat",
1538
+ "structured",
1539
+ "tools",
1540
+ "vision",
1541
+ "embeddings",
1542
+ "streaming",
1543
+ "image_generation",
1544
+ "audio"
1545
+ ])
1546
+ });
1547
+ case "ollama":
1548
+ return new OpenAiCompatibleProviderAdapter(id, "ollama", config.model, {
1549
+ baseUrl: config.baseUrl ?? "http://localhost:11434/v1",
1550
+ apiKey: envOrUndefined(config.apiKeyEnv) ?? "ollama",
1551
+ headers: config.headers,
1552
+ apiStyle: config.apiStyle ?? "responses",
1553
+ capabilities: resolveCapabilities(config, [
1554
+ "responses",
1555
+ "chat",
1556
+ "structured",
1557
+ "tools",
1558
+ "vision",
1559
+ "embeddings",
1560
+ "streaming",
1561
+ "local",
1562
+ "audio"
1563
+ ])
1564
+ });
1565
+ case "openai-compatible":
1566
+ return new OpenAiCompatibleProviderAdapter(id, "openai-compatible", config.model, {
1567
+ baseUrl: config.baseUrl ?? "http://localhost:8000/v1",
1568
+ apiKey: envOrUndefined(config.apiKeyEnv),
1569
+ headers: config.headers,
1570
+ apiStyle: config.apiStyle ?? "responses",
1571
+ capabilities: resolveCapabilities(config, ["chat", "structured", "embeddings", "audio"])
1572
+ });
1573
+ case "openrouter":
1574
+ return createOpenAiCompatiblePreset(id, "openrouter", config, {
1575
+ baseUrl: "https://openrouter.ai/api/v1",
1576
+ apiKeyEnv: "OPENROUTER_API_KEY",
1577
+ apiStyle: "chat",
1578
+ capabilities: ["chat", "structured", "embeddings"]
1579
+ });
1580
+ case "groq":
1581
+ return createOpenAiCompatiblePreset(id, "groq", config, {
1582
+ baseUrl: "https://api.groq.com/openai/v1",
1583
+ apiKeyEnv: "GROQ_API_KEY",
1584
+ apiStyle: "chat",
1585
+ capabilities: ["chat", "structured", "embeddings", "audio"]
1586
+ });
1587
+ case "together":
1588
+ return createOpenAiCompatiblePreset(id, "together", config, {
1589
+ baseUrl: "https://api.together.xyz/v1",
1590
+ apiKeyEnv: "TOGETHER_API_KEY",
1591
+ apiStyle: "chat",
1592
+ capabilities: ["chat", "structured", "embeddings"]
1593
+ });
1594
+ case "xai":
1595
+ return createOpenAiCompatiblePreset(id, "xai", config, {
1596
+ baseUrl: "https://api.x.ai/v1",
1597
+ apiKeyEnv: "XAI_API_KEY",
1598
+ apiStyle: "chat",
1599
+ capabilities: ["chat", "structured", "embeddings"]
1600
+ });
1601
+ case "cerebras":
1602
+ return createOpenAiCompatiblePreset(id, "cerebras", config, {
1603
+ baseUrl: "https://api.cerebras.ai/v1",
1604
+ apiKeyEnv: "CEREBRAS_API_KEY",
1605
+ apiStyle: "chat",
1606
+ capabilities: ["chat", "structured", "embeddings"]
1607
+ });
1608
+ case "anthropic":
1609
+ return new AnthropicProviderAdapter(id, config.model, {
1610
+ apiKey: envOrUndefined(config.apiKeyEnv),
1611
+ headers: config.headers,
1612
+ baseUrl: config.baseUrl
1613
+ });
1614
+ case "gemini":
1615
+ return new GeminiProviderAdapter(id, config.model, {
1616
+ apiKey: envOrUndefined(config.apiKeyEnv),
1617
+ baseUrl: config.baseUrl
1618
+ });
1619
+ case "local-whisper":
1620
+ return new LocalWhisperProviderAdapter(id, config.model, {
1621
+ binaryPath: config.binaryPath,
1622
+ modelPath: config.modelPath,
1623
+ extraArgs: config.extraArgs,
1624
+ threads: config.threads
1625
+ });
1626
+ case "custom": {
1627
+ if (!config.module) {
1628
+ throw new Error(`Provider ${id} is type "custom" but no module path was configured.`);
1629
+ }
1630
+ const resolvedModule = path5.isAbsolute(config.module) ? config.module : path5.resolve(rootDir, config.module);
1631
+ const loaded = await import(pathToFileURL(resolvedModule).href);
1632
+ const parsed = customModuleSchema.parse(loaded);
1633
+ return parsed.createAdapter(id, config, rootDir);
1634
+ }
1635
+ default:
1636
+ throw new Error(`Unsupported provider type ${String(config.type)}`);
1637
+ }
1638
+ }
1639
+ async function getProviderForTask(rootDir, task) {
1640
+ const { config } = await loadVaultConfig(rootDir);
1641
+ const providerId = config.tasks[task];
1642
+ if (!providerId) {
1643
+ throw new Error(`No provider configured for task "${String(task)}".`);
1644
+ }
1645
+ const providerConfig = config.providers[providerId];
1646
+ if (!providerConfig) {
1647
+ throw new Error(`No provider configured with id "${providerId}" for task "${task}".`);
1648
+ }
1649
+ return createProvider(providerId, providerConfig, rootDir);
1650
+ }
1651
+ function assertProviderCapability(provider, capability) {
1652
+ if (!provider.capabilities.has(capability)) {
1653
+ throw new Error(`Provider ${provider.id} does not support required capability "${capability}".`);
1654
+ }
1655
+ }
1656
+ async function getResolvedPaths(rootDir) {
1657
+ return (await loadVaultConfig(rootDir)).paths;
1658
+ }
1659
+
1660
+ export {
1661
+ slugify,
1662
+ sha256,
1663
+ ensureDir,
1664
+ fileExists,
1665
+ readJsonFile,
1666
+ writeJsonFile,
1667
+ appendJsonLine,
1668
+ writeFileIfChanged,
1669
+ toPosix,
1670
+ isPathWithin,
1671
+ firstSentences,
1672
+ uniqueBy,
1673
+ normalizeWhitespace,
1674
+ safeFrontmatter,
1675
+ truncate,
1676
+ listFilesRecursive,
1677
+ PRIMARY_SCHEMA_FILENAME,
1678
+ SWARMVAULT_OUT_ENV,
1679
+ defaultVaultConfig,
1680
+ defaultVaultSchema,
1681
+ resolvePaths,
1682
+ resolveArtifactRootDir,
1683
+ loadVaultConfig,
1684
+ initWorkspace,
1685
+ LocalWhisperProviderAdapter,
1686
+ createProvider,
1687
+ getProviderForTask,
1688
+ assertProviderCapability,
1689
+ getResolvedPaths
1690
+ };