@thanh01.pmt/domain-kit 0.5.0 → 0.6.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 (101) hide show
  1. package/README.md +682 -168
  2. package/dist/assembly/index.d.cts +3 -2
  3. package/dist/assembly/index.d.ts +3 -2
  4. package/dist/{chunk-TYXSJUQU.mjs → chunk-4UCXYRM3.mjs} +208 -310
  5. package/dist/chunk-4UCXYRM3.mjs.map +1 -0
  6. package/dist/chunk-7A2SG466.mjs +325 -0
  7. package/dist/chunk-7A2SG466.mjs.map +1 -0
  8. package/dist/{chunk-E4GCCWJO.mjs → chunk-CGNXGVHV.mjs} +37 -4
  9. package/dist/chunk-CGNXGVHV.mjs.map +1 -0
  10. package/dist/{chunk-DRVOV5ZD.mjs → chunk-DSEJLTUO.mjs} +2 -2
  11. package/dist/chunk-DSEJLTUO.mjs.map +1 -0
  12. package/dist/{chunk-VCPIWJ7R.mjs → chunk-H4W3YJLK.mjs} +29 -4
  13. package/dist/chunk-H4W3YJLK.mjs.map +1 -0
  14. package/dist/chunk-IBVXXVGE.mjs +14 -0
  15. package/dist/chunk-IBVXXVGE.mjs.map +1 -0
  16. package/dist/{chunk-G7HLFMD2.mjs → chunk-KHCZYLKM.mjs} +68 -33
  17. package/dist/chunk-KHCZYLKM.mjs.map +1 -0
  18. package/dist/chunk-OYSZQVPY.mjs +10 -0
  19. package/dist/chunk-OYSZQVPY.mjs.map +1 -0
  20. package/dist/{chunk-BL5NBKPH.mjs → chunk-WD3P5V3B.mjs} +19 -8
  21. package/dist/chunk-WD3P5V3B.mjs.map +1 -0
  22. package/dist/{chunk-TWKIVUTQ.mjs → chunk-WKTH3JPA.mjs} +197 -36
  23. package/dist/chunk-WKTH3JPA.mjs.map +1 -0
  24. package/dist/{conceptEscalator-ltRLtPhf.d.cts → conceptEscalator-BmMJD8C5.d.cts} +10 -1
  25. package/dist/{conceptEscalator-YfMKIKCZ.d.ts → conceptEscalator-DMsehscs.d.ts} +10 -1
  26. package/dist/concepts/index.d.cts +4 -3
  27. package/dist/concepts/index.d.ts +4 -3
  28. package/dist/{cpp-CZUPqJt9.d.cts → cpp-vlDl6pCL.d.cts} +4 -4
  29. package/dist/{cpp-CZUPqJt9.d.ts → cpp-vlDl6pCL.d.ts} +4 -4
  30. package/dist/{curriculumFeedSchema-DUFwFyr2.d.ts → curriculumFeedSchema-DK-TUAJZ.d.cts} +164 -9
  31. package/dist/{curriculumFeedSchema-DUFwFyr2.d.cts → curriculumFeedSchema-DK-TUAJZ.d.ts} +164 -9
  32. package/dist/detector/index.cjs +22 -6
  33. package/dist/detector/index.cjs.map +1 -1
  34. package/dist/detector/index.d.cts +2 -2
  35. package/dist/detector/index.d.ts +2 -2
  36. package/dist/detector/index.mjs +2 -1
  37. package/dist/{domainProfileDetector-DHb1T0zB.d.ts → domainProfileDetector-6pjgL-6_.d.ts} +1 -1
  38. package/dist/{domainProfileDetector-bzugdQmS.d.cts → domainProfileDetector-BsADsuRV.d.cts} +1 -1
  39. package/dist/{domainProfileSchema-CwT3Ffsw.d.ts → domainProfileSchema-779FGk14.d.cts} +38 -1
  40. package/dist/{domainProfileSchema-CwT3Ffsw.d.cts → domainProfileSchema-779FGk14.d.ts} +38 -1
  41. package/dist/extractors/index.d.cts +3 -3
  42. package/dist/extractors/index.d.ts +3 -3
  43. package/dist/feed/index.cjs +229 -33
  44. package/dist/feed/index.cjs.map +1 -1
  45. package/dist/feed/index.d.cts +32 -2
  46. package/dist/feed/index.d.ts +32 -2
  47. package/dist/feed/index.mjs +2 -2
  48. package/dist/graph/index.cjs +308 -86
  49. package/dist/graph/index.cjs.map +1 -1
  50. package/dist/graph/index.d.cts +20 -8
  51. package/dist/graph/index.d.ts +20 -8
  52. package/dist/graph/index.mjs +4 -1
  53. package/dist/{hybridGraphPipeline-dT-bio-3.d.cts → hybridGraphPipeline-B4mamLim.d.cts} +6 -2
  54. package/dist/{hybridGraphPipeline-DXQDbBEj.d.ts → hybridGraphPipeline-D2_1GUva.d.ts} +6 -2
  55. package/dist/{hybridGraphSchema-nrUeSwpU.d.ts → hybridGraphSchema-C37qxdTM.d.cts} +16 -3
  56. package/dist/{hybridGraphSchema-nrUeSwpU.d.cts → hybridGraphSchema-C37qxdTM.d.ts} +16 -3
  57. package/dist/index.cjs +3948 -3464
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.d.cts +11 -10
  60. package/dist/index.d.ts +11 -10
  61. package/dist/index.mjs +14 -11
  62. package/dist/{keywordExtractor-BxlGRMHC.d.cts → keywordExtractor-1YAFntsn.d.cts} +1 -1
  63. package/dist/{keywordExtractor-DU5XRN8-.d.ts → keywordExtractor-DWw8z_AF.d.ts} +1 -1
  64. package/dist/parsers/index.cjs.map +1 -1
  65. package/dist/parsers/index.d.cts +1 -1
  66. package/dist/parsers/index.d.ts +1 -1
  67. package/dist/parsers/index.mjs +1 -1
  68. package/dist/pipeline/index.cjs +2436 -1994
  69. package/dist/pipeline/index.cjs.map +1 -1
  70. package/dist/pipeline/index.d.cts +19 -10
  71. package/dist/pipeline/index.d.ts +19 -10
  72. package/dist/pipeline/index.mjs +11 -8
  73. package/dist/schemas/index.cjs +63 -2
  74. package/dist/schemas/index.cjs.map +1 -1
  75. package/dist/schemas/index.d.cts +3 -3
  76. package/dist/schemas/index.d.ts +3 -3
  77. package/dist/schemas/index.mjs +2 -2
  78. package/dist/utils/llmClient.cjs +344 -0
  79. package/dist/utils/llmClient.cjs.map +1 -0
  80. package/dist/utils/llmClient.d.cts +118 -0
  81. package/dist/utils/llmClient.d.ts +118 -0
  82. package/dist/utils/llmClient.mjs +4 -0
  83. package/dist/utils/llmClient.mjs.map +1 -0
  84. package/dist/utils/llmTransportResult.cjs +16 -0
  85. package/dist/utils/llmTransportResult.cjs.map +1 -0
  86. package/dist/utils/llmTransportResult.d.cts +36 -0
  87. package/dist/utils/llmTransportResult.d.ts +36 -0
  88. package/dist/utils/llmTransportResult.mjs +3 -0
  89. package/dist/utils/llmTransportResult.mjs.map +1 -0
  90. package/package.json +1 -1
  91. package/dist/chunk-BL5NBKPH.mjs.map +0 -1
  92. package/dist/chunk-DRVOV5ZD.mjs.map +0 -1
  93. package/dist/chunk-E4GCCWJO.mjs.map +0 -1
  94. package/dist/chunk-G7HLFMD2.mjs.map +0 -1
  95. package/dist/chunk-TWKIVUTQ.mjs.map +0 -1
  96. package/dist/chunk-TYXSJUQU.mjs.map +0 -1
  97. package/dist/chunk-VCPIWJ7R.mjs.map +0 -1
  98. package/dist/curriculumFeedEmitter-T7NUBLAM.mjs +0 -4
  99. package/dist/curriculumFeedEmitter-T7NUBLAM.mjs.map +0 -1
  100. package/dist/llmClient-ysPhLjcH.d.cts +0 -16
  101. package/dist/llmClient-ysPhLjcH.d.ts +0 -16
@@ -0,0 +1,325 @@
1
+ import { transportResultFrom } from './chunk-IBVXXVGE.mjs';
2
+
3
+ // src/utils/llmClient.ts
4
+ function summarizeProvenance(callLog) {
5
+ const providers = [];
6
+ const models = [];
7
+ let failed = 0;
8
+ for (const r of callLog) {
9
+ if (!providers.includes(r.provider)) providers.push(r.provider);
10
+ if (!models.includes(r.model)) models.push(r.model);
11
+ if (!r.ok) failed++;
12
+ }
13
+ return { providers, models, calls: callLog.length, failed_calls: failed, degraded: failed > 0 };
14
+ }
15
+ async function responseJsonCompat(response) {
16
+ const text = await response.text();
17
+ try {
18
+ return JSON.parse(text);
19
+ } catch {
20
+ const start = text.indexOf("{");
21
+ if (start < 0) {
22
+ throw new LlmClientError(`LLM API returned a non-JSON body: ${text.slice(0, 200)}`);
23
+ }
24
+ let depth = 0;
25
+ let inString = false;
26
+ let escape = false;
27
+ for (let i = start; i < text.length; i++) {
28
+ const ch = text[i];
29
+ if (escape) {
30
+ escape = false;
31
+ continue;
32
+ }
33
+ if (ch === "\\") {
34
+ escape = true;
35
+ continue;
36
+ }
37
+ if (ch === '"') {
38
+ inString = !inString;
39
+ continue;
40
+ }
41
+ if (inString) continue;
42
+ if (ch === "{") depth++;
43
+ if (ch === "}") {
44
+ depth--;
45
+ if (depth === 0) {
46
+ return JSON.parse(text.slice(start, i + 1));
47
+ }
48
+ }
49
+ }
50
+ throw new LlmClientError(`LLM API returned an unterminated JSON body: ${text.slice(0, 200)}`);
51
+ }
52
+ }
53
+ var LlmClientError = class extends Error {
54
+ constructor(message, status, cause) {
55
+ super(message);
56
+ this.status = status;
57
+ this.cause = cause;
58
+ this.name = "LlmClientError";
59
+ }
60
+ };
61
+ function build9routerProvider(model) {
62
+ const apiKey = process.env.NINEROUTER_API_KEY;
63
+ if (!apiKey) return null;
64
+ const baseUrl = (process.env.NINEROUTER_BASE_URL || "https://ai-router.orchable.app/v1").replace(/^["']|["']$/g, "").trim();
65
+ const label = "9router";
66
+ const modelName = (model || process.env.NINEROUTER_MODEL || "laguna-s-2.1").trim();
67
+ return { label, baseUrl, apiKey, model: modelName };
68
+ }
69
+ function resolve9routerChain(configModel) {
70
+ const primary = build9routerProvider(configModel);
71
+ if (!primary) return [];
72
+ const alt = process.env.NINEROUTER_MODEL_ALT && process.env.NINEROUTER_MODEL_ALT.trim();
73
+ if (!alt) return [primary];
74
+ const secondary = build9routerProvider(alt);
75
+ if (!secondary) return [primary];
76
+ return primary.model === secondary.model ? [primary] : [primary, secondary];
77
+ }
78
+ var MIN_REQUEST_INTERVAL_MS = 2500;
79
+ var requestIntervalMs = Number(process.env.LLM_MIN_REQUEST_INTERVAL_MS || MIN_REQUEST_INTERVAL_MS);
80
+ var lastRequestAt = 0;
81
+ async function pacedDelay() {
82
+ const wait = lastRequestAt + requestIntervalMs - Date.now();
83
+ if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));
84
+ lastRequestAt = Date.now();
85
+ }
86
+ function createLlmClient(config) {
87
+ const chain = resolve9routerChain(config?.model);
88
+ const primary = chain[0];
89
+ if (!primary) {
90
+ throw new LlmClientError(
91
+ "No 9router provider configured. Set NINEROUTER_API_KEY and NINEROUTER_MODEL (or the NINEROUTER_* env defaults expected by the kit)."
92
+ );
93
+ }
94
+ primary.apiKey;
95
+ primary.baseUrl;
96
+ const model = primary.model;
97
+ if (config?.minRequestIntervalMs !== void 0) {
98
+ requestIntervalMs = Math.max(0, config.minRequestIntervalMs);
99
+ }
100
+ const callLog = [];
101
+ async function chat(messages, options) {
102
+ const temperature = options?.temperature ?? config?.temperature ?? 0.1;
103
+ const maxTokens = options?.maxTokens ?? config?.maxTokens ?? parseInt(process.env.LLM_MAX_TOKENS || "65536", 10);
104
+ const attempts = [];
105
+ let lastError = null;
106
+ const TRANSIENT = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
107
+ const REQUEST_TIMEOUT_MS = Number(process.env.LLM_REQUEST_TIMEOUT_MS || 3e5);
108
+ const MAX_CHAIN_ROUNDS = 3;
109
+ for (let round = 1; round <= MAX_CHAIN_ROUNDS; round++) {
110
+ if (round > 1) {
111
+ attempts.push("round " + (round - 1) + " failed \u2014 backing off 20s before rewalking the chain");
112
+ await new Promise((resolve) => setTimeout(resolve, 2e4));
113
+ }
114
+ for (const provider of chain) {
115
+ const recIdx = callLog.push({ provider: provider.label, model: provider.model, ok: false }) - 1;
116
+ try {
117
+ const headers = {
118
+ "Content-Type": "application/json",
119
+ "Authorization": `Bearer ${provider.apiKey}`
120
+ };
121
+ const payload = {
122
+ model: provider.model,
123
+ messages,
124
+ temperature,
125
+ max_tokens: maxTokens
126
+ };
127
+ await pacedDelay();
128
+ const doFetch = () => fetch(`${provider.baseUrl}/chat/completions`, {
129
+ method: "POST",
130
+ headers: {
131
+ "Content-Type": "application/json",
132
+ "Authorization": `Bearer ${provider.apiKey}`
133
+ },
134
+ body: JSON.stringify({
135
+ model: provider.model,
136
+ messages,
137
+ temperature,
138
+ max_tokens: maxTokens
139
+ }),
140
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
141
+ });
142
+ let response;
143
+ try {
144
+ response = await doFetch();
145
+ } catch (netErr) {
146
+ attempts.push(`${provider.label}: network error (${netErr instanceof Error ? netErr.message : String(netErr)}) \u2014 retrying once`);
147
+ await new Promise((r) => setTimeout(r, 5e3));
148
+ lastRequestAt = Date.now();
149
+ response = await doFetch();
150
+ }
151
+ if (!response.ok) {
152
+ const body = await response.text().catch(() => "");
153
+ const err = new LlmClientError(
154
+ `[${provider.label}] LLM API error: ${response.status} ${response.statusText} \u2014 ${body.slice(0, 200)}`,
155
+ response.status
156
+ );
157
+ if (response.status === 429) {
158
+ const retryAfterRaw = response.headers.get("retry-after");
159
+ const retryAfterMs = Math.min(
160
+ 6e4,
161
+ Math.max(15e3, (Number.isFinite(Number(retryAfterRaw)) ? Number(retryAfterRaw) : 20) * 1e3)
162
+ );
163
+ attempts.push(`${provider.label}: 429 rate-limited \u2014 backing off ${Math.round(retryAfterMs / 1e3)}s`);
164
+ await new Promise((resolve) => setTimeout(resolve, retryAfterMs));
165
+ lastRequestAt = Date.now();
166
+ const retry = await fetch(`${provider.baseUrl}/chat/completions`, {
167
+ method: "POST",
168
+ headers,
169
+ body: JSON.stringify(payload),
170
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
171
+ });
172
+ if (!retry.ok) throw err;
173
+ const retryData = await responseJsonCompat(retry);
174
+ const retryContent2 = retryData.choices?.[0]?.message?.content || "";
175
+ if (!retryContent2.trim()) throw err;
176
+ callLog[recIdx] = { ...callLog[recIdx], ok: true };
177
+ const transport2 = transportResultFrom(provider.label, retryData.model || provider.model, {
178
+ usage: retryData.usage,
179
+ framingStripped: true
180
+ });
181
+ return {
182
+ content: retryContent2,
183
+ model: retryData.model || provider.model,
184
+ provider: provider.label,
185
+ attempts: attempts.slice(),
186
+ usage: retryData.usage,
187
+ finishReason: retryData.choices?.[0]?.finish_reason,
188
+ _transport: transport2
189
+ };
190
+ }
191
+ if (TRANSIENT.has(response.status)) {
192
+ await new Promise((r) => setTimeout(r, 3e3));
193
+ const retry = await fetch(`${provider.baseUrl}/chat/completions`, {
194
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
195
+ method: "POST",
196
+ headers: {
197
+ "Content-Type": "application/json",
198
+ "Authorization": `Bearer ${provider.apiKey}`
199
+ },
200
+ body: JSON.stringify({
201
+ model: provider.model,
202
+ messages,
203
+ temperature,
204
+ max_tokens: maxTokens
205
+ })
206
+ });
207
+ if (!retry.ok) throw err;
208
+ const retryData = await responseJsonCompat(retry);
209
+ const retryContent = retryData.choices?.[0]?.message?.content || "";
210
+ callLog[recIdx] = { ...callLog[recIdx], ok: true };
211
+ const transport2 = transportResultFrom(provider.label, retryData.model || provider.model, {
212
+ usage: retryData.usage,
213
+ framingStripped: true
214
+ });
215
+ return {
216
+ content: retryContent,
217
+ model: retryData.model || provider.model,
218
+ provider: provider.label,
219
+ attempts: attempts.slice(),
220
+ usage: retryData.usage,
221
+ finishReason: retryData.choices?.[0]?.finish_reason,
222
+ _transport: transport2
223
+ };
224
+ }
225
+ throw err;
226
+ }
227
+ const data = await responseJsonCompat(response);
228
+ const wrappedError = data.error;
229
+ if (wrappedError) {
230
+ throw new LlmClientError("[" + provider.label + "] upstream error " + (wrappedError.code ?? "") + ": " + (wrappedError.message ?? "unknown"));
231
+ }
232
+ const content = data.choices?.[0]?.message?.content || "";
233
+ if (!content.trim()) {
234
+ throw new LlmClientError("[" + provider.label + "] empty completion returned");
235
+ }
236
+ const transport = transportResultFrom(provider.label, data.model || provider.model, {
237
+ usage: data.usage,
238
+ framingStripped: true
239
+ });
240
+ return {
241
+ content,
242
+ model: data.model || provider.model,
243
+ provider: provider.label,
244
+ attempts: attempts.slice(),
245
+ usage: data.usage,
246
+ finishReason: data.choices?.[0]?.finish_reason,
247
+ _transport: transport
248
+ };
249
+ } catch (err) {
250
+ const msg = err instanceof Error ? err.message : String(err);
251
+ callLog[recIdx] = { ...callLog[recIdx], ok: false, error: msg.slice(0, 200) };
252
+ attempts.push(`${provider.label}: ${msg}`);
253
+ lastError = err instanceof LlmClientError ? err : new LlmClientError(`[${provider.label}] ${msg}`);
254
+ }
255
+ }
256
+ if (round < MAX_CHAIN_ROUNDS) continue;
257
+ throw lastError ?? new LlmClientError("All LLM providers failed (empty chain).");
258
+ }
259
+ throw lastError ?? new LlmClientError("All LLM providers failed after " + MAX_CHAIN_ROUNDS + " rounds.");
260
+ }
261
+ return { chat, model, callLog };
262
+ }
263
+ function parseJsonFromLlm(text) {
264
+ let cleaned = text.trim();
265
+ if (cleaned.startsWith("```json")) {
266
+ cleaned = cleaned.slice(7);
267
+ } else if (cleaned.startsWith("```")) {
268
+ cleaned = cleaned.slice(3);
269
+ }
270
+ if (cleaned.endsWith("```")) {
271
+ cleaned = cleaned.slice(0, -3);
272
+ }
273
+ cleaned = cleaned.trim();
274
+ try {
275
+ return JSON.parse(cleaned);
276
+ } catch {
277
+ const start = cleaned.indexOf("{");
278
+ if (start >= 0) {
279
+ let depth = 0;
280
+ let inString = false;
281
+ let escape = false;
282
+ for (let i = start; i < cleaned.length; i++) {
283
+ const ch = cleaned[i];
284
+ if (escape) {
285
+ escape = false;
286
+ continue;
287
+ }
288
+ if (ch === "\\") {
289
+ escape = true;
290
+ continue;
291
+ }
292
+ if (ch === '"') {
293
+ inString = !inString;
294
+ continue;
295
+ }
296
+ if (inString) continue;
297
+ if (ch === "{") depth++;
298
+ if (ch === "}") {
299
+ depth--;
300
+ if (depth === 0) {
301
+ try {
302
+ return JSON.parse(cleaned.slice(start, i + 1));
303
+ } catch {
304
+ }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ throw new LlmClientError(`Failed to parse JSON from LLM response: ${cleaned.slice(0, 200)}`);
310
+ }
311
+ }
312
+ async function llmChatJson(client, systemPrompt, userPrompt, options) {
313
+ const result = await client.chat(
314
+ [
315
+ { role: "system", content: systemPrompt },
316
+ { role: "user", content: userPrompt }
317
+ ],
318
+ options
319
+ );
320
+ return parseJsonFromLlm(result.content);
321
+ }
322
+
323
+ export { LlmClientError, build9routerProvider, createLlmClient, llmChatJson, parseJsonFromLlm, resolve9routerChain, responseJsonCompat, summarizeProvenance };
324
+ //# sourceMappingURL=chunk-7A2SG466.mjs.map
325
+ //# sourceMappingURL=chunk-7A2SG466.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/llmClient.ts"],"names":["transport"],"mappings":"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","file":"chunk-7A2SG466.mjs","sourcesContent":["/**\n * LLM client wrapper — provides a unified interface for LLM calls\n * used by the graph generation pipeline.\n *\n * Supports OpenAI-compatible APIs via environment variables:\n * LLM_API_KEY, LLM_BASE_URL, LLM_MODEL (or LLM_TIER_FAST / LLM_TIER_STRONG)\n */\n\nimport { transportResultFrom, type LlmTransportResult } from './llmTransportResult';\nexport { transportResultFrom, type LlmTransportResult } from './llmTransportResult';\n\nexport interface LlmClientConfig {\n apiKey?: string;\n baseUrl?: string;\n model?: string;\n temperature?: number;\n maxTokens?: number;\n /**\n * Minimum ms between requests, enforced process-wide so free tiers are not\n * rate-limited by a burst. Defaults to LLM_MIN_REQUEST_INTERVAL_MS (2500).\n * Set 0 to disable — correct when several pipelines run concurrently in one\n * process and per-pipeline pacing is enough, or when the caller owns the\n * rate limit itself (serverless).\n */\n minRequestIntervalMs?: number;\n}\n\n/** One provider attempt. Kept per client instance so provenance never leaks across requests. */\nexport interface LlmCallRecord {\n provider: string;\n model: string;\n ok: boolean;\n error?: string;\n}\n\nexport interface LlmProvenance {\n providers: string[];\n models: string[];\n calls: number;\n failed_calls: number;\n /** true when at least one provider fell through to the next in the chain. */\n degraded: boolean;\n}\n\nexport function summarizeProvenance(callLog: LlmCallRecord[]): LlmProvenance {\n const providers: string[] = [];\n const models: string[] = [];\n let failed = 0;\n for (const r of callLog) {\n if (!providers.includes(r.provider)) providers.push(r.provider);\n if (!models.includes(r.model)) models.push(r.model);\n if (!r.ok) failed++;\n }\n return { providers, models, calls: callLog.length, failed_calls: failed, degraded: failed > 0 };\n}\n\n/**\n * Some OpenAI-compatible gateways (observed: 9router / ai-router.orchable.app)\n * paste SSE stream framing onto NON-stream responses: a leading blank line,\n * then the chat.completion JSON object, wrapped with a trailing `data: [DONE]`.\n * response.json() throws \"Unexpected non-whitespace character after JSON\", so\n * read the text and cut the body back to the first balanced JSON object.\n */\nexport async function responseJsonCompat(response: Response): Promise<Record<string, any>> {\n const text = await response.text();\n try {\n return JSON.parse(text);\n } catch {\n const start = text.indexOf('{');\n if (start < 0) {\n throw new LlmClientError(`LLM API returned a non-JSON body: ${text.slice(0, 200)}`);\n }\n let depth = 0;\n let inString = false;\n let escape = false;\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (escape) { escape = false; continue; }\n if (ch === '\\\\') { escape = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === '{') depth++;\n if (ch === '}') {\n depth--;\n if (depth === 0) {\n return JSON.parse(text.slice(start, i + 1));\n }\n }\n }\n throw new LlmClientError(`LLM API returned an unterminated JSON body: ${text.slice(0, 200)}`);\n }\n}\n\n/** One callable provider endpoint in the fallback chain. */\nexport interface ProviderSpec {\n label: string;\n baseUrl: string;\n apiKey: string;\n model: string;\n}\n\nexport interface LlmClient {\n chat(messages: LlmChatMessage[], options?: { temperature?: number; maxTokens?: number }): Promise<LlmChatResult>;\n model: string;\n /** Every provider attempt made through this client — the provenance of the graph it produced. */\n callLog: LlmCallRecord[];\n}\n\nexport interface LlmChatMessage {\n role: 'system' | 'user' | 'assistant';\n content: string;\n}\n\nexport interface LlmChatResult {\n content: string;\n model: string;\n /** Provider label that ultimately served the request. */\n provider?: string;\n /** What happened per provider when fallback occurred (empty when first try succeeded). */\n attempts?: string[];\n /** 'stop' when complete; 'length' means the response was TRUNCATED by max_tokens. */\n finishReason?: string;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n finishReason?: string;\n };\n _transport?: LlmTransportResult;\n}\n\nexport class LlmClientError extends Error {\n constructor(\n message: string,\n public readonly status?: number,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = 'LlmClientError';\n }\n}\n\n/**\n * Create an LLM client from environment variables.\n */\nexport function build9routerProvider(model?: string): ProviderSpec | null {\n const apiKey = process.env.NINEROUTER_API_KEY;\n if (!apiKey) return null;\n const baseUrl = (process.env.NINEROUTER_BASE_URL || 'https://ai-router.orchable.app/v1').replace(/^[\"']|[\"']$/g, '').trim();\n const label = '9router';\n const modelName = (model || process.env.NINEROUTER_MODEL || 'laguna-s-2.1').trim();\n return { label, baseUrl, apiKey, model: modelName };\n}\n\n/**\n * Synthesize a 9router-only chain from the configured primary model.\n * POLICY (user directive 2026-10-07): 9router free-tier ONLY — laguna-s-2.1\n * and nemotron-ultra ride the same pool from different upstream providers.\n * The returned chain is PRIMARY then SAME-POOL SECONDARY; the caller owns any\n * fallback semantics and MUST NOT silently branch to another gateway.\n */\nexport function resolve9routerChain(configModel?: string): ProviderSpec[] {\n const primary = build9routerProvider(configModel);\n if (!primary) return [];\n const alt = process.env.NINEROUTER_MODEL_ALT && process.env.NINEROUTER_MODEL_ALT.trim();\n if (!alt) return [primary];\n const secondary = build9routerProvider(alt);\n if (!secondary) return [primary];\n return primary.model === secondary.model ? [primary] : [primary, secondary];\n}\n\n// Free-tier rate limits (NVIDIA NIM, OpenRouter) trip when a pipeline fires\n// many requests in a burst. Global pacing keeps us under the limit; a single\n// isolated call still goes through immediately.\nconst MIN_REQUEST_INTERVAL_MS = 2_500;\nlet requestIntervalMs = Number(process.env.LLM_MIN_REQUEST_INTERVAL_MS || MIN_REQUEST_INTERVAL_MS);\nlet lastRequestAt = 0;\n\nasync function pacedDelay(): Promise<void> {\n const wait = lastRequestAt + requestIntervalMs - Date.now();\n if (wait > 0) await new Promise((resolve) => setTimeout(resolve, wait));\n lastRequestAt = Date.now();\n}\n\nexport function createLlmClient(config?: LlmClientConfig): LlmClient {\n const chain = resolve9routerChain(config?.model);\n const primary = chain[0];\n if (!primary) {\n throw new LlmClientError(\n 'No 9router provider configured. Set NINEROUTER_API_KEY and NINEROUTER_MODEL (or the NINEROUTER_* env defaults expected by the kit).',\n );\n }\n const apiKey = primary.apiKey;\n const baseUrl = primary.baseUrl;\n const model = primary.model;\n if (config?.minRequestIntervalMs !== undefined) {\n requestIntervalMs = Math.max(0, config.minRequestIntervalMs);\n }\n // Per-client, so concurrent pipelines never inherit each other's provenance.\n const callLog: LlmCallRecord[] = [];\n\n async function chat(\n messages: LlmChatMessage[],\n options?: { temperature?: number; maxTokens?: number },\n ): Promise<LlmChatResult> {\n const temperature = options?.temperature ?? config?.temperature ?? 0.1;\n const maxTokens = options?.maxTokens ?? config?.maxTokens ?? parseInt(process.env.LLM_MAX_TOKENS || '65536', 10);\n\n const attempts: string[] = [];\n let lastError: LlmClientError | null = null;\n const TRANSIENT = new Set([408, 429, 500, 502, 503, 504]);\n // Hard per-request cap so a hanging provider degrades to a transient error\n // and the chain falls through instead of blocking the pipeline forever.\n // Reasoning presets (e.g. deepseek-v4-flash) can legitimately think for\n // minutes on large structured outputs — 5 minutes default, env-overridable.\n const REQUEST_TIMEOUT_MS = Number(process.env.LLM_REQUEST_TIMEOUT_MS || 300_000);\n const MAX_CHAIN_ROUNDS = 3;\n for (let round = 1; round <= MAX_CHAIN_ROUNDS; round++) {\n if (round > 1) {\n // Whole chain failed transiently (rate limits / upstream 5xx) — back\n // off and rewalk the chain from the primary instead of dying.\n attempts.push('round ' + (round - 1) + ' failed — backing off 20s before rewalking the chain');\n await new Promise((resolve) => setTimeout(resolve, 20_000));\n }\n for (const provider of chain) {\n // Provenance: opened as failed, flipped to ok on the paths that return.\n const recIdx = callLog.push({ provider: provider.label, model: provider.model, ok: false }) - 1;\n try {\n const headers = {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${provider.apiKey}`,\n };\n const payload = {\n model: provider.model,\n messages,\n temperature,\n max_tokens: maxTokens,\n };\n await pacedDelay();\n const doFetch = (): Promise<Response> => fetch(`${provider.baseUrl}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${provider.apiKey}`,\n },\n body: JSON.stringify({\n model: provider.model,\n messages,\n temperature,\n max_tokens: maxTokens,\n }),\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n // Transient network failures (DNS blip, TLS reset, \"fetch failed\") get\n // one same-provider retry before falling through the chain.\n let response: Response;\n try {\n response = await doFetch();\n } catch (netErr) {\n attempts.push(`${provider.label}: network error (${netErr instanceof Error ? netErr.message : String(netErr)}) — retrying once`);\n await new Promise((r) => setTimeout(r, 5_000));\n lastRequestAt = Date.now();\n response = await doFetch();\n }\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n const err = new LlmClientError(\n `[${provider.label}] LLM API error: ${response.status} ${response.statusText} — ${body.slice(0, 200)}`,\n response.status,\n );\n if (response.status === 429) {\n // Rate-limited: honor Retry-After (bounded) and retry once on the\n // same provider before falling through to the next one.\n const retryAfterRaw = response.headers.get('retry-after');\n const retryAfterMs = Math.min(\n 60_000,\n Math.max(15_000, (Number.isFinite(Number(retryAfterRaw)) ? Number(retryAfterRaw) : 20) * 1_000),\n );\n attempts.push(`${provider.label}: 429 rate-limited — backing off ${Math.round(retryAfterMs / 1000)}s`);\n await new Promise((resolve) => setTimeout(resolve, retryAfterMs));\n lastRequestAt = Date.now();\n const retry = await fetch(`${provider.baseUrl}/chat/completions`, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n });\n if (!retry.ok) throw err;\n const retryData = await responseJsonCompat(retry) as {\n choices?: Array<{ message?: { content?: string }; finish_reason?: string }>;\n model?: string;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n };\n const retryContent2 = retryData.choices?.[0]?.message?.content || '';\n if (!retryContent2.trim()) throw err;\n callLog[recIdx] = { ...callLog[recIdx], ok: true };\n const transport = transportResultFrom(provider.label, retryData.model || provider.model, {\n usage: retryData.usage,\n framingStripped: true,\n });\n return {\n content: retryContent2,\n model: retryData.model || provider.model,\n provider: provider.label,\n attempts: attempts.slice(),\n usage: retryData.usage,\n finishReason: retryData.choices?.[0]?.finish_reason,\n _transport: transport,\n };\n }\n if (TRANSIENT.has(response.status)) {\n await new Promise((r) => setTimeout(r, 3000));\n const retry = await fetch(`${provider.baseUrl}/chat/completions`, {\n signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${provider.apiKey}`,\n },\n body: JSON.stringify({\n model: provider.model,\n messages,\n temperature,\n max_tokens: maxTokens,\n }),\n });\n if (!retry.ok) throw err; // still transient-failing — fall through to next provider\n const retryData = await responseJsonCompat(retry) as {\n choices?: Array<{ message?: { content?: string }; finish_reason?: string }>;\n model?: string;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n };\n const retryContent = retryData.choices?.[0]?.message?.content || '';\n callLog[recIdx] = { ...callLog[recIdx], ok: true };\n const transport = transportResultFrom(provider.label, retryData.model || provider.model, {\n usage: retryData.usage,\n framingStripped: true,\n });\n return {\n content: retryContent,\n model: retryData.model || provider.model,\n provider: provider.label,\n attempts: attempts.slice(),\n usage: retryData.usage,\n finishReason: retryData.choices?.[0]?.finish_reason,\n _transport: transport,\n };\n }\n throw err;\n }\n const data = await responseJsonCompat(response) as {\n choices?: Array<{ message?: { content?: string }; finish_reason?: string }>;\n model?: string;\n usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number };\n };\n const wrappedError = (data as { error?: { message?: string; code?: number } }).error;\n if (wrappedError) {\n // OpenRouter proxies some upstream failures as HTTP 200 with an\n // {\"error\": {...}} body — treat as transient and fall through.\n throw new LlmClientError('[' + provider.label + '] upstream error ' + (wrappedError.code ?? '') + ': ' + (wrappedError.message ?? 'unknown'));\n }\n const content = data.choices?.[0]?.message?.content || '';\n if (!content.trim()) {\n // Empty completion (provider outage / content filter) is transient —\n // fall through to the next provider instead of returning unusable ''.\n throw new LlmClientError('[' + provider.label + '] empty completion returned');\n }\n const transport = transportResultFrom(provider.label, data.model || provider.model, {\n usage: data.usage,\n framingStripped: true,\n });\n return {\n content,\n model: data.model || provider.model,\n provider: provider.label,\n attempts: attempts.slice(),\n usage: data.usage,\n finishReason: data.choices?.[0]?.finish_reason,\n _transport: transport,\n };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n callLog[recIdx] = { ...callLog[recIdx], ok: false, error: msg.slice(0, 200) };\n attempts.push(`${provider.label}: ${msg}`);\n lastError = err instanceof LlmClientError ? err : new LlmClientError(`[${provider.label}] ${msg}`);\n // try next provider\n }\n }\n if (round < MAX_CHAIN_ROUNDS) continue; // rewalk the chain\n throw lastError ?? new LlmClientError('All LLM providers failed (empty chain).');\n }\n throw lastError ?? new LlmClientError('All LLM providers failed after ' + MAX_CHAIN_ROUNDS + ' rounds.');\n }\n\n return { chat, model, callLog };\n}\n\n/**\n * Parse JSON from LLM response, handling markdown code fences.\n */\nexport function parseJsonFromLlm(text: string): Record<string, unknown> {\n // Strip markdown code fences if present\n let cleaned = text.trim();\n if (cleaned.startsWith('```json')) {\n cleaned = cleaned.slice(7);\n } else if (cleaned.startsWith('```')) {\n cleaned = cleaned.slice(3);\n }\n if (cleaned.endsWith('```')) {\n cleaned = cleaned.slice(0, -3);\n }\n cleaned = cleaned.trim();\n\n try {\n return JSON.parse(cleaned) as Record<string, unknown>;\n } catch {\n // Try to find first complete JSON object using brace-depth tracking\n const start = cleaned.indexOf('{');\n if (start >= 0) {\n let depth = 0;\n let inString = false;\n let escape = false;\n for (let i = start; i < cleaned.length; i++) {\n const ch = cleaned[i];\n if (escape) { escape = false; continue; }\n if (ch === '\\\\') { escape = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === '{') depth++;\n if (ch === '}') {\n depth--;\n if (depth === 0) {\n try {\n return JSON.parse(cleaned.slice(start, i + 1)) as Record<string, unknown>;\n } catch {\n // keep looking\n }\n }\n }\n }\n }\n throw new LlmClientError(`Failed to parse JSON from LLM response: ${cleaned.slice(0, 200)}`);\n }\n}\n\n/**\n * Call LLM and parse JSON response.\n */\nexport async function llmChatJson(\n client: ReturnType<typeof createLlmClient>,\n systemPrompt: string,\n userPrompt: string,\n options?: { temperature?: number; maxTokens?: number },\n): Promise<Record<string, unknown>> {\n const result = await client.chat(\n [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n options,\n );\n\n return parseJsonFromLlm(result.content);\n}\n"]}
@@ -50,6 +50,14 @@ var FeedNodeSchema = z.object({
50
50
  reason: z.string().default("")
51
51
  })).optional(),
52
52
  is_core: z.boolean().default(false),
53
+ /**
54
+ * Prerequisite concept ids this node needs that NO node in this feed provides —
55
+ * i.e. the learner must already know them before starting. Previously these were
56
+ * silently dropped (knowledge path) or crashed emission (both hybrid paths), so a
57
+ * course silently lost its entry requirements. Kept on the node so the mapping
58
+ * "concept X needs prior knowledge Y" survives; a course-level roll-up is derivable.
59
+ */
60
+ external_prerequisites: z.array(z.string()).default([]),
53
61
  // Depth layer text (optional, additive — old feeds validate unchanged).
54
62
  // Carries the ULO/CIO/SIO authored text from the KnowledgeGraph concept
55
63
  // so downstream consumers (KX generator) can inject depth-appropriate
@@ -85,12 +93,28 @@ var FeedPhaseSchema = z.object({
85
93
  product_completion: z.string().min(1),
86
94
  node_ids: z.array(z.string()).min(1)
87
95
  });
96
+ var FeedDiagnosticSchema = z.object({
97
+ code: z.string().min(1),
98
+ severity: z.enum(["info", "warning", "error"]),
99
+ message: z.string(),
100
+ node_ids: z.array(z.string()).default([])
101
+ });
102
+ var FeedProvenanceSchema = z.object({
103
+ providers: z.array(z.string()).default([]),
104
+ models: z.array(z.string()).default([]),
105
+ calls: z.number().int().nonnegative().default(0),
106
+ failed_calls: z.number().int().nonnegative().default(0),
107
+ degraded: z.boolean().default(false)
108
+ });
88
109
  var CurriculumFeedSchema = z.object({
89
110
  schema_version: z.literal(2),
90
111
  source: z.object({
91
112
  graph_type: z.enum(["project_graph", "knowledge_graph", "hybrid_graph"]),
113
+ /** DERIVED from diagnostics — do not write to it directly. */
92
114
  warnings: z.array(z.string()).default([]),
93
- hallucination_count: z.number().int().nonnegative().default(0)
115
+ hallucination_count: z.number().int().nonnegative().default(0),
116
+ diagnostics: z.array(FeedDiagnosticSchema).default([]),
117
+ provenance: FeedProvenanceSchema.optional()
94
118
  }),
95
119
  learning_nodes: z.array(FeedNodeSchema).min(1, "curriculum feed must contain at least one learning node"),
96
120
  dependency_edges: z.array(FeedEdgeSchema).default([]),
@@ -123,6 +147,15 @@ function validateCurriculumFeed(feed) {
123
147
  }
124
148
  const phaseIds = parsed.phases.map((p) => p.id);
125
149
  if (new Set(phaseIds).size !== phaseIds.length) throw new Error("curriculum feed contains duplicate phase ids");
150
+ const phaseOrders = parsed.phases.map((p) => p.order);
151
+ if (new Set(phaseOrders).size !== phaseOrders.length) {
152
+ throw new Error("curriculum feed contains duplicate phase orders: " + phaseOrders.join(", "));
153
+ }
154
+ for (let i = 1; i < phaseOrders.length; i++) {
155
+ if (phaseOrders[i] <= phaseOrders[i - 1]) {
156
+ throw new Error("curriculum feed phase orders are not ascending: " + phaseOrders.join(", "));
157
+ }
158
+ }
126
159
  if (parsed.phases.length > 0) {
127
160
  const phaseIndexOf = /* @__PURE__ */ new Map();
128
161
  parsed.phases.forEach((p, i) => {
@@ -142,6 +175,6 @@ function validateCurriculumFeed(feed) {
142
175
  return parsed;
143
176
  }
144
177
 
145
- export { CurriculumFeedSchema, FeedEdgeSchema, FeedGroupingSchema, FeedKeywordsSchema, FeedNodeSchema, FeedPhaseSchema, FeedScaffoldCandidateSchema, validateCurriculumFeed };
146
- //# sourceMappingURL=chunk-E4GCCWJO.mjs.map
147
- //# sourceMappingURL=chunk-E4GCCWJO.mjs.map
178
+ export { CurriculumFeedSchema, FeedDiagnosticSchema, FeedEdgeSchema, FeedGroupingSchema, FeedKeywordsSchema, FeedNodeSchema, FeedPhaseSchema, FeedProvenanceSchema, FeedScaffoldCandidateSchema, validateCurriculumFeed };
179
+ //# sourceMappingURL=chunk-CGNXGVHV.mjs.map
180
+ //# sourceMappingURL=chunk-CGNXGVHV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schemas/curriculumFeedSchema.ts"],"names":[],"mappings":"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","file":"chunk-CGNXGVHV.mjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * CurriculumFeed v2 — normalized, planner-ready projection of a domain graph.\n * Consumers: curriculum-kit Curriculum Planner (see docs/ideas/2026-08-30-curriculum-feed-v2-output.md).\n * Guarantees enforced here: cycle-free is the CALLER's verifier job; the feed\n * itself guarantees resolution (edges/keywords reference emitted nodes) and\n * non-emptiness.\n */\n\nexport const FeedKeywordsSchema = z.object({\n all: z.array(z.string()).default([]),\n new: z.array(z.string()).default([]),\n prerequisite: z.array(z.string()).default([]),\n});\n\nexport const FeedScaffoldCandidateSchema = z.object({\n item: z.string(),\n reason: z.string().default(''),\n minutes_saved: z.number().int().nonnegative().default(0),\n suggest: z.enum(['provide_full', 'provide_partial', 'provide_reference']).default('provide_full'),\n});\n\nexport const FeedNodeSchema = z.object({\n id: z.string().min(1),\n kind: z.enum(['skill', 'concept', 'product_step']),\n name: z.string().min(1),\n concept_codes: z.array(z.string()).default([]),\n keywords: FeedKeywordsSchema,\n estimated_minutes: z.number().int().nonnegative(),\n bloom_hint: z.enum(['Remember', 'Understand', 'Apply', 'Analyze', 'Evaluate', 'Create']).nullable().default(null),\n depth_hint: z.enum(['ulo', 'cio', 'sio']).nullable().default(null),\n completion_level: z.enum(['mvp', 'standard', 'stretch']).default('standard'),\n scaffolding: z.object({\n scaffold_candidates: z.array(FeedScaffoldCandidateSchema).default([]),\n core_value_minutes: z.number().int().nonnegative().default(0),\n }).default({ scaffold_candidates: [], core_value_minutes: 0 }),\n references: z.array(z.object({ file: z.string().min(1), evidence: z.string().default('') })).default([]),\n feature_id: z.string().nullable().default(null),\n concept_ref: z.string().nullable().default(null),\n // P52/T2.1: outcome.user_visible projected from the ProjectGraph step — the\n // tangible checkpoint the session-cutting planner aims for (empty when the\n // step declares no user-visible result). Concepts carry '' by default.\n user_visible_deliverable: z.string().default(''),\n // P52/T3.1 — depth-reconcile inputs (optional/defaulted so old feeds\n // validate unchanged):\n // • depth_variants: minutes to teach this node at each depth level.\n // ULO (know-it-exists) ≤ CIO (mechanism) ≤ SIO (implementation). All 3\n // required so a downgrade never turns an anchor teach into a zero.\n // • depth_scaffold_candidates: parallel to scaffold_candidates but\n // acts on DEPTH (e.g. SIO→CIO) instead of lesson time.\n // • is_core: concept is a Master-Tree core concept — reconciler must\n // never downgrade it; escalate instead.\n depth_variants: z.object({ ulo: z.number().int().nonnegative(), cio: z.number().int().nonnegative(), sio: z.number().int().nonnegative() }).optional(),\n depth_scaffold_candidates: z.array(z.object({\n from_depth: z.enum(['ulo', 'cio', 'sio']),\n to_depth: z.enum(['ulo', 'cio', 'sio']),\n minutes_saved: z.number().int().nonnegative(),\n reason: z.string().default(''),\n })).optional(),\n is_core: z.boolean().default(false),\n /**\n * Prerequisite concept ids this node needs that NO node in this feed provides —\n * i.e. the learner must already know them before starting. Previously these were\n * silently dropped (knowledge path) or crashed emission (both hybrid paths), so a\n * course silently lost its entry requirements. Kept on the node so the mapping\n * \"concept X needs prior knowledge Y\" survives; a course-level roll-up is derivable.\n */\n external_prerequisites: z.array(z.string()).default([]),\n // Depth layer text (optional, additive — old feeds validate unchanged).\n // Carries the ULO/CIO/SIO authored text from the KnowledgeGraph concept\n // so downstream consumers (KX generator) can inject depth-appropriate\n // material into their prompts without a second LLM call.\n depth_context: z.object({\n ulo: z.string().default(''),\n cio: z.string().default(''),\n sio: z.array(z.string()).default([]),\n }).optional(),\n // Progressive-completion bookkeeping (feed v2, additive):\n // phase_id = development phase that introduces/uses this node;\n // introduce_aspect = how the phase teaches a concept node (first intro, advanced\n // revisit, or preview — a light ULO awareness pass that is NOT the introduction).\n phase_id: z.string().default(''),\n introduce_aspect: z.enum(['new', 'advanced', 'preview']).nullable().default(null),\n});\n\nexport const FeedEdgeSchema = z.object({\n from: z.string().min(1),\n to: z.string().min(1),\n kind: z.enum(['knowledge', 'task']),\n reason: z.string().default(''),\n});\n\nexport const FeedGroupingSchema = z.object({\n id: z.string().min(1),\n name: z.string().min(1),\n node_ids: z.array(z.string()).min(1),\n rationale: z.string().default(''),\n});\n\nexport const FeedPhaseSchema = z.object({\n id: z.string().min(1),\n order: z.number().int().positive(),\n name: z.string().min(1),\n product_completion: z.string().min(1),\n node_ids: z.array(z.string()).min(1),\n});\nexport type FeedPhase = z.infer<typeof FeedPhaseSchema>;\n\n/**\n * Machine-readable diagnostic. `warnings` in `source` is the DERIVED text view of\n * these (every diagnostic whose severity is not 'info'), so there is exactly one\n * source of truth and the host can gate on `code`/`severity` instead of substring\n * matching free text — the 2026-09-17 class of incident was a drift that was only\n * ever a warning string.\n */\nexport const FeedDiagnosticSchema = z.object({\n code: z.string().min(1),\n severity: z.enum(['info', 'warning', 'error']),\n message: z.string(),\n node_ids: z.array(z.string()).default([]),\n});\nexport type FeedDiagnostic = z.infer<typeof FeedDiagnosticSchema>;\n\n/** Which LLM providers actually served this graph, and whether any call fell over. */\nexport const FeedProvenanceSchema = z.object({\n providers: z.array(z.string()).default([]),\n models: z.array(z.string()).default([]),\n calls: z.number().int().nonnegative().default(0),\n failed_calls: z.number().int().nonnegative().default(0),\n degraded: z.boolean().default(false),\n});\nexport type FeedProvenance = z.infer<typeof FeedProvenanceSchema>;\n\nexport const CurriculumFeedSchema = z.object({\n schema_version: z.literal(2),\n source: z.object({\n graph_type: z.enum(['project_graph', 'knowledge_graph', 'hybrid_graph']),\n /** DERIVED from diagnostics — do not write to it directly. */\n warnings: z.array(z.string()).default([]),\n hallucination_count: z.number().int().nonnegative().default(0),\n diagnostics: z.array(FeedDiagnosticSchema).default([]),\n provenance: FeedProvenanceSchema.optional(),\n }),\n learning_nodes: z.array(FeedNodeSchema).min(1, 'curriculum feed must contain at least one learning node'),\n dependency_edges: z.array(FeedEdgeSchema).default([]),\n suggested_groupings: z.array(FeedGroupingSchema).default([]),\n // Progressive completion phases (cross-feature). When non-empty, these — not\n // suggested_groupings — define the teaching sequence and unit structure.\n phases: z.array(FeedPhaseSchema).default([]),\n});\n\nexport type CurriculumFeed = z.infer<typeof CurriculumFeedSchema>;\nexport type FeedNode = z.infer<typeof FeedNodeSchema>;\nexport type FeedEdge = z.infer<typeof FeedEdgeSchema>;\nexport type FeedGrouping = z.infer<typeof FeedGroupingSchema>;\n\n/** Strict pre-save resolution validation — fail-closed, no defaulted-empty graphs. */\nexport function validateCurriculumFeed(feed: unknown): CurriculumFeed {\n const parsed = CurriculumFeedSchema.parse(feed);\n const ids = new Set(parsed.learning_nodes.map((n) => n.id));\n if (ids.size !== parsed.learning_nodes.length) {\n throw new Error('curriculum feed contains duplicate node ids');\n }\n for (const e of parsed.dependency_edges) {\n if (!ids.has(e.from) || !ids.has(e.to)) {\n throw new Error('curriculum feed edge ' + e.from + ' -> ' + e.to + ' references unknown node');\n }\n if (e.from === e.to) throw new Error('curriculum feed self-loop edge on ' + e.from);\n }\n for (const g of parsed.suggested_groupings) {\n for (const id of g.node_ids) {\n if (!ids.has(id)) throw new Error('grouping ' + g.id + ' references unknown node ' + id);\n }\n }\n for (const ph of parsed.phases) {\n for (const id of ph.node_ids) {\n if (!ids.has(id)) throw new Error('phase ' + ph.id + ' references unknown node ' + id);\n }\n }\n const phaseIds = parsed.phases.map((p) => p.id);\n if (new Set(phaseIds).size !== phaseIds.length) throw new Error('curriculum feed contains duplicate phase ids');\n // `order` IS the teaching sequence once phases exist, so two phases claiming the\n // same slot makes that sequence undefined. Nothing downstream re-derives it.\n const phaseOrders = parsed.phases.map((p) => p.order);\n if (new Set(phaseOrders).size !== phaseOrders.length) {\n throw new Error('curriculum feed contains duplicate phase orders: ' + phaseOrders.join(', '));\n }\n // The array IS the teaching sequence, so orders must ascend in it. (Sorting a\n // copy first would make this check vacuous.)\n for (let i = 1; i < phaseOrders.length; i++) {\n if (phaseOrders[i] <= phaseOrders[i - 1]) {\n throw new Error('curriculum feed phase orders are not ascending: ' + phaseOrders.join(', '));\n }\n }\n // Teaching-order invariant (2026-09-17 incident): when phases define the\n // teaching sequence, a prerequisite must live in the same or an EARLIER\n // phase than its consumer. Without this check a self-contradictory feed\n // (backwards phase-crossing edge) sailed through emission and only exploded\n // much later inside the planner as \"Teaching-order violation in session ...\"\n // — far from the constructor that produced the contradiction.\n if (parsed.phases.length > 0) {\n const phaseIndexOf = new Map<string, number>();\n parsed.phases.forEach((p, i) => {\n for (const id of p.node_ids) phaseIndexOf.set(id, i);\n });\n for (const e of parsed.dependency_edges) {\n const fromIdx = phaseIndexOf.get(e.from);\n const toIdx = phaseIndexOf.get(e.to);\n if (fromIdx === undefined || toIdx === undefined) continue; // node outside the phase sequence\n if (fromIdx > toIdx) {\n throw new Error(\n 'curriculum feed teaching-order violation: prerequisite ' + e.from +\n ' (phase ' + (fromIdx + 1) + ') is scheduled AFTER its consumer ' + e.to +\n ' (phase ' + (toIdx + 1) + ')',\n );\n }\n }\n }\n return parsed;\n}\n"]}
@@ -310,5 +310,5 @@ function mergeCppResults(results) {
310
310
  }
311
311
 
312
312
  export { mergeCppResults, mergePythonResults, mergeSwiftResults, mergeTsResults, parseCppFile, parsePythonFile, parseSwiftFile, parseTsFile };
313
- //# sourceMappingURL=chunk-DRVOV5ZD.mjs.map
314
- //# sourceMappingURL=chunk-DRVOV5ZD.mjs.map
313
+ //# sourceMappingURL=chunk-DSEJLTUO.mjs.map
314
+ //# sourceMappingURL=chunk-DSEJLTUO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/parsers/swift.ts","../src/parsers/typescript.ts","../src/parsers/python.ts","../src/parsers/cpp.ts"],"names":["IMPORT_RE","TYPE_RE","FUNC_RE"],"mappings":"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","file":"chunk-DSEJLTUO.mjs","sourcesContent":["/**\n * Swift regex-based parser (heuristic) — extracts imports, types, functions, property wrappers,\n * error handling patterns, and framework usage from Swift source files.\n *\n * Ported from knowledge-tree/extract_project_keywords.py parse_swift_file()\n */\n\nexport interface SwiftParseResult {\n imports: string[];\n types: Array<{\n kind: string;\n name: string;\n conforms_to: string[];\n file: string;\n }>;\n functions: Array<{\n name: string;\n params: string;\n returns: string;\n file: string;\n }>;\n property_wrappers: string[];\n error_handling: string[];\n frameworks_used: string[];\n}\n\nconst IMPORT_RE = /^import\\s+(\\w+)/gm;\nconst TYPE_RE = /\\b(class|struct|enum|protocol)\\s+(\\w+)(?:\\s*:\\s*([^{]+))?/g;\nconst FUNC_RE = /\\bfunc\\s+(\\w+)\\s*\\(([^)]*)\\)(?:\\s*->\\s*([^{]+))?/g;\nconst WRAPPER_RE = /@(State|Binding|ObservedObject|StateObject|Published|Environment|EnvironmentObject|AppStorage|SceneStorage|FocusState|Observable)\\b/g;\nconst FRAMEWORK_USAGE_RE = /\\b(URLSession|URLRequest|JSONSerialization|JSONDecoder|JSONEncoder|Task|DispatchQueue|Timer|NotificationCenter|UserDefaults|FileManager|Bundle)\\b/g;\n\nexport function parseSwiftFile(filepath: string, content: string): SwiftParseResult {\n const result: SwiftParseResult = {\n imports: [],\n types: [],\n functions: [],\n property_wrappers: [],\n error_handling: [],\n frameworks_used: [],\n };\n\n // Extract imports\n let match: RegExpExecArray | null;\n while ((match = IMPORT_RE.exec(content)) !== null) {\n result.imports.push(match[1]);\n }\n\n // Extract framework usage (types used in body, not declarations)\n const fwSeen = new Set<string>();\n while ((match = FRAMEWORK_USAGE_RE.exec(content)) !== null) {\n if (!fwSeen.has(match[1])) {\n fwSeen.add(match[1]);\n result.frameworks_used.push(match[1]);\n }\n }\n\n // Extract type declarations\n while ((match = TYPE_RE.exec(content)) !== null) {\n const conforms = (match[3] || '')\n .split(',')\n .map(p => p.trim())\n .filter(Boolean);\n result.types.push({\n kind: match[1],\n name: match[2],\n conforms_to: conforms,\n file: filepath,\n });\n }\n\n // Extract function signatures\n while ((match = FUNC_RE.exec(content)) !== null) {\n const name = match[1];\n if (name.startsWith('_')) continue;\n result.functions.push({\n name,\n params: (match[2] || '').trim(),\n returns: (match[3] || '').trim(),\n file: filepath,\n });\n }\n\n // Extract property wrappers\n const wrapperSet = new Set<string>();\n while ((match = WRAPPER_RE.exec(content)) !== null) {\n wrapperSet.add(match[1]);\n }\n result.property_wrappers = Array.from(wrapperSet);\n\n // Extract error handling patterns\n if (content.includes('do {') || content.includes('do{')) {\n result.error_handling.push('do-catch');\n }\n if (content.includes('throws')) {\n result.error_handling.push('throws');\n }\n if (content.includes('Result<')) {\n result.error_handling.push('Result type');\n }\n if (content.includes('try?')) {\n result.error_handling.push('try?');\n }\n if (content.includes('try!')) {\n result.error_handling.push('try!');\n }\n\n return result;\n}\n\nexport function mergeSwiftResults(results: SwiftParseResult[]): SwiftParseResult {\n const merged: SwiftParseResult = {\n imports: [],\n types: [],\n functions: [],\n property_wrappers: [],\n error_handling: [],\n frameworks_used: [],\n };\n\n const importCounts = new Map<string, number>();\n const wrapperCounts = new Map<string, number>();\n const errorCounts = new Map<string, number>();\n const frameworkSet = new Set<string>();\n\n for (const r of results) {\n for (const imp of r.imports) {\n importCounts.set(imp, (importCounts.get(imp) || 0) + 1);\n }\n merged.types.push(...r.types);\n merged.functions.push(...r.functions);\n for (const w of r.property_wrappers) {\n wrapperCounts.set(w, (wrapperCounts.get(w) || 0) + 1);\n }\n for (const e of r.error_handling) {\n errorCounts.set(e, (errorCounts.get(e) || 0) + 1);\n }\n for (const fw of r.frameworks_used) {\n frameworkSet.add(fw);\n }\n }\n\n // Sort imports by count descending, top 20\n merged.imports = Array.from(importCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 20)\n .map(([mod, count]) => mod);\n\n // Sort wrappers by count, top 10\n merged.property_wrappers = Array.from(wrapperCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([w]) => w);\n\n // Sort errors by count, top 10\n merged.error_handling = Array.from(errorCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([p]) => p);\n\n merged.frameworks_used = Array.from(frameworkSet).sort();\n\n // Limit types and functions\n merged.types = merged.types.slice(0, 100);\n merged.functions = merged.functions.slice(0, 100);\n\n return merged;\n}\n","/**\n * TypeScript/JavaScript regex-based parser (heuristic) — extracts imports, types, functions,\n * and error handling patterns.\n *\n * Ported from knowledge-tree/extract_project_keywords.py parse_ts_file()\n */\n\nexport interface TsParseResult {\n imports: string[];\n types: Array<{\n kind: string;\n name: string;\n file: string;\n }>;\n functions: Array<{\n name: string;\n params: string;\n file: string;\n }>;\n error_handling: string[];\n}\n\nconst IMPORT_RE = /import\\s+.*?from\\s+['\"]([^'\"]+)['\"]/g;\nconst TYPE_RE = /\\b(interface|type|class|enum)\\s+(\\w+)/g;\nconst FUNC_RE = /\\bfunction\\s+(\\w+)\\s*\\(([^)]*)\\)/g;\nconst ARROW_RE = /\\bconst\\s+(\\w+)\\s*=\\s*\\([^)]*\\)\\s*(?::\\s*\\w+)?\\s*=>/g;\n\nexport function parseTsFile(filepath: string, content: string): TsParseResult {\n const result: TsParseResult = {\n imports: [],\n types: [],\n functions: [],\n error_handling: [],\n };\n\n let match: RegExpExecArray | null;\n\n // Extract imports\n while ((match = IMPORT_RE.exec(content)) !== null) {\n result.imports.push(match[1]);\n }\n\n // Extract type/interface/class/enum declarations\n while ((match = TYPE_RE.exec(content)) !== null) {\n result.types.push({\n kind: match[1],\n name: match[2],\n file: filepath,\n });\n }\n\n // Extract function declarations\n while ((match = FUNC_RE.exec(content)) !== null) {\n result.functions.push({\n name: match[1],\n params: (match[2] || '').trim(),\n file: filepath,\n });\n }\n\n // Extract arrow functions\n while ((match = ARROW_RE.exec(content)) !== null) {\n result.functions.push({\n name: match[1],\n params: '',\n file: filepath,\n });\n }\n\n // Extract error handling patterns\n if (content.includes('try {') || content.includes('try{')) {\n result.error_handling.push('try-catch');\n }\n if (content.includes('Promise')) {\n result.error_handling.push('Promise');\n }\n if (content.includes('.catch(')) {\n result.error_handling.push('.catch()');\n }\n\n return result;\n}\n\nexport function mergeTsResults(results: TsParseResult[]): TsParseResult {\n const merged: TsParseResult = {\n imports: [],\n types: [],\n functions: [],\n error_handling: [],\n };\n\n const importCounts = new Map<string, number>();\n const errorCounts = new Map<string, number>();\n\n for (const r of results) {\n for (const imp of r.imports) {\n importCounts.set(imp, (importCounts.get(imp) || 0) + 1);\n }\n merged.types.push(...r.types);\n merged.functions.push(...r.functions);\n for (const e of r.error_handling) {\n errorCounts.set(e, (errorCounts.get(e) || 0) + 1);\n }\n }\n\n merged.imports = Array.from(importCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 20)\n .map(([mod]) => mod);\n\n merged.error_handling = Array.from(errorCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([p]) => p);\n\n merged.types = merged.types.slice(0, 100);\n merged.functions = merged.functions.slice(0, 100);\n\n return merged;\n}\n","/**\n * Python regex-based parser (heuristic) — extracts imports, classes, functions, docstrings,\n * and error handling patterns.\n *\n * Ported from knowledge-tree/extract_project_keywords.py parse_python_file()\n */\n\nexport interface PythonParseResult {\n imports: string[];\n types: Array<{\n kind: string;\n name: string;\n bases: string[];\n file: string;\n }>;\n functions: Array<{\n name: string;\n params: string;\n file: string;\n }>;\n docstrings: string[];\n error_handling: string[];\n}\n\nconst IMPORT_RE = /^(?:import|from)\\s+(\\w+)/gm;\nconst CLASS_RE = /\\bclass\\s+(\\w+)(?:\\s*\\(([^)]+)\\))?/g;\nconst FUNC_RE = /\\bdef\\s+(\\w+)\\s*\\(([^)]*)\\)/g;\nconst DOCSTRING_RE = /\"\"\"([^\"]{20,300})\"\"\"/g;\n\nexport function parsePythonFile(filepath: string, content: string): PythonParseResult {\n const result: PythonParseResult = {\n imports: [],\n types: [],\n functions: [],\n docstrings: [],\n error_handling: [],\n };\n\n let match: RegExpExecArray | null;\n\n // Extract imports\n while ((match = IMPORT_RE.exec(content)) !== null) {\n result.imports.push(match[1]);\n }\n\n // Extract docstrings (module, class, function level) — high-signal domain intent\n while ((match = DOCSTRING_RE.exec(content)) !== null) {\n const firstSentence = match[1].trim().split('\\n')[0].slice(0, 120);\n if (firstSentence.length > 15) {\n result.docstrings.push(firstSentence);\n }\n }\n\n // Extract class declarations\n while ((match = CLASS_RE.exec(content)) !== null) {\n const bases = (match[2] || '')\n .split(',')\n .map(b => b.trim())\n .filter(Boolean);\n result.types.push({\n kind: 'class',\n name: match[1],\n bases,\n file: filepath,\n });\n }\n\n // Extract function declarations\n while ((match = FUNC_RE.exec(content)) !== null) {\n const name = match[1];\n // Skip dunder methods (__init__, __str__)\n if (name.startsWith('__') && name.endsWith('__')) continue;\n result.functions.push({\n name,\n params: (match[2] || '').trim(),\n file: filepath,\n });\n }\n\n // Extract error handling patterns\n if (content.includes('try:')) {\n result.error_handling.push('try-except');\n }\n if (content.includes('raise')) {\n result.error_handling.push('raise');\n }\n\n return result;\n}\n\nexport function mergePythonResults(results: PythonParseResult[]): PythonParseResult {\n const merged: PythonParseResult = {\n imports: [],\n types: [],\n functions: [],\n docstrings: [],\n error_handling: [],\n };\n\n const importCounts = new Map<string, number>();\n const errorCounts = new Map<string, number>();\n\n for (const r of results) {\n for (const imp of r.imports) {\n importCounts.set(imp, (importCounts.get(imp) || 0) + 1);\n }\n merged.types.push(...r.types);\n merged.functions.push(...r.functions);\n merged.docstrings.push(...r.docstrings);\n for (const e of r.error_handling) {\n errorCounts.set(e, (errorCounts.get(e) || 0) + 1);\n }\n }\n\n merged.imports = Array.from(importCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 20)\n .map(([mod]) => mod);\n\n merged.error_handling = Array.from(errorCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([p]) => p);\n\n merged.types = merged.types.slice(0, 100);\n merged.functions = merged.functions.slice(0, 100);\n merged.docstrings = merged.docstrings.slice(0, 20);\n\n return merged;\n}\n","/**\n * C++/Arduino regex-based parser (heuristic) — extracts #include, types, functions,\n * and error handling patterns.\n *\n * Ported from knowledge-tree/extract_project_keywords.py parse_cpp_file()\n */\n\nexport interface CppParseResult {\n imports: string[];\n types: Array<{\n kind: string;\n name: string;\n file: string;\n }>;\n functions: Array<{\n name: string;\n file: string;\n }>;\n error_handling: string[];\n}\n\nconst INCLUDE_RE = /#include\\s*[<\"]([^>\"]+)[>\"]/g;\nconst TYPE_RE = /\\b(?:class|struct|enum)\\s+(\\w+)/g;\nconst FUNC_RE = /\\b(?:void|int|float|double|char|bool|String|uint8_t|uint16_t|uint32_t)\\s+(\\w+)\\s*\\(/g;\n\nexport function parseCppFile(filepath: string, content: string): CppParseResult {\n const result: CppParseResult = {\n imports: [],\n types: [],\n functions: [],\n error_handling: [],\n };\n\n let match: RegExpExecArray | null;\n\n // Extract #include\n while ((match = INCLUDE_RE.exec(content)) !== null) {\n result.imports.push(match[1]);\n }\n\n // Extract type declarations\n while ((match = TYPE_RE.exec(content)) !== null) {\n result.types.push({\n kind: 'cpp',\n name: match[1],\n file: filepath,\n });\n }\n\n // Extract function declarations\n while ((match = FUNC_RE.exec(content)) !== null) {\n result.functions.push({\n name: match[1],\n file: filepath,\n });\n }\n\n // Extract error handling patterns\n if (/\\btry\\b|\\bthrow\\b|\\bcatch\\b/.test(content)) {\n result.error_handling.push('try/throw/catch');\n }\n\n return result;\n}\n\nexport function mergeCppResults(results: CppParseResult[]): CppParseResult {\n const merged: CppParseResult = {\n imports: [],\n types: [],\n functions: [],\n error_handling: [],\n };\n\n const importCounts = new Map<string, number>();\n\n for (const r of results) {\n for (const imp of r.imports) {\n importCounts.set(imp, (importCounts.get(imp) || 0) + 1);\n }\n merged.types.push(...r.types);\n merged.functions.push(...r.functions);\n merged.error_handling.push(...r.error_handling);\n }\n\n merged.imports = Array.from(importCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 20)\n .map(([mod]) => mod);\n\n // Deduplicate error handling\n merged.error_handling = [...new Set(merged.error_handling)];\n\n return merged;\n}\n"]}