@swarmvaultai/engine 3.16.0 → 3.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.js +36 -12
  3. package/package.json +3 -3
  4. package/dist/chunk-2CH2WWS4.js +0 -1359
  5. package/dist/chunk-2PN46RDI.js +0 -26846
  6. package/dist/chunk-333AMRSV.js +0 -1056
  7. package/dist/chunk-3GVEUYQZ.js +0 -1641
  8. package/dist/chunk-4MSSM2GH.js +0 -1476
  9. package/dist/chunk-563TZ4TZ.js +0 -26573
  10. package/dist/chunk-5GEPTIZE.js +0 -26010
  11. package/dist/chunk-5HNZ2WQI.js +0 -1341
  12. package/dist/chunk-5Q4IV4O3.js +0 -1336
  13. package/dist/chunk-6MO57J5C.js +0 -988
  14. package/dist/chunk-6UPHDGEB.js +0 -1073
  15. package/dist/chunk-75BU5TQ6.js +0 -1690
  16. package/dist/chunk-7O2HJSWQ.js +0 -1686
  17. package/dist/chunk-7QHDATCQ.js +0 -1673
  18. package/dist/chunk-B3FC4J3P.js +0 -1214
  19. package/dist/chunk-BTWPJEP2.js +0 -1421
  20. package/dist/chunk-CG67P2HB.js +0 -1420
  21. package/dist/chunk-CSPDMCON.js +0 -26846
  22. package/dist/chunk-CVFY54CF.js +0 -24893
  23. package/dist/chunk-CWLDFLH2.js +0 -1163
  24. package/dist/chunk-DAJAZPPO.js +0 -26865
  25. package/dist/chunk-EEWB4WGH.js +0 -1056
  26. package/dist/chunk-EXD4RWT3.js +0 -1131
  27. package/dist/chunk-F7HZZ3VM.js +0 -931
  28. package/dist/chunk-FD3LJQ4T.js +0 -1216
  29. package/dist/chunk-G2TH6ZTA.js +0 -1468
  30. package/dist/chunk-H3CDZYRE.js +0 -1701
  31. package/dist/chunk-HFU5S5NO.js +0 -838
  32. package/dist/chunk-HKU2T5JX.js +0 -25213
  33. package/dist/chunk-HOJ7NSYC.js +0 -937
  34. package/dist/chunk-HRRPWXRZ.js +0 -1335
  35. package/dist/chunk-HW72C7O2.js +0 -1690
  36. package/dist/chunk-IAEYFTUS.js +0 -1159
  37. package/dist/chunk-IHMJCCXR.js +0 -1146
  38. package/dist/chunk-JJDJF2P3.js +0 -27012
  39. package/dist/chunk-JTRE7C7P.js +0 -26062
  40. package/dist/chunk-L7DKPPV4.js +0 -27339
  41. package/dist/chunk-LEUV6TWJ.js +0 -1131
  42. package/dist/chunk-MB7HPUTR.js +0 -1364
  43. package/dist/chunk-MZSUYTSL.js +0 -998
  44. package/dist/chunk-N56FAH4N.js +0 -1404
  45. package/dist/chunk-NCSZ4AKP.js +0 -1057
  46. package/dist/chunk-NECZ4MUE.js +0 -1416
  47. package/dist/chunk-NHGS4LOI.js +0 -1346
  48. package/dist/chunk-NUWZUYE7.js +0 -1701
  49. package/dist/chunk-OK5752AP.js +0 -1325
  50. package/dist/chunk-QMW7OISM.js +0 -1063
  51. package/dist/chunk-RN56HUXA.js +0 -26972
  52. package/dist/chunk-RSQRF4FV.js +0 -1424
  53. package/dist/chunk-S2E65WRI.js +0 -26062
  54. package/dist/chunk-SRHM3HP4.js +0 -944
  55. package/dist/chunk-U7JO257M.js +0 -25017
  56. package/dist/chunk-UQCF65BN.js +0 -1623
  57. package/dist/chunk-USSP4GVB.js +0 -25064
  58. package/dist/chunk-V7KX3AQD.js +0 -26010
  59. package/dist/chunk-WOA5LSNB.js +0 -26559
  60. package/dist/chunk-WWP3VPEJ.js +0 -26080
  61. package/dist/chunk-YFKWMXJ6.js +0 -26066
  62. package/dist/chunk-Z552HHPV.js +0 -26846
  63. package/dist/chunk-ZQ5T64AR.js +0 -1365
  64. package/dist/memory-A4VPLUBA.js +0 -32
  65. package/dist/memory-DNSQCDHC.js +0 -32
  66. package/dist/memory-ECS3TSGC.js +0 -32
  67. package/dist/memory-FVIBFROA.js +0 -32
  68. package/dist/memory-G6I3DBW4.js +0 -32
  69. package/dist/memory-HE6VWUPV.js +0 -32
  70. package/dist/memory-HEA7XNKB.js +0 -32
  71. package/dist/memory-JRYTVHNH.js +0 -32
  72. package/dist/memory-K3NL5E3K.js +0 -32
  73. package/dist/memory-KANI73CX.js +0 -32
  74. package/dist/memory-KI5G2A4C.js +0 -32
  75. package/dist/memory-PK55JUKG.js +0 -32
  76. package/dist/memory-PK5JJNAG.js +0 -32
  77. package/dist/memory-QCVKS3H4.js +0 -32
  78. package/dist/memory-SAQPBIB4.js +0 -32
  79. package/dist/memory-SVGRP5KS.js +0 -32
  80. package/dist/memory-TQ46BGCI.js +0 -32
  81. package/dist/memory-YKQWWIVY.js +0 -32
  82. package/dist/memory-Z7BP5OSC.js +0 -32
  83. package/dist/registry-2QC3VN7M.js +0 -12
  84. package/dist/registry-2REAPKPO.js +0 -12
  85. package/dist/registry-2XHXZDGH.js +0 -12
  86. package/dist/registry-4C55ZCPL.js +0 -12
  87. package/dist/registry-4QRMVAHX.js +0 -12
  88. package/dist/registry-5SYH3Y3U.js +0 -12
  89. package/dist/registry-6KZMA3XM.js +0 -12
  90. package/dist/registry-7QACDJQQ.js +0 -12
  91. package/dist/registry-B7UXRBW3.js +0 -12
  92. package/dist/registry-FKEREVDO.js +0 -12
  93. package/dist/registry-FLSGGY2R.js +0 -12
  94. package/dist/registry-G7NSRYCO.js +0 -12
  95. package/dist/registry-GH4O3A7H.js +0 -12
  96. package/dist/registry-IBH6K2KK.js +0 -12
  97. package/dist/registry-ILDEBNCW.js +0 -12
  98. package/dist/registry-JFEW5RUP.js +0 -12
  99. package/dist/registry-JQYQOZYN.js +0 -12
  100. package/dist/registry-JR5WY22P.js +0 -12
  101. package/dist/registry-KLO5YIHP.js +0 -12
  102. package/dist/registry-KVJAO5DF.js +0 -12
  103. package/dist/registry-MYJX6AEE.js +0 -12
  104. package/dist/registry-NBLIJHZT.js +0 -12
  105. package/dist/registry-NLRWSN5J.js +0 -12
  106. package/dist/registry-NMXDBYIZ.js +0 -12
  107. package/dist/registry-OUB6W3LM.js +0 -12
  108. package/dist/registry-P5KRT66L.js +0 -12
  109. package/dist/registry-PGZWRXMD.js +0 -12
  110. package/dist/registry-QAG2ZYH3.js +0 -12
  111. package/dist/registry-SUXWCWB4.js +0 -12
  112. package/dist/registry-SYCRRA65.js +0 -12
  113. package/dist/registry-TYROWPR5.js +0 -12
  114. package/dist/registry-U23ML76I.js +0 -12
  115. package/dist/registry-U76DBOV3.js +0 -12
  116. package/dist/registry-UA42LQUQ.js +0 -12
  117. package/dist/registry-W6ZFRI73.js +0 -12
  118. package/dist/registry-X5PMZTZY.js +0 -12
  119. package/dist/registry-XIL5F33J.js +0 -12
  120. package/dist/registry-XOPLQNZY.js +0 -12
  121. package/dist/registry-YDXVCE4Q.js +0 -12
  122. package/dist/registry-YGVTLIZH.js +0 -12
  123. package/dist/registry-ZNW3FDED.js +0 -12
@@ -1,1404 +0,0 @@
1
- // src/providers/registry.ts
2
- import path4 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
- "custom"
39
- ]);
40
- var agentTypeSchema = z.enum([
41
- "codex",
42
- "claude",
43
- "cursor",
44
- "goose",
45
- "pi",
46
- "gemini",
47
- "opencode",
48
- "aider",
49
- "copilot",
50
- "trae",
51
- "claw",
52
- "droid"
53
- ]);
54
- var webSearchProviderTypeSchema = z.enum(["http-json", "custom"]);
55
-
56
- // src/utils.ts
57
- import crypto from "crypto";
58
- import fs from "fs/promises";
59
- import path from "path";
60
- function slugify(value) {
61
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "item";
62
- }
63
- function sha256(value) {
64
- return crypto.createHash("sha256").update(value).digest("hex");
65
- }
66
- async function ensureDir(dirPath) {
67
- await fs.mkdir(dirPath, { recursive: true });
68
- }
69
- async function fileExists(filePath) {
70
- try {
71
- await fs.access(filePath);
72
- return true;
73
- } catch {
74
- return false;
75
- }
76
- }
77
- async function readJsonFile(filePath) {
78
- try {
79
- const content = await fs.readFile(filePath, "utf8");
80
- return JSON.parse(content);
81
- } catch (error) {
82
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
83
- return null;
84
- }
85
- throw error;
86
- }
87
- }
88
- async function writeJsonFile(filePath, value) {
89
- await ensureDir(path.dirname(filePath));
90
- await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}
91
- `, "utf8");
92
- }
93
- async function appendJsonLine(filePath, value) {
94
- await ensureDir(path.dirname(filePath));
95
- await fs.appendFile(filePath, `${JSON.stringify(value)}
96
- `, "utf8");
97
- }
98
- async function writeFileIfChanged(filePath, content) {
99
- await ensureDir(path.dirname(filePath));
100
- if (await fileExists(filePath)) {
101
- const existing = await fs.readFile(filePath, "utf8");
102
- if (existing === content) {
103
- return false;
104
- }
105
- }
106
- await fs.writeFile(filePath, content, "utf8");
107
- return true;
108
- }
109
- function toPosix(value) {
110
- return value.split(path.sep).join(path.posix.sep);
111
- }
112
- function isPathWithin(rootDir, candidate) {
113
- const normalizedRoot = path.resolve(rootDir);
114
- const normalizedCandidate = path.resolve(candidate);
115
- if (normalizedCandidate === normalizedRoot) {
116
- return true;
117
- }
118
- const withSep = normalizedRoot.endsWith(path.sep) ? normalizedRoot : normalizedRoot + path.sep;
119
- return normalizedCandidate.startsWith(withSep);
120
- }
121
- function firstSentences(value, count = 3) {
122
- const sentences = value.replace(/\s+/g, " ").split(/(?<=[.!?])\s+/).filter(Boolean);
123
- return sentences.slice(0, count).join(" ").trim();
124
- }
125
- function uniqueBy(items, key) {
126
- const seen = /* @__PURE__ */ new Set();
127
- const result = [];
128
- for (const item of items) {
129
- const itemKey = key(item);
130
- if (seen.has(itemKey)) {
131
- continue;
132
- }
133
- seen.add(itemKey);
134
- result.push(item);
135
- }
136
- return result;
137
- }
138
- function extractJson(text) {
139
- const fencedMatch = text.match(/```json\s*([\s\S]*?)```/i);
140
- if (fencedMatch) {
141
- return fencedMatch[1].trim();
142
- }
143
- const start = text.indexOf("{");
144
- if (start !== -1) {
145
- let end = text.lastIndexOf("}");
146
- while (end > start) {
147
- const candidate = text.slice(start, end + 1);
148
- try {
149
- JSON.parse(candidate);
150
- return candidate;
151
- } catch {
152
- end = text.lastIndexOf("}", end - 1);
153
- }
154
- }
155
- }
156
- throw new Error("Could not locate JSON object in provider response.");
157
- }
158
- function normalizeWhitespace(value) {
159
- return value.replace(/\s+/g, " ").trim();
160
- }
161
- function truncate(value, maxLength) {
162
- if (value.length <= maxLength) {
163
- return value;
164
- }
165
- if (maxLength < 4) {
166
- return value.slice(0, maxLength);
167
- }
168
- return `${value.slice(0, maxLength - 3)}...`;
169
- }
170
- async function listFilesRecursive(rootDir) {
171
- const entries = await fs.readdir(rootDir, { withFileTypes: true }).catch(() => []);
172
- const files = [];
173
- for (const entry of entries) {
174
- const absolutePath = path.join(rootDir, entry.name);
175
- if (entry.isDirectory()) {
176
- files.push(...await listFilesRecursive(absolutePath));
177
- continue;
178
- }
179
- if (entry.isFile()) {
180
- files.push(absolutePath);
181
- }
182
- }
183
- return files;
184
- }
185
-
186
- // src/config.ts
187
- var PRIMARY_CONFIG_FILENAME = "swarmvault.config.json";
188
- var PRIMARY_SCHEMA_FILENAME = "swarmvault.schema.md";
189
- var moduleDir = path2.dirname(fileURLToPath(import.meta.url));
190
- var viewerDistDir = path2.basename(moduleDir) === "src" ? path2.resolve(moduleDir, "../../viewer/dist") : path2.resolve(moduleDir, "viewer");
191
- var providerConfigSchema = z2.object({
192
- type: providerTypeSchema,
193
- model: z2.string().min(1),
194
- baseUrl: z2.string().url().optional(),
195
- apiKeyEnv: z2.string().min(1).optional(),
196
- headers: z2.record(z2.string(), z2.string()).optional(),
197
- module: z2.string().min(1).optional(),
198
- capabilities: z2.array(providerCapabilitySchema).optional(),
199
- apiStyle: z2.enum(["responses", "chat"]).optional()
200
- });
201
- var sourceClassSchema = z2.enum(["first_party", "third_party", "resource", "generated"]);
202
- var vaultProfilePresetSchema = z2.enum(["reader", "timeline", "diligence", "thesis"]);
203
- var vaultDashboardPackSchema = z2.enum(["default", "reader", "diligence"]);
204
- var guidedSessionModeSchema = z2.enum(["insights_only", "canonical_review"]);
205
- var neo4jGraphSinkConfigSchema = z2.object({
206
- uri: z2.string().min(1),
207
- username: z2.string().min(1),
208
- passwordEnv: z2.string().min(1),
209
- database: z2.string().min(1).optional(),
210
- vaultId: z2.string().min(1).optional(),
211
- includeClasses: z2.array(sourceClassSchema).optional(),
212
- batchSize: z2.number().int().positive().optional()
213
- });
214
- var scheduleTriggerSchema = z2.object({
215
- cron: z2.string().min(1).optional(),
216
- every: z2.string().min(1).optional()
217
- }).refine((value) => Boolean(value.cron || value.every), {
218
- message: "Schedule triggers require `cron` or `every`."
219
- });
220
- var scheduledTaskSchema = z2.discriminatedUnion("type", [
221
- z2.object({
222
- type: z2.literal("compile"),
223
- approve: z2.boolean().optional()
224
- }),
225
- z2.object({
226
- type: z2.literal("lint"),
227
- deep: z2.boolean().optional(),
228
- web: z2.boolean().optional()
229
- }),
230
- z2.object({
231
- type: z2.literal("query"),
232
- question: z2.string().min(1),
233
- format: z2.enum(["markdown", "report", "slides", "chart", "image"]).optional(),
234
- save: z2.boolean().optional()
235
- }),
236
- z2.object({
237
- type: z2.literal("explore"),
238
- question: z2.string().min(1),
239
- steps: z2.number().int().positive().optional(),
240
- format: z2.enum(["markdown", "report", "slides", "chart", "image"]).optional()
241
- })
242
- ]);
243
- var roleExecutorConfigSchema = z2.discriminatedUnion("type", [
244
- z2.object({
245
- type: z2.literal("provider"),
246
- provider: z2.string().min(1)
247
- }),
248
- z2.object({
249
- type: z2.literal("command"),
250
- command: z2.array(z2.string().min(1)).min(1),
251
- cwd: z2.string().min(1).optional(),
252
- env: z2.record(z2.string(), z2.string()).optional(),
253
- timeoutMs: z2.number().int().positive().optional()
254
- })
255
- ]);
256
- var webSearchProviderConfigSchema = z2.object({
257
- type: webSearchProviderTypeSchema,
258
- endpoint: z2.string().url().optional(),
259
- method: z2.enum(["GET", "POST"]).optional(),
260
- apiKeyEnv: z2.string().min(1).optional(),
261
- apiKeyHeader: z2.string().min(1).optional(),
262
- apiKeyPrefix: z2.string().optional(),
263
- headers: z2.record(z2.string(), z2.string()).optional(),
264
- queryParam: z2.string().min(1).optional(),
265
- limitParam: z2.string().min(1).optional(),
266
- resultsPath: z2.string().min(1).optional(),
267
- titleField: z2.string().min(1).optional(),
268
- urlField: z2.string().min(1).optional(),
269
- snippetField: z2.string().min(1).optional(),
270
- module: z2.string().min(1).optional()
271
- });
272
- var vaultProfileConfigSchema = z2.object({
273
- presets: z2.array(vaultProfilePresetSchema).default([]),
274
- dashboardPack: vaultDashboardPackSchema.default("default"),
275
- guidedSessionMode: guidedSessionModeSchema.default("insights_only"),
276
- dataviewBlocks: z2.boolean().default(false),
277
- guidedIngestDefault: z2.boolean().default(false),
278
- deepLintDefault: z2.boolean().default(false)
279
- });
280
- var WORKSPACE_DIR_DEFAULTS = {
281
- rawDir: "raw",
282
- wikiDir: "wiki",
283
- stateDir: "state",
284
- agentDir: "agent",
285
- inboxDir: "inbox"
286
- };
287
- var vaultConfigSchema = z2.object({
288
- workspace: z2.object({
289
- rawDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.rawDir),
290
- wikiDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.wikiDir),
291
- stateDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.stateDir),
292
- agentDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.agentDir),
293
- inboxDir: z2.string().min(1).default(WORKSPACE_DIR_DEFAULTS.inboxDir)
294
- }).default(WORKSPACE_DIR_DEFAULTS),
295
- providers: z2.record(z2.string(), providerConfigSchema),
296
- tasks: z2.object({
297
- compileProvider: z2.string().min(1),
298
- queryProvider: z2.string().min(1),
299
- lintProvider: z2.string().min(1),
300
- visionProvider: z2.string().min(1),
301
- imageProvider: z2.string().min(1).optional(),
302
- embeddingProvider: z2.string().min(1).optional(),
303
- audioProvider: z2.string().min(1).optional()
304
- }),
305
- viewer: z2.object({
306
- port: z2.number().int().positive()
307
- }),
308
- profile: vaultProfileConfigSchema.default({
309
- presets: [],
310
- dashboardPack: "default",
311
- guidedSessionMode: "insights_only",
312
- dataviewBlocks: false,
313
- guidedIngestDefault: false,
314
- deepLintDefault: false
315
- }),
316
- projects: z2.record(
317
- z2.string(),
318
- z2.object({
319
- roots: z2.array(z2.string().min(1)).min(1),
320
- schemaPath: z2.string().min(1).optional()
321
- })
322
- ).optional(),
323
- agents: z2.array(agentTypeSchema).default(["codex", "claude", "cursor"]),
324
- schedules: z2.record(z2.string(), z2.object({ enabled: z2.boolean().optional(), when: scheduleTriggerSchema, task: scheduledTaskSchema })).optional(),
325
- orchestration: z2.object({
326
- maxParallelRoles: z2.number().int().positive().optional(),
327
- compilePostPass: z2.boolean().optional(),
328
- roles: z2.object({
329
- research: z2.object({ executor: roleExecutorConfigSchema }).optional(),
330
- audit: z2.object({ executor: roleExecutorConfigSchema }).optional(),
331
- context: z2.object({ executor: roleExecutorConfigSchema }).optional(),
332
- safety: z2.object({ executor: roleExecutorConfigSchema }).optional()
333
- }).optional()
334
- }).optional(),
335
- benchmark: z2.object({
336
- enabled: z2.boolean().optional(),
337
- questions: z2.array(z2.string().min(1)).optional(),
338
- maxQuestions: z2.number().int().positive().optional()
339
- }).optional(),
340
- repoAnalysis: z2.object({
341
- classifyGlobs: z2.partialRecord(sourceClassSchema, z2.array(z2.string().min(1))).optional(),
342
- extractClasses: z2.array(sourceClassSchema).optional()
343
- }).optional(),
344
- graphSinks: z2.object({
345
- neo4j: neo4jGraphSinkConfigSchema.optional()
346
- }).optional(),
347
- graph: z2.object({
348
- communityResolution: z2.number().positive().optional()
349
- }).optional(),
350
- webSearch: z2.object({
351
- providers: z2.record(z2.string(), webSearchProviderConfigSchema),
352
- tasks: z2.object({
353
- deepLintProvider: z2.string().min(1)
354
- })
355
- }).optional(),
356
- search: z2.object({
357
- hybrid: z2.boolean().optional(),
358
- rerank: z2.boolean().optional()
359
- }).optional(),
360
- autoCommit: z2.boolean().optional()
361
- });
362
- function normalizeProfilePresets(presets) {
363
- return [...new Set(presets)];
364
- }
365
- function inferDashboardPackFromPresets(presets) {
366
- if (presets.includes("diligence") && !presets.includes("reader")) {
367
- return "diligence";
368
- }
369
- return presets.length ? "reader" : "default";
370
- }
371
- function inferGuidedSessionModeFromPresets(presets) {
372
- return presets.length ? "canonical_review" : "insights_only";
373
- }
374
- function defaultVaultProfileConfig() {
375
- return {
376
- presets: [],
377
- dashboardPack: "default",
378
- guidedSessionMode: "insights_only",
379
- dataviewBlocks: false,
380
- guidedIngestDefault: false,
381
- deepLintDefault: false
382
- };
383
- }
384
- function personalResearchProfileConfig() {
385
- return {
386
- presets: ["reader", "timeline", "thesis"],
387
- dashboardPack: "reader",
388
- guidedSessionMode: "canonical_review",
389
- dataviewBlocks: true,
390
- guidedIngestDefault: true,
391
- deepLintDefault: true
392
- };
393
- }
394
- function normalizeVaultProfileConfig(profile) {
395
- const defaults = defaultVaultProfileConfig();
396
- const presets = normalizeProfilePresets(profile?.presets ?? defaults.presets);
397
- return {
398
- presets,
399
- dashboardPack: profile?.dashboardPack ?? inferDashboardPackFromPresets(presets),
400
- guidedSessionMode: profile?.guidedSessionMode ?? inferGuidedSessionModeFromPresets(presets),
401
- dataviewBlocks: profile?.dataviewBlocks ?? presets.length > 0,
402
- guidedIngestDefault: profile?.guidedIngestDefault ?? false,
403
- deepLintDefault: profile?.deepLintDefault ?? false
404
- };
405
- }
406
- function resolveInitProfile(profile) {
407
- const value = profile?.trim();
408
- if (!value || value === "default") {
409
- return {
410
- alias: "default",
411
- profile: defaultVaultProfileConfig()
412
- };
413
- }
414
- if (value === "personal-research") {
415
- return {
416
- alias: "personal-research",
417
- profile: personalResearchProfileConfig()
418
- };
419
- }
420
- const presets = normalizeProfilePresets(
421
- value.split(",").map((item) => item.trim()).filter(Boolean).map((item) => {
422
- const parsed = vaultProfilePresetSchema.safeParse(item);
423
- if (!parsed.success) {
424
- throw new Error(
425
- `Unknown init profile or preset: ${item}. Use \`default\`, \`personal-research\`, or a comma-separated list of presets: reader,timeline,diligence,thesis.`
426
- );
427
- }
428
- return parsed.data;
429
- })
430
- );
431
- return {
432
- alias: presets.join(","),
433
- profile: normalizeVaultProfileConfig({
434
- presets
435
- })
436
- };
437
- }
438
- function defaultVaultConfig(profile = defaultVaultProfileConfig()) {
439
- return {
440
- workspace: { ...WORKSPACE_DIR_DEFAULTS },
441
- providers: {
442
- local: {
443
- type: "heuristic",
444
- model: "heuristic-v1",
445
- capabilities: ["chat", "structured", "vision", "local"]
446
- }
447
- },
448
- tasks: {
449
- compileProvider: "local",
450
- queryProvider: "local",
451
- lintProvider: "local",
452
- visionProvider: "local",
453
- imageProvider: "local",
454
- audioProvider: void 0
455
- },
456
- viewer: {
457
- port: 4123
458
- },
459
- profile,
460
- projects: {},
461
- agents: ["codex", "claude", "cursor"],
462
- schedules: {},
463
- orchestration: {
464
- maxParallelRoles: 2,
465
- compilePostPass: false,
466
- roles: {}
467
- },
468
- benchmark: {
469
- enabled: true,
470
- questions: [],
471
- maxQuestions: 3
472
- },
473
- repoAnalysis: {
474
- classifyGlobs: {},
475
- extractClasses: ["first_party"]
476
- },
477
- graphSinks: {}
478
- };
479
- }
480
- function defaultVaultSchema(profile = "default") {
481
- const resolvedProfile = typeof profile === "string" ? resolveInitProfile(profile).profile : normalizeVaultProfileConfig(profile);
482
- const isResearchProfile = resolvedProfile.presets.length > 0 || resolvedProfile.guidedSessionMode === "canonical_review" || resolvedProfile.dataviewBlocks;
483
- if (isResearchProfile) {
484
- const presetLines = [];
485
- if (resolvedProfile.presets.includes("reader")) {
486
- presetLines.push("- Keep source pages and source guides optimized for rereading, synthesis, and durable summaries.");
487
- }
488
- if (resolvedProfile.presets.includes("timeline")) {
489
- presetLines.push("- Preserve chronology, dates, and source progression so timeline dashboards stay meaningful.");
490
- }
491
- if (resolvedProfile.presets.includes("diligence")) {
492
- presetLines.push("- Track evidence quality, explicit contradictions, and unresolved judgment calls instead of smoothing them away.");
493
- }
494
- if (resolvedProfile.presets.includes("thesis")) {
495
- presetLines.push("- Maintain explicit thesis, hub, or recurring-question pages that evolve as new evidence arrives.");
496
- }
497
- return [
498
- "# SwarmVault Schema",
499
- "",
500
- "Edit this file to teach SwarmVault how this research vault should be organized and maintained.",
501
- "",
502
- "## Vault Purpose",
503
- "",
504
- "- Track a personal research domain, reading program, or evolving thesis.",
505
- "- Prefer source-grounded summaries that help you revisit what mattered and what changed your mind.",
506
- "",
507
- "## Working Style",
508
- "",
509
- "- Favor one-source-at-a-time guided ingest and explicit review before treating a claim as canonical.",
510
- "- Preserve uncertainty, contradictions, and open questions instead of forcing synthesis too early.",
511
- "- Save useful summaries, briefs, and source guides back into the wiki so they become durable context.",
512
- ...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."],
513
- ...presetLines.length ? ["", "## Profile Emphasis", "", ...presetLines] : [],
514
- "",
515
- "## Naming Conventions",
516
- "",
517
- "- Prefer stable, descriptive page titles.",
518
- "- Keep concept, thesis, and entity names specific to the subject area.",
519
- "- Use source pages for grounded notes, concept/entity pages for accumulated understanding, and outputs for guided integration artifacts.",
520
- "",
521
- "## Page Structure Rules",
522
- "",
523
- "- Source pages should stay grounded in the original material.",
524
- "- Concept and entity pages should aggregate source-backed claims instead of inventing new ones.",
525
- "- Summaries should call out what is new, what is reinforcing, and what is conflicting.",
526
- "- Preserve contradictions instead of smoothing them away.",
527
- "",
528
- "## Categories",
529
- "",
530
- "- List domain-specific concept categories here.",
531
- "- Add thesis pages, recurring themes, or reading tracks that should act as canonical hubs.",
532
- "",
533
- "## Relationship Types",
534
- "",
535
- "- Mentions",
536
- "- Supports",
537
- "- Contradicts",
538
- "- Builds On",
539
- "- Questions",
540
- "",
541
- "## Dashboard Priorities",
542
- "",
543
- "- Recent source guides should surface active reading and ingestion progress.",
544
- "- Open questions should stay visible until resolved or explicitly archived.",
545
- "- Contradictions and follow-up sources should be easy to scan from dashboards.",
546
- ...resolvedProfile.dataviewBlocks ? ["- Keep frontmatter and page titles friendly to Dataview queries, but make every dashboard usable as plain markdown first."] : [],
547
- ""
548
- ].join("\n");
549
- }
550
- return [
551
- "# SwarmVault Schema",
552
- "",
553
- "Edit this file to teach SwarmVault how this vault should be organized and maintained.",
554
- "",
555
- "## Vault Purpose",
556
- "",
557
- "- Describe the domain this vault covers.",
558
- "- Note the intended audience and the kinds of questions the vault should answer well.",
559
- "",
560
- "## Naming Conventions",
561
- "",
562
- "- Prefer stable, descriptive page titles.",
563
- "- Keep concept and entity names specific to the domain.",
564
- "",
565
- "## Page Structure Rules",
566
- "",
567
- "- Source pages should stay grounded in the original material.",
568
- "- Concept and entity pages should aggregate source-backed claims instead of inventing new ones.",
569
- "- Preserve contradictions instead of smoothing them away.",
570
- "",
571
- "## Categories",
572
- "",
573
- "- List domain-specific concept categories here.",
574
- "- List important entity types here.",
575
- "",
576
- "## Relationship Types",
577
- "",
578
- "- Mentions",
579
- "- Supports",
580
- "- Contradicts",
581
- "- Depends on",
582
- "",
583
- "## Grounding Rules",
584
- "",
585
- "- Prefer raw sources over summaries.",
586
- "- Cite source ids whenever claims are stated.",
587
- "- Do not treat the wiki as a source of truth when the raw material disagrees.",
588
- "",
589
- "## Exclusions",
590
- "",
591
- "- List topics, claims, or page types the compiler should avoid generating.",
592
- ""
593
- ].join("\n");
594
- }
595
- async function findConfigPath(rootDir) {
596
- const primaryPath = path2.join(rootDir, PRIMARY_CONFIG_FILENAME);
597
- return primaryPath;
598
- }
599
- async function findSchemaPath(rootDir) {
600
- const primaryPath = path2.join(rootDir, PRIMARY_SCHEMA_FILENAME);
601
- return primaryPath;
602
- }
603
- function resolvePaths(rootDir, config, configPath = path2.join(rootDir, PRIMARY_CONFIG_FILENAME), schemaPath = path2.join(rootDir, PRIMARY_SCHEMA_FILENAME)) {
604
- const effective = config ?? defaultVaultConfig();
605
- const rawDir = path2.resolve(rootDir, effective.workspace.rawDir);
606
- const rawSourcesDir = path2.join(rawDir, "sources");
607
- const rawAssetsDir = path2.join(rawDir, "assets");
608
- const wikiDir = path2.resolve(rootDir, effective.workspace.wikiDir);
609
- const outputsAssetsDir = path2.join(wikiDir, "outputs", "assets");
610
- const projectsDir = path2.join(wikiDir, "projects");
611
- const candidatesDir = path2.join(wikiDir, "candidates");
612
- const stateDir = path2.resolve(rootDir, effective.workspace.stateDir);
613
- const schedulesDir = path2.join(stateDir, "schedules");
614
- const watchDir = path2.join(stateDir, "watch");
615
- const managedSourcesDir = path2.join(stateDir, "sources");
616
- const agentDir = path2.resolve(rootDir, effective.workspace.agentDir);
617
- const inboxDir = path2.resolve(rootDir, effective.workspace.inboxDir);
618
- return {
619
- rootDir,
620
- schemaPath,
621
- rawDir,
622
- rawSourcesDir,
623
- rawAssetsDir,
624
- wikiDir,
625
- outputsAssetsDir,
626
- projectsDir,
627
- candidatesDir,
628
- candidateConceptsDir: path2.join(candidatesDir, "concepts"),
629
- candidateEntitiesDir: path2.join(candidatesDir, "entities"),
630
- stateDir,
631
- schedulesDir,
632
- agentDir,
633
- inboxDir,
634
- manifestsDir: path2.join(stateDir, "manifests"),
635
- extractsDir: path2.join(stateDir, "extracts"),
636
- analysesDir: path2.join(stateDir, "analyses"),
637
- viewerDistDir,
638
- graphPath: path2.join(stateDir, "graph.json"),
639
- searchDbPath: path2.join(stateDir, "search.sqlite"),
640
- compileStatePath: path2.join(stateDir, "compile-state.json"),
641
- codeIndexPath: path2.join(stateDir, "code-index.json"),
642
- embeddingsPath: path2.join(stateDir, "embeddings.json"),
643
- benchmarkPath: path2.join(stateDir, "benchmark.json"),
644
- jobsLogPath: path2.join(stateDir, "jobs.ndjson"),
645
- sessionsDir: path2.join(stateDir, "sessions"),
646
- sourceSessionsDir: path2.join(stateDir, "source-sessions"),
647
- approvalsDir: path2.join(stateDir, "approvals"),
648
- watchDir,
649
- watchStatusPath: path2.join(watchDir, "status.json"),
650
- pendingSemanticRefreshPath: path2.join(watchDir, "pending-semantic-refresh.json"),
651
- managedSourcesPath: path2.join(stateDir, "sources.json"),
652
- managedSourcesDir,
653
- configPath
654
- };
655
- }
656
- async function loadVaultConfig(rootDir) {
657
- const configPath = await findConfigPath(rootDir);
658
- const schemaPath = await findSchemaPath(rootDir);
659
- const raw = await readJsonFile(configPath);
660
- const parsed = vaultConfigSchema.parse(raw ?? defaultVaultConfig());
661
- const config = {
662
- ...parsed,
663
- profile: normalizeVaultProfileConfig(parsed.profile)
664
- };
665
- return {
666
- config,
667
- paths: resolvePaths(rootDir, config, configPath, schemaPath)
668
- };
669
- }
670
- async function initWorkspace(rootDir, options = {}) {
671
- const configPath = await findConfigPath(rootDir);
672
- const schemaPath = await findSchemaPath(rootDir);
673
- const initProfile = resolveInitProfile(options.profile);
674
- const config = await fileExists(configPath) ? (await loadVaultConfig(rootDir)).config : defaultVaultConfig(initProfile.profile);
675
- const paths = resolvePaths(rootDir, config, configPath, schemaPath);
676
- const primarySchemaPath = path2.join(rootDir, PRIMARY_SCHEMA_FILENAME);
677
- await Promise.all([
678
- ensureDir(paths.rawDir),
679
- ensureDir(paths.wikiDir),
680
- ensureDir(paths.outputsAssetsDir),
681
- ensureDir(paths.projectsDir),
682
- ensureDir(paths.candidatesDir),
683
- ensureDir(paths.candidateConceptsDir),
684
- ensureDir(paths.candidateEntitiesDir),
685
- ensureDir(paths.stateDir),
686
- ensureDir(paths.schedulesDir),
687
- ensureDir(paths.watchDir),
688
- ensureDir(paths.managedSourcesDir),
689
- ensureDir(paths.sessionsDir),
690
- ensureDir(paths.sourceSessionsDir),
691
- ensureDir(paths.approvalsDir),
692
- ensureDir(paths.agentDir),
693
- ensureDir(paths.inboxDir),
694
- ensureDir(paths.manifestsDir),
695
- ensureDir(paths.extractsDir),
696
- ensureDir(paths.analysesDir),
697
- ensureDir(paths.rawSourcesDir),
698
- ensureDir(paths.rawAssetsDir)
699
- ]);
700
- if (!await fileExists(configPath)) {
701
- await writeJsonFile(configPath, config);
702
- }
703
- if (!await fileExists(primarySchemaPath)) {
704
- await ensureDir(path2.dirname(primarySchemaPath));
705
- await fs2.writeFile(primarySchemaPath, defaultVaultSchema(config.profile), "utf8");
706
- }
707
- return { config, paths };
708
- }
709
-
710
- // src/providers/base.ts
711
- import fs3 from "fs/promises";
712
- import { z as z3 } from "zod";
713
- var BaseProviderAdapter = class {
714
- constructor(id, type, model, capabilities) {
715
- this.id = id;
716
- this.type = type;
717
- this.model = model;
718
- this.capabilities = new Set(capabilities);
719
- }
720
- id;
721
- type;
722
- model;
723
- capabilities;
724
- async embedTexts(_texts) {
725
- throw new Error(`Provider ${this.id} does not support embeddings.`);
726
- }
727
- async generateImage(_request) {
728
- throw new Error(`Provider ${this.id} does not support image generation.`);
729
- }
730
- async transcribeAudio(_request) {
731
- throw new Error(`Provider ${this.id} does not support audio transcription.`);
732
- }
733
- async generateStructured(request, schema) {
734
- const schemaDescription = JSON.stringify(z3.toJSONSchema(schema), null, 2);
735
- const response = await this.generateText({
736
- ...request,
737
- prompt: `${request.prompt}
738
-
739
- Return JSON only. Follow this JSON Schema exactly:
740
- ${schemaDescription}`
741
- });
742
- const parsed = JSON.parse(extractJson(response.text));
743
- return schema.parse(parsed);
744
- }
745
- async encodeAttachments(attachments = []) {
746
- return Promise.all(
747
- attachments.map(async (attachment) => ({
748
- mimeType: attachment.mimeType,
749
- base64: await fs3.readFile(attachment.filePath, "base64")
750
- }))
751
- );
752
- }
753
- };
754
-
755
- // src/providers/anthropic.ts
756
- var AnthropicProviderAdapter = class extends BaseProviderAdapter {
757
- apiKey;
758
- headers;
759
- baseUrl;
760
- constructor(id, model, options) {
761
- super(id, "anthropic", model, ["chat", "structured", "tools", "vision", "streaming"]);
762
- this.apiKey = options.apiKey;
763
- this.headers = options.headers;
764
- this.baseUrl = (options.baseUrl ?? "https://api.anthropic.com").replace(/\/+$/, "");
765
- }
766
- async generateText(request) {
767
- const encodedAttachments = await this.encodeAttachments(request.attachments);
768
- const content = [
769
- { type: "text", text: request.prompt },
770
- ...encodedAttachments.map((item) => ({
771
- type: "image",
772
- source: {
773
- type: "base64",
774
- media_type: item.mimeType,
775
- data: item.base64
776
- }
777
- }))
778
- ];
779
- const response = await fetch(`${this.baseUrl}/v1/messages`, {
780
- method: "POST",
781
- headers: {
782
- "content-type": "application/json",
783
- "anthropic-version": "2023-06-01",
784
- ...this.apiKey ? { "x-api-key": this.apiKey } : {},
785
- ...this.headers
786
- },
787
- body: JSON.stringify({
788
- model: this.model,
789
- max_tokens: request.maxOutputTokens ?? 1200,
790
- system: request.system,
791
- messages: [
792
- {
793
- role: "user",
794
- content
795
- }
796
- ]
797
- })
798
- });
799
- if (!response.ok) {
800
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
801
- }
802
- const payload = await response.json();
803
- return {
804
- text: payload.content?.filter((item) => item.type === "text").map((item) => item.text ?? "").join("\n") ?? "",
805
- usage: payload.usage ? { inputTokens: payload.usage.input_tokens, outputTokens: payload.usage.output_tokens } : void 0
806
- };
807
- }
808
- };
809
-
810
- // src/providers/gemini.ts
811
- var GeminiProviderAdapter = class extends BaseProviderAdapter {
812
- apiKey;
813
- baseUrl;
814
- constructor(id, model, options) {
815
- super(id, "gemini", model, ["chat", "structured", "vision", "tools", "streaming"]);
816
- this.apiKey = options.apiKey;
817
- this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(/\/+$/, "");
818
- }
819
- async generateText(request) {
820
- const encodedAttachments = await this.encodeAttachments(request.attachments);
821
- const parts = [
822
- ...request.system ? [{ text: `System instructions:
823
- ${request.system}` }] : [],
824
- { text: request.prompt },
825
- ...encodedAttachments.map((item) => ({
826
- inline_data: {
827
- mime_type: item.mimeType,
828
- data: item.base64
829
- }
830
- }))
831
- ];
832
- const response = await fetch(`${this.baseUrl}/models/${this.model}:generateContent`, {
833
- method: "POST",
834
- headers: {
835
- "content-type": "application/json",
836
- ...this.apiKey ? { "x-goog-api-key": this.apiKey } : {}
837
- },
838
- body: JSON.stringify({
839
- contents: [
840
- {
841
- role: "user",
842
- parts
843
- }
844
- ],
845
- generationConfig: {
846
- maxOutputTokens: request.maxOutputTokens ?? 1200
847
- }
848
- })
849
- });
850
- if (!response.ok) {
851
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
852
- }
853
- const payload = await response.json();
854
- const text = payload.candidates?.[0]?.content?.parts?.map((part) => part.text ?? "").join("\n") ?? "";
855
- return {
856
- text,
857
- usage: payload.usageMetadata ? { inputTokens: payload.usageMetadata.promptTokenCount, outputTokens: payload.usageMetadata.candidatesTokenCount } : void 0
858
- };
859
- }
860
- };
861
-
862
- // src/providers/heuristic.ts
863
- function summarizePrompt(prompt) {
864
- const cleaned = normalizeWhitespace(prompt);
865
- if (!cleaned) {
866
- return "No prompt content provided.";
867
- }
868
- return firstSentences(cleaned, 2) || cleaned.slice(0, 280);
869
- }
870
- var HeuristicProviderAdapter = class extends BaseProviderAdapter {
871
- constructor(id, model) {
872
- super(id, "heuristic", model, ["chat", "structured", "vision", "local"]);
873
- }
874
- async generateText(request) {
875
- const attachmentHint = request.attachments?.length ? ` Attachments: ${request.attachments.length}.` : "";
876
- return {
877
- text: `Heuristic provider response.${attachmentHint} ${summarizePrompt(request.prompt)}`.trim()
878
- };
879
- }
880
- };
881
-
882
- // src/providers/openai-compatible.ts
883
- import path3 from "path";
884
- import { z as z4 } from "zod";
885
- function buildAuthHeaders(apiKey) {
886
- return apiKey ? { Authorization: `Bearer ${apiKey}` } : {};
887
- }
888
- function extractResponsesText(payload) {
889
- if (typeof payload.output_text === "string" && payload.output_text.trim()) {
890
- return payload.output_text;
891
- }
892
- return payload.output?.flatMap((item) => item.content ?? []).filter((item) => item.type === "output_text" && typeof item.text === "string").map((item) => item.text ?? "").join("\n").trim() ?? "";
893
- }
894
- function isJsonSchemaObject(value) {
895
- return typeof value === "object" && value !== null && !Array.isArray(value);
896
- }
897
- function allowNullInSchema(schema) {
898
- if (Array.isArray(schema.type)) {
899
- return schema.type.includes("null") ? schema : { ...schema, type: [...schema.type, "null"] };
900
- }
901
- if (typeof schema.type === "string") {
902
- return schema.type === "null" ? schema : { ...schema, type: [schema.type, "null"] };
903
- }
904
- if (Array.isArray(schema.enum)) {
905
- return schema.enum.includes(null) ? schema : { ...schema, enum: [...schema.enum, null] };
906
- }
907
- if (Array.isArray(schema.anyOf)) {
908
- return schema.anyOf.some((item) => isJsonSchemaObject(item) && item.type === "null") ? schema : { ...schema, anyOf: [...schema.anyOf, { type: "null" }] };
909
- }
910
- return { anyOf: [schema, { type: "null" }] };
911
- }
912
- function toOpenAiStrictJsonSchema(schema) {
913
- if (Array.isArray(schema)) {
914
- return schema.map((item) => toOpenAiStrictJsonSchema(item));
915
- }
916
- if (!isJsonSchemaObject(schema)) {
917
- return schema;
918
- }
919
- const normalizedEntries = Object.entries(schema).filter(([key]) => key !== "$schema").map(([key, value]) => [key, toOpenAiStrictJsonSchema(value)]);
920
- const normalizedSchema = Object.fromEntries(normalizedEntries);
921
- if (isJsonSchemaObject(normalizedSchema.properties)) {
922
- const properties = normalizedSchema.properties;
923
- const originalRequired = Array.isArray(normalizedSchema.required) ? normalizedSchema.required.filter((item) => typeof item === "string") : [];
924
- const requiredSet = new Set(originalRequired);
925
- const propertyEntries = Object.entries(properties).map(([key, value]) => {
926
- const normalizedProperty = isJsonSchemaObject(value) ? value : {};
927
- return [key, requiredSet.has(key) ? normalizedProperty : allowNullInSchema(normalizedProperty)];
928
- });
929
- return {
930
- ...normalizedSchema,
931
- properties: Object.fromEntries(propertyEntries),
932
- required: Object.keys(properties),
933
- additionalProperties: false
934
- };
935
- }
936
- return normalizedSchema;
937
- }
938
- function stripNullObjectProperties(value) {
939
- if (Array.isArray(value)) {
940
- return value.map((item) => stripNullObjectProperties(item));
941
- }
942
- if (!isJsonSchemaObject(value)) {
943
- return value;
944
- }
945
- const entries = Object.entries(value).filter(([, item]) => item !== null).map(([key, item]) => [key, stripNullObjectProperties(item)]);
946
- return Object.fromEntries(entries);
947
- }
948
- function buildStructuredFormat(schema) {
949
- return {
950
- type: "json_schema",
951
- name: "swarmvault_response",
952
- schema: toOpenAiStrictJsonSchema(z4.toJSONSchema(schema)),
953
- strict: true
954
- };
955
- }
956
- var OpenAiCompatibleProviderAdapter = class extends BaseProviderAdapter {
957
- baseUrl;
958
- apiKey;
959
- headers;
960
- apiStyle;
961
- constructor(id, type, model, options) {
962
- super(id, type, model, options.capabilities);
963
- this.baseUrl = options.baseUrl.replace(/\/+$/, "");
964
- this.apiKey = options.apiKey;
965
- this.headers = options.headers;
966
- this.apiStyle = options.apiStyle ?? "responses";
967
- }
968
- async generateText(request) {
969
- if (this.apiStyle === "chat") {
970
- return this.generateViaChatCompletions(request);
971
- }
972
- return this.generateViaResponses(request);
973
- }
974
- async generateStructured(request, schema) {
975
- if (this.type !== "openai") {
976
- return super.generateStructured(request, schema);
977
- }
978
- const structuredFormat = buildStructuredFormat(schema);
979
- const text = this.apiStyle === "chat" ? await this.generateStructuredViaChatCompletions(
980
- {
981
- ...request
982
- },
983
- structuredFormat
984
- ) : await this.generateStructuredViaResponses(
985
- {
986
- ...request
987
- },
988
- structuredFormat
989
- );
990
- return schema.parse(stripNullObjectProperties(JSON.parse(extractJson(text))));
991
- }
992
- async embedTexts(texts) {
993
- if (!texts.length) {
994
- return [];
995
- }
996
- const response = await fetch(`${this.baseUrl}/embeddings`, {
997
- method: "POST",
998
- headers: {
999
- "content-type": "application/json",
1000
- ...buildAuthHeaders(this.apiKey),
1001
- ...this.headers
1002
- },
1003
- body: JSON.stringify({
1004
- model: this.model,
1005
- input: texts
1006
- })
1007
- });
1008
- if (!response.ok) {
1009
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1010
- }
1011
- const payload = await response.json();
1012
- const vectors = payload.data?.map((item) => item.embedding ?? []) ?? [];
1013
- if (vectors.length !== texts.length || vectors.some((vector) => !Array.isArray(vector) || vector.length === 0)) {
1014
- throw new Error(`Provider ${this.id} returned invalid embedding data.`);
1015
- }
1016
- return vectors;
1017
- }
1018
- async generateImage(request) {
1019
- const encodedAttachments = await this.encodeAttachments(request.attachments);
1020
- const response = await fetch(`${this.baseUrl}/images/generations`, {
1021
- method: "POST",
1022
- headers: {
1023
- "content-type": "application/json",
1024
- ...buildAuthHeaders(this.apiKey),
1025
- ...this.headers
1026
- },
1027
- body: JSON.stringify({
1028
- model: this.model,
1029
- prompt: request.prompt,
1030
- size: request.width && request.height ? `${Math.max(256, Math.round(request.width))}x${Math.max(256, Math.round(request.height))}` : void 0,
1031
- response_format: "b64_json",
1032
- ...encodedAttachments.length ? {
1033
- input_image: encodedAttachments.map((item) => `data:${item.mimeType};base64,${item.base64}`)
1034
- } : {}
1035
- })
1036
- });
1037
- if (!response.ok) {
1038
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1039
- }
1040
- const payload = await response.json();
1041
- const image = payload.data?.[0];
1042
- if (!image?.b64_json) {
1043
- throw new Error(`Provider ${this.id} returned no image data.`);
1044
- }
1045
- return {
1046
- mimeType: "image/png",
1047
- bytes: Buffer.from(image.b64_json, "base64"),
1048
- width: request.width,
1049
- height: request.height,
1050
- revisedPrompt: image.revised_prompt
1051
- };
1052
- }
1053
- async transcribeAudio(request) {
1054
- const extension = request.mimeType.split("/")[1]?.split("+")[0] ?? "bin";
1055
- const fileName = request.fileName ?? `audio.${extension}`;
1056
- const formData = new FormData();
1057
- formData.append("file", new File([new Uint8Array(request.bytes)], path3.basename(fileName), { type: request.mimeType }));
1058
- formData.append("model", this.model);
1059
- formData.append("response_format", "verbose_json");
1060
- if (request.language) {
1061
- formData.append("language", request.language);
1062
- }
1063
- const response = await fetch(`${this.baseUrl}/audio/transcriptions`, {
1064
- method: "POST",
1065
- headers: {
1066
- ...buildAuthHeaders(this.apiKey),
1067
- ...this.headers
1068
- },
1069
- body: formData
1070
- });
1071
- if (!response.ok) {
1072
- throw new Error(`Provider ${this.id} audio transcription failed: ${response.status} ${response.statusText}`);
1073
- }
1074
- const payload = await response.json();
1075
- return {
1076
- text: payload.text ?? "",
1077
- duration: payload.duration,
1078
- language: payload.language
1079
- };
1080
- }
1081
- async generateViaResponses(request) {
1082
- const encodedAttachments = await this.encodeAttachments(request.attachments);
1083
- const input = encodedAttachments.length ? [
1084
- {
1085
- role: "user",
1086
- content: [
1087
- { type: "input_text", text: request.prompt },
1088
- ...encodedAttachments.map((item) => ({
1089
- type: "input_image",
1090
- image_url: `data:${item.mimeType};base64,${item.base64}`
1091
- }))
1092
- ]
1093
- }
1094
- ] : request.prompt;
1095
- const response = await fetch(`${this.baseUrl}/responses`, {
1096
- method: "POST",
1097
- headers: {
1098
- "content-type": "application/json",
1099
- ...buildAuthHeaders(this.apiKey),
1100
- ...this.headers
1101
- },
1102
- body: JSON.stringify({
1103
- model: this.model,
1104
- input,
1105
- instructions: request.system,
1106
- max_output_tokens: request.maxOutputTokens
1107
- })
1108
- });
1109
- if (!response.ok) {
1110
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1111
- }
1112
- const payload = await response.json();
1113
- return {
1114
- text: extractResponsesText(payload),
1115
- usage: payload.usage ? { inputTokens: payload.usage.input_tokens, outputTokens: payload.usage.output_tokens } : void 0
1116
- };
1117
- }
1118
- async generateStructuredViaResponses(request, format) {
1119
- const encodedAttachments = await this.encodeAttachments(request.attachments);
1120
- const input = encodedAttachments.length ? [
1121
- {
1122
- role: "user",
1123
- content: [
1124
- { type: "input_text", text: request.prompt },
1125
- ...encodedAttachments.map((item) => ({
1126
- type: "input_image",
1127
- image_url: `data:${item.mimeType};base64,${item.base64}`
1128
- }))
1129
- ]
1130
- }
1131
- ] : request.prompt;
1132
- const response = await fetch(`${this.baseUrl}/responses`, {
1133
- method: "POST",
1134
- headers: {
1135
- "content-type": "application/json",
1136
- ...buildAuthHeaders(this.apiKey),
1137
- ...this.headers
1138
- },
1139
- body: JSON.stringify({
1140
- model: this.model,
1141
- input,
1142
- instructions: request.system,
1143
- max_output_tokens: request.maxOutputTokens,
1144
- text: {
1145
- format
1146
- }
1147
- })
1148
- });
1149
- if (!response.ok) {
1150
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1151
- }
1152
- const payload = await response.json();
1153
- return extractResponsesText(payload);
1154
- }
1155
- async generateViaChatCompletions(request) {
1156
- const encodedAttachments = await this.encodeAttachments(request.attachments);
1157
- const content = encodedAttachments.length ? [
1158
- { type: "text", text: request.prompt },
1159
- ...encodedAttachments.map((item) => ({
1160
- type: "image_url",
1161
- image_url: {
1162
- url: `data:${item.mimeType};base64,${item.base64}`
1163
- }
1164
- }))
1165
- ] : request.prompt;
1166
- const messages = [...request.system ? [{ role: "system", content: request.system }] : [], { role: "user", content }];
1167
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
1168
- method: "POST",
1169
- headers: {
1170
- "content-type": "application/json",
1171
- ...buildAuthHeaders(this.apiKey),
1172
- ...this.headers
1173
- },
1174
- body: JSON.stringify({
1175
- model: this.model,
1176
- messages,
1177
- max_tokens: request.maxOutputTokens
1178
- })
1179
- });
1180
- if (!response.ok) {
1181
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1182
- }
1183
- const payload = await response.json();
1184
- const contentValue = payload.choices?.[0]?.message?.content;
1185
- const text = Array.isArray(contentValue) ? contentValue.map((item) => item.text ?? "").join("\n") : contentValue ?? "";
1186
- return {
1187
- text,
1188
- usage: payload.usage ? { inputTokens: payload.usage.prompt_tokens, outputTokens: payload.usage.completion_tokens } : void 0
1189
- };
1190
- }
1191
- async generateStructuredViaChatCompletions(request, format) {
1192
- const encodedAttachments = await this.encodeAttachments(request.attachments);
1193
- const content = encodedAttachments.length ? [
1194
- { type: "text", text: request.prompt },
1195
- ...encodedAttachments.map((item) => ({
1196
- type: "image_url",
1197
- image_url: {
1198
- url: `data:${item.mimeType};base64,${item.base64}`
1199
- }
1200
- }))
1201
- ] : request.prompt;
1202
- const messages = [...request.system ? [{ role: "system", content: request.system }] : [], { role: "user", content }];
1203
- const response = await fetch(`${this.baseUrl}/chat/completions`, {
1204
- method: "POST",
1205
- headers: {
1206
- "content-type": "application/json",
1207
- ...buildAuthHeaders(this.apiKey),
1208
- ...this.headers
1209
- },
1210
- body: JSON.stringify({
1211
- model: this.model,
1212
- messages,
1213
- max_tokens: request.maxOutputTokens,
1214
- response_format: {
1215
- type: "json_schema",
1216
- json_schema: format
1217
- }
1218
- })
1219
- });
1220
- if (!response.ok) {
1221
- throw new Error(`Provider ${this.id} failed: ${response.status} ${response.statusText}`);
1222
- }
1223
- const payload = await response.json();
1224
- const contentValue = payload.choices?.[0]?.message?.content;
1225
- return Array.isArray(contentValue) ? contentValue.map((item) => item.text ?? "").join("\n") : contentValue ?? "";
1226
- }
1227
- };
1228
-
1229
- // src/providers/registry.ts
1230
- var customModuleSchema = z5.object({
1231
- createAdapter: z5.function({
1232
- input: [z5.string(), z5.custom(), z5.string()],
1233
- output: z5.promise(z5.custom())
1234
- })
1235
- });
1236
- function resolveCapabilities(config, fallback) {
1237
- return config.capabilities?.length ? config.capabilities : fallback;
1238
- }
1239
- function envOrUndefined(name) {
1240
- return name ? process.env[name] : void 0;
1241
- }
1242
- function createOpenAiCompatiblePreset(id, type, config, defaults) {
1243
- return new OpenAiCompatibleProviderAdapter(id, type, config.model, {
1244
- baseUrl: config.baseUrl ?? defaults.baseUrl,
1245
- apiKey: envOrUndefined(config.apiKeyEnv ?? defaults.apiKeyEnv),
1246
- headers: config.headers,
1247
- apiStyle: config.apiStyle ?? defaults.apiStyle ?? "chat",
1248
- capabilities: resolveCapabilities(config, defaults.capabilities)
1249
- });
1250
- }
1251
- async function createProvider(id, config, rootDir) {
1252
- switch (config.type) {
1253
- case "heuristic":
1254
- return new HeuristicProviderAdapter(id, config.model);
1255
- case "openai":
1256
- return new OpenAiCompatibleProviderAdapter(id, "openai", config.model, {
1257
- baseUrl: config.baseUrl ?? "https://api.openai.com/v1",
1258
- apiKey: envOrUndefined(config.apiKeyEnv),
1259
- headers: config.headers,
1260
- apiStyle: config.apiStyle ?? "responses",
1261
- capabilities: resolveCapabilities(config, [
1262
- "responses",
1263
- "chat",
1264
- "structured",
1265
- "tools",
1266
- "vision",
1267
- "embeddings",
1268
- "streaming",
1269
- "image_generation",
1270
- "audio"
1271
- ])
1272
- });
1273
- case "ollama":
1274
- return new OpenAiCompatibleProviderAdapter(id, "ollama", config.model, {
1275
- baseUrl: config.baseUrl ?? "http://localhost:11434/v1",
1276
- apiKey: envOrUndefined(config.apiKeyEnv) ?? "ollama",
1277
- headers: config.headers,
1278
- apiStyle: config.apiStyle ?? "responses",
1279
- capabilities: resolveCapabilities(config, [
1280
- "responses",
1281
- "chat",
1282
- "structured",
1283
- "tools",
1284
- "vision",
1285
- "embeddings",
1286
- "streaming",
1287
- "local",
1288
- "audio"
1289
- ])
1290
- });
1291
- case "openai-compatible":
1292
- return new OpenAiCompatibleProviderAdapter(id, "openai-compatible", config.model, {
1293
- baseUrl: config.baseUrl ?? "http://localhost:8000/v1",
1294
- apiKey: envOrUndefined(config.apiKeyEnv),
1295
- headers: config.headers,
1296
- apiStyle: config.apiStyle ?? "responses",
1297
- capabilities: resolveCapabilities(config, ["chat", "structured", "embeddings", "audio"])
1298
- });
1299
- case "openrouter":
1300
- return createOpenAiCompatiblePreset(id, "openrouter", config, {
1301
- baseUrl: "https://openrouter.ai/api/v1",
1302
- apiKeyEnv: "OPENROUTER_API_KEY",
1303
- apiStyle: "chat",
1304
- capabilities: ["chat", "structured", "embeddings"]
1305
- });
1306
- case "groq":
1307
- return createOpenAiCompatiblePreset(id, "groq", config, {
1308
- baseUrl: "https://api.groq.com/openai/v1",
1309
- apiKeyEnv: "GROQ_API_KEY",
1310
- apiStyle: "chat",
1311
- capabilities: ["chat", "structured", "embeddings", "audio"]
1312
- });
1313
- case "together":
1314
- return createOpenAiCompatiblePreset(id, "together", config, {
1315
- baseUrl: "https://api.together.xyz/v1",
1316
- apiKeyEnv: "TOGETHER_API_KEY",
1317
- apiStyle: "chat",
1318
- capabilities: ["chat", "structured", "embeddings"]
1319
- });
1320
- case "xai":
1321
- return createOpenAiCompatiblePreset(id, "xai", config, {
1322
- baseUrl: "https://api.x.ai/v1",
1323
- apiKeyEnv: "XAI_API_KEY",
1324
- apiStyle: "chat",
1325
- capabilities: ["chat", "structured", "embeddings"]
1326
- });
1327
- case "cerebras":
1328
- return createOpenAiCompatiblePreset(id, "cerebras", config, {
1329
- baseUrl: "https://api.cerebras.ai/v1",
1330
- apiKeyEnv: "CEREBRAS_API_KEY",
1331
- apiStyle: "chat",
1332
- capabilities: ["chat", "structured", "embeddings"]
1333
- });
1334
- case "anthropic":
1335
- return new AnthropicProviderAdapter(id, config.model, {
1336
- apiKey: envOrUndefined(config.apiKeyEnv),
1337
- headers: config.headers,
1338
- baseUrl: config.baseUrl
1339
- });
1340
- case "gemini":
1341
- return new GeminiProviderAdapter(id, config.model, {
1342
- apiKey: envOrUndefined(config.apiKeyEnv),
1343
- baseUrl: config.baseUrl
1344
- });
1345
- case "custom": {
1346
- if (!config.module) {
1347
- throw new Error(`Provider ${id} is type "custom" but no module path was configured.`);
1348
- }
1349
- const resolvedModule = path4.isAbsolute(config.module) ? config.module : path4.resolve(rootDir, config.module);
1350
- const loaded = await import(pathToFileURL(resolvedModule).href);
1351
- const parsed = customModuleSchema.parse(loaded);
1352
- return parsed.createAdapter(id, config, rootDir);
1353
- }
1354
- default:
1355
- throw new Error(`Unsupported provider type ${String(config.type)}`);
1356
- }
1357
- }
1358
- async function getProviderForTask(rootDir, task) {
1359
- const { config } = await loadVaultConfig(rootDir);
1360
- const providerId = config.tasks[task];
1361
- if (!providerId) {
1362
- throw new Error(`No provider configured for task "${String(task)}".`);
1363
- }
1364
- const providerConfig = config.providers[providerId];
1365
- if (!providerConfig) {
1366
- throw new Error(`No provider configured with id "${providerId}" for task "${task}".`);
1367
- }
1368
- return createProvider(providerId, providerConfig, rootDir);
1369
- }
1370
- function assertProviderCapability(provider, capability) {
1371
- if (!provider.capabilities.has(capability)) {
1372
- throw new Error(`Provider ${provider.id} does not support required capability "${capability}".`);
1373
- }
1374
- }
1375
- async function getResolvedPaths(rootDir) {
1376
- return (await loadVaultConfig(rootDir)).paths;
1377
- }
1378
-
1379
- export {
1380
- slugify,
1381
- sha256,
1382
- ensureDir,
1383
- fileExists,
1384
- readJsonFile,
1385
- writeJsonFile,
1386
- appendJsonLine,
1387
- writeFileIfChanged,
1388
- toPosix,
1389
- isPathWithin,
1390
- firstSentences,
1391
- uniqueBy,
1392
- normalizeWhitespace,
1393
- truncate,
1394
- listFilesRecursive,
1395
- defaultVaultConfig,
1396
- defaultVaultSchema,
1397
- resolvePaths,
1398
- loadVaultConfig,
1399
- initWorkspace,
1400
- createProvider,
1401
- getProviderForTask,
1402
- assertProviderCapability,
1403
- getResolvedPaths
1404
- };