@x12i/ai-gateway 7.9.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 (179) hide show
  1. package/README.md +4259 -0
  2. package/config.defaults.json +31 -0
  3. package/dist/activity-manager.d.ts +206 -0
  4. package/dist/activity-manager.js +1051 -0
  5. package/dist/config/activity-tracking-config.d.ts +11 -0
  6. package/dist/config/activity-tracking-config.js +15 -0
  7. package/dist/config.defaults.json +31 -0
  8. package/dist/content-normalizer/content-normalizer.d.ts +46 -0
  9. package/dist/content-normalizer/content-normalizer.js +393 -0
  10. package/dist/content-normalizer/index.d.ts +7 -0
  11. package/dist/content-normalizer/index.js +6 -0
  12. package/dist/content-normalizer/types.d.ts +33 -0
  13. package/dist/content-normalizer/types.js +4 -0
  14. package/dist/defaults/instructions-blocks.json +61 -0
  15. package/dist/defaults/model-config.json +16 -0
  16. package/dist/defaults/template-rendering.json +6 -0
  17. package/dist/flex-md-loader.d.ts +109 -0
  18. package/dist/flex-md-loader.js +940 -0
  19. package/dist/gateway-config.d.ts +49 -0
  20. package/dist/gateway-config.js +292 -0
  21. package/dist/gateway-conversion.d.ts +29 -0
  22. package/dist/gateway-conversion.js +174 -0
  23. package/dist/gateway-instructions.d.ts +30 -0
  24. package/dist/gateway-instructions.js +62 -0
  25. package/dist/gateway-memory.d.ts +51 -0
  26. package/dist/gateway-memory.js +207 -0
  27. package/dist/gateway-messages.d.ts +23 -0
  28. package/dist/gateway-messages.js +83 -0
  29. package/dist/gateway-meta.d.ts +25 -0
  30. package/dist/gateway-meta.js +87 -0
  31. package/dist/gateway-provider-auto-register.d.ts +17 -0
  32. package/dist/gateway-provider-auto-register.js +159 -0
  33. package/dist/gateway-provider.d.ts +54 -0
  34. package/dist/gateway-provider.js +202 -0
  35. package/dist/gateway-rate-limiter-constants.d.ts +16 -0
  36. package/dist/gateway-rate-limiter-constants.js +16 -0
  37. package/dist/gateway-rate-limiter.d.ts +56 -0
  38. package/dist/gateway-rate-limiter.js +107 -0
  39. package/dist/gateway-retry.d.ts +49 -0
  40. package/dist/gateway-retry.js +204 -0
  41. package/dist/gateway-utils.d.ts +21 -0
  42. package/dist/gateway-utils.js +181 -0
  43. package/dist/gateway-validation.d.ts +13 -0
  44. package/dist/gateway-validation.js +50 -0
  45. package/dist/gateway.d.ts +39 -0
  46. package/dist/gateway.js +430 -0
  47. package/dist/index.d.ts +36 -0
  48. package/dist/index.js +55 -0
  49. package/dist/instruction-errors.d.ts +16 -0
  50. package/dist/instruction-errors.js +29 -0
  51. package/dist/instruction-optimizer.d.ts +113 -0
  52. package/dist/instruction-optimizer.js +293 -0
  53. package/dist/instructions-parser.d.ts +31 -0
  54. package/dist/instructions-parser.js +56 -0
  55. package/dist/logger-factory.d.ts +17 -0
  56. package/dist/logger-factory.js +42 -0
  57. package/dist/message-builder.d.ts +41 -0
  58. package/dist/message-builder.js +522 -0
  59. package/dist/object-types-library-integration.d.ts +22 -0
  60. package/dist/object-types-library-integration.js +27 -0
  61. package/dist/object-types-library.d.ts +351 -0
  62. package/dist/object-types-library.js +210 -0
  63. package/dist/output-auditor.d.ts +44 -0
  64. package/dist/output-auditor.js +49 -0
  65. package/dist/request-report-generator.d.ts +60 -0
  66. package/dist/request-report-generator.js +169 -0
  67. package/dist/response-analyzer/format-type-detector.d.ts +35 -0
  68. package/dist/response-analyzer/format-type-detector.js +115 -0
  69. package/dist/response-analyzer/index.d.ts +9 -0
  70. package/dist/response-analyzer/index.js +8 -0
  71. package/dist/response-analyzer/object-type-detector.d.ts +42 -0
  72. package/dist/response-analyzer/object-type-detector.js +95 -0
  73. package/dist/response-analyzer/response-analyzer.d.ts +38 -0
  74. package/dist/response-analyzer/response-analyzer.js +97 -0
  75. package/dist/response-analyzer/types.d.ts +97 -0
  76. package/dist/response-analyzer/types.js +4 -0
  77. package/dist/response-fallback-fixer.d.ts +11 -0
  78. package/dist/response-fallback-fixer.js +123 -0
  79. package/dist/runtime-objects.d.ts +52 -0
  80. package/dist/runtime-objects.js +46 -0
  81. package/dist/template-parser.d.ts +58 -0
  82. package/dist/template-parser.js +99 -0
  83. package/dist/template-render-merge.d.ts +9 -0
  84. package/dist/template-render-merge.js +40 -0
  85. package/dist/troubleshooting-helper.d.ts +123 -0
  86. package/dist/troubleshooting-helper.js +596 -0
  87. package/dist/types.d.ts +1173 -0
  88. package/dist/types.js +6 -0
  89. package/dist/usage-tracker.d.ts +78 -0
  90. package/dist/usage-tracker.js +79 -0
  91. package/dist-cjs/activity-manager.cjs +1056 -0
  92. package/dist-cjs/activity-manager.d.ts +206 -0
  93. package/dist-cjs/config/activity-tracking-config.cjs +18 -0
  94. package/dist-cjs/config/activity-tracking-config.d.ts +11 -0
  95. package/dist-cjs/config.defaults.json +31 -0
  96. package/dist-cjs/content-normalizer/content-normalizer.cjs +398 -0
  97. package/dist-cjs/content-normalizer/content-normalizer.d.ts +46 -0
  98. package/dist-cjs/content-normalizer/index.cjs +12 -0
  99. package/dist-cjs/content-normalizer/index.d.ts +7 -0
  100. package/dist-cjs/content-normalizer/types.cjs +5 -0
  101. package/dist-cjs/content-normalizer/types.d.ts +33 -0
  102. package/dist-cjs/defaults/instructions-blocks.json +61 -0
  103. package/dist-cjs/defaults/model-config.json +16 -0
  104. package/dist-cjs/defaults/template-rendering.json +6 -0
  105. package/dist-cjs/flex-md-loader.cjs +986 -0
  106. package/dist-cjs/flex-md-loader.d.ts +109 -0
  107. package/dist-cjs/gateway-config.cjs +331 -0
  108. package/dist-cjs/gateway-config.d.ts +49 -0
  109. package/dist-cjs/gateway-conversion.cjs +212 -0
  110. package/dist-cjs/gateway-conversion.d.ts +29 -0
  111. package/dist-cjs/gateway-instructions.cjs +67 -0
  112. package/dist-cjs/gateway-instructions.d.ts +30 -0
  113. package/dist-cjs/gateway-memory.cjs +211 -0
  114. package/dist-cjs/gateway-memory.d.ts +51 -0
  115. package/dist-cjs/gateway-messages.cjs +86 -0
  116. package/dist-cjs/gateway-messages.d.ts +23 -0
  117. package/dist-cjs/gateway-meta.cjs +90 -0
  118. package/dist-cjs/gateway-meta.d.ts +25 -0
  119. package/dist-cjs/gateway-provider-auto-register.cjs +195 -0
  120. package/dist-cjs/gateway-provider-auto-register.d.ts +17 -0
  121. package/dist-cjs/gateway-provider.cjs +214 -0
  122. package/dist-cjs/gateway-provider.d.ts +54 -0
  123. package/dist-cjs/gateway-rate-limiter-constants.cjs +19 -0
  124. package/dist-cjs/gateway-rate-limiter-constants.d.ts +16 -0
  125. package/dist-cjs/gateway-rate-limiter.cjs +111 -0
  126. package/dist-cjs/gateway-rate-limiter.d.ts +56 -0
  127. package/dist-cjs/gateway-retry.cjs +212 -0
  128. package/dist-cjs/gateway-retry.d.ts +49 -0
  129. package/dist-cjs/gateway-utils.cjs +219 -0
  130. package/dist-cjs/gateway-utils.d.ts +21 -0
  131. package/dist-cjs/gateway-validation.cjs +54 -0
  132. package/dist-cjs/gateway-validation.d.ts +13 -0
  133. package/dist-cjs/gateway.cjs +434 -0
  134. package/dist-cjs/gateway.d.ts +39 -0
  135. package/dist-cjs/index.cjs +108 -0
  136. package/dist-cjs/index.d.ts +36 -0
  137. package/dist-cjs/instruction-errors.cjs +34 -0
  138. package/dist-cjs/instruction-errors.d.ts +16 -0
  139. package/dist-cjs/instruction-optimizer.cjs +299 -0
  140. package/dist-cjs/instruction-optimizer.d.ts +113 -0
  141. package/dist-cjs/instructions-parser.cjs +61 -0
  142. package/dist-cjs/instructions-parser.d.ts +31 -0
  143. package/dist-cjs/logger-factory.cjs +45 -0
  144. package/dist-cjs/logger-factory.d.ts +17 -0
  145. package/dist-cjs/message-builder.cjs +558 -0
  146. package/dist-cjs/message-builder.d.ts +41 -0
  147. package/dist-cjs/object-types-library-integration.cjs +32 -0
  148. package/dist-cjs/object-types-library-integration.d.ts +22 -0
  149. package/dist-cjs/object-types-library.cjs +215 -0
  150. package/dist-cjs/object-types-library.d.ts +351 -0
  151. package/dist-cjs/output-auditor.cjs +52 -0
  152. package/dist-cjs/output-auditor.d.ts +44 -0
  153. package/dist-cjs/request-report-generator.cjs +172 -0
  154. package/dist-cjs/request-report-generator.d.ts +60 -0
  155. package/dist-cjs/response-analyzer/format-type-detector.cjs +119 -0
  156. package/dist-cjs/response-analyzer/format-type-detector.d.ts +35 -0
  157. package/dist-cjs/response-analyzer/index.cjs +14 -0
  158. package/dist-cjs/response-analyzer/index.d.ts +9 -0
  159. package/dist-cjs/response-analyzer/object-type-detector.cjs +99 -0
  160. package/dist-cjs/response-analyzer/object-type-detector.d.ts +42 -0
  161. package/dist-cjs/response-analyzer/response-analyzer.cjs +101 -0
  162. package/dist-cjs/response-analyzer/response-analyzer.d.ts +38 -0
  163. package/dist-cjs/response-analyzer/types.cjs +5 -0
  164. package/dist-cjs/response-analyzer/types.d.ts +97 -0
  165. package/dist-cjs/response-fallback-fixer.cjs +126 -0
  166. package/dist-cjs/response-fallback-fixer.d.ts +11 -0
  167. package/dist-cjs/runtime-objects.cjs +52 -0
  168. package/dist-cjs/runtime-objects.d.ts +52 -0
  169. package/dist-cjs/template-parser.cjs +136 -0
  170. package/dist-cjs/template-parser.d.ts +58 -0
  171. package/dist-cjs/template-render-merge.cjs +43 -0
  172. package/dist-cjs/template-render-merge.d.ts +9 -0
  173. package/dist-cjs/troubleshooting-helper.cjs +611 -0
  174. package/dist-cjs/troubleshooting-helper.d.ts +123 -0
  175. package/dist-cjs/types.cjs +7 -0
  176. package/dist-cjs/types.d.ts +1173 -0
  177. package/dist-cjs/usage-tracker.cjs +83 -0
  178. package/dist-cjs/usage-tracker.d.ts +78 -0
  179. package/package.json +91 -0
@@ -0,0 +1,986 @@
1
+ "use strict";
2
+ /**
3
+ * @x12i/flex-md Loader
4
+ *
5
+ * Handles ES Module loading and provides unified API for flex-md operations.
6
+ * @x12i/flex-md is an ES Module, so we use dynamic import() instead of require().
7
+ *
8
+ * Features:
9
+ * - extractJsonFromFlexMd: Extract and parse JSON from flex-md content
10
+ * - buildDynamicInstructions: Build output format instructions from schema objects (3.1+)
11
+ * - Embedded nx-md-parser: flex-md 4.x includes nx-md-parser functionality (no separate package needed)
12
+ * - Utilizes latest flex-md 4.x APIs: extractFromMarkdown, transformWithOfs, processResponseMarkdown
13
+ * - Memory/caching: flex-md 4.x functions may utilize internal memory/caching for performance
14
+ * - Native output format support: Extracts and uses flex-md native output formats from instructions
15
+ * based on compliance level when no explicit format is provided
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.loadFlexMd = loadFlexMd;
52
+ exports.extractJsonFromFlexMd = extractJsonFromFlexMd;
53
+ exports.isFlexMdAvailable = isFlexMdAvailable;
54
+ exports.getModelMaxTokensFromFlexMd = getModelMaxTokensFromFlexMd;
55
+ exports.getFlexMdModule = getFlexMdModule;
56
+ exports.buildDynamicInstructions = buildDynamicInstructions;
57
+ exports.enrichInstructionsWithFlexMd = enrichInstructionsWithFlexMd;
58
+ exports.getStrictnessDefaults = getStrictnessDefaults;
59
+ exports.enforceFlexMdFormat = enforceFlexMdFormat;
60
+ exports.hasFlexMdContract = hasFlexMdContract;
61
+ exports.detectComplianceLevel = detectComplianceLevel;
62
+ const path = __importStar(require("path"));
63
+ const url_1 = require("url");
64
+ let flexMdModule = null;
65
+ let flexMdLoadError = null;
66
+ let flexMdLoading = null;
67
+ let hasLoggedFlexMdLoadWarning = false;
68
+ // Preserve native dynamic import in both ESM and transpiled CJS output.
69
+ // TypeScript can rewrite direct `import()` to `require()` in CJS builds.
70
+ const importModule = new Function('specifier', 'return import(specifier)');
71
+ async function importFlexMdModule() {
72
+ try {
73
+ return await importModule('@x12i/flex-md');
74
+ }
75
+ catch (esmImportError) {
76
+ // If package import condition is broken, import explicit CJS entry via file URL.
77
+ const { createRequire } = await importModule('module');
78
+ const requireFn = typeof require === 'function' ? require : createRequire(path.join(process.cwd(), 'noop.cjs'));
79
+ const cjsEntryPath = requireFn.resolve('@x12i/flex-md');
80
+ const cjsEntryUrl = (0, url_1.pathToFileURL)(cjsEntryPath).href;
81
+ try {
82
+ return await importModule(cjsEntryUrl);
83
+ }
84
+ catch (cjsImportError) {
85
+ const details = [
86
+ `import condition failed: ${esmImportError instanceof Error ? esmImportError.message : String(esmImportError)}`,
87
+ `cjs entry fallback failed: ${cjsImportError instanceof Error ? cjsImportError.message : String(cjsImportError)}`
88
+ ].join('\n');
89
+ throw new Error(details);
90
+ }
91
+ }
92
+ }
93
+ // nx-md-parser functionality is now embedded in flex-md, no separate loading needed
94
+ /**
95
+ * Load flex-md module (ES Module) asynchronously
96
+ * Caches the result to avoid multiple imports
97
+ */
98
+ async function loadFlexMd() {
99
+ // Return cached module if available
100
+ if (flexMdModule) {
101
+ return flexMdModule;
102
+ }
103
+ // Return cached error if loading previously failed
104
+ if (flexMdLoadError) {
105
+ throw flexMdLoadError;
106
+ }
107
+ // If already loading, wait for that promise
108
+ if (flexMdLoading) {
109
+ return await flexMdLoading;
110
+ }
111
+ // Start loading
112
+ flexMdLoading = (async () => {
113
+ try {
114
+ // Always load through preserved dynamic import to avoid CJS require() rewrites.
115
+ const imported = await importFlexMdModule();
116
+ flexMdModule = imported.default || imported;
117
+ return flexMdModule;
118
+ }
119
+ catch (esImportError) {
120
+ const errorMsg = [
121
+ '@x12i/flex-md failed to load via dynamic import',
122
+ `reason: ${esImportError instanceof Error ? esImportError.message : String(esImportError)}`
123
+ ].join('\n');
124
+ flexMdLoadError = new Error(errorMsg);
125
+ throw flexMdLoadError;
126
+ }
127
+ finally {
128
+ flexMdLoading = null;
129
+ }
130
+ })();
131
+ return await flexMdLoading;
132
+ }
133
+ /**
134
+ * Transform markdown to JSON using flex-md's embedded nx-md-parser functionality
135
+ * flex-md 4.x includes nx-md-parser functionality, so we use flex-md's APIs directly
136
+ */
137
+ async function transformWithFlexMdNxParser(content, schema, flexMd) {
138
+ try {
139
+ // flex-md 4.x includes nx-md-parser functionality
140
+ // Check if flex-md has JSONTransformer and Schema (from embedded nx-md-parser)
141
+ if (flexMd.JSONTransformer && flexMd.Schema) {
142
+ const { JSONTransformer, Schema } = flexMd;
143
+ // Convert schema to nx-md-parser Schema format
144
+ let nxSchema;
145
+ // If schema is already a Schema object (from Schema.object()), use it directly
146
+ if (schema && typeof schema === 'object' && 'toJSON' in schema) {
147
+ nxSchema = schema;
148
+ }
149
+ else if (schema && typeof schema === 'object' && schema.type === 'object' && schema.properties) {
150
+ // Convert JSON Schema to Schema builder format
151
+ nxSchema = convertJsonSchemaToSchemaBuilder(schema, Schema);
152
+ }
153
+ else if (schema && typeof schema === 'object') {
154
+ // Plain object - try to convert to Schema builder format
155
+ nxSchema = convertPlainObjectToSchemaBuilder(schema, Schema);
156
+ }
157
+ else {
158
+ return null; // Invalid schema format
159
+ }
160
+ const transformer = new JSONTransformer(nxSchema);
161
+ const result = transformer.transformMarkdown(content);
162
+ if (result && result.status !== 'failed' && result.result) {
163
+ return {
164
+ json: result.result,
165
+ method: `flex-md-nx-parser-${result.status || 'validated'}`
166
+ };
167
+ }
168
+ }
169
+ return null;
170
+ }
171
+ catch (error) {
172
+ // Transformation failed, return null to fall back to other methods
173
+ return null;
174
+ }
175
+ }
176
+ /**
177
+ * Convert JSON Schema to nx-md-parser Schema format
178
+ * Supports both JSON Schema format and nx-md-parser Schema builder format
179
+ */
180
+ function convertJsonSchemaToNxMdSchema(schema) {
181
+ // If it's already a nx-md-parser Schema object (has methods like toJSON), return as-is
182
+ if (schema && typeof schema === 'object' && 'toJSON' in schema) {
183
+ return schema;
184
+ }
185
+ // If it's a JSON Schema-like object, convert it
186
+ if (schema && typeof schema === 'object' && schema.type === 'object' && schema.properties) {
187
+ // We'll need to convert JSON Schema to nx-md-parser Schema format
188
+ // For now, return the schema as-is and let nx-md-parser handle it
189
+ // nx-md-parser supports JSON Schema format via createTransformerFromSchemaFile
190
+ return schema;
191
+ }
192
+ // If it's a plain object (simple format), try to infer structure
193
+ if (schema && typeof schema === 'object' && !Array.isArray(schema)) {
194
+ return schema;
195
+ }
196
+ return schema;
197
+ }
198
+ /**
199
+ * Convert JSON Schema properties to nx-md-parser Schema builder format
200
+ */
201
+ function convertJsonSchemaToSchemaBuilder(jsonSchema, Schema) {
202
+ if (!jsonSchema.properties || typeof jsonSchema.properties !== 'object') {
203
+ throw new Error('Invalid JSON Schema: missing properties');
204
+ }
205
+ const schemaProps = {};
206
+ for (const [key, prop] of Object.entries(jsonSchema.properties)) {
207
+ const propObj = prop;
208
+ const propType = propObj.type;
209
+ if (propType === 'string') {
210
+ schemaProps[key] = Schema.string();
211
+ }
212
+ else if (propType === 'number' || propType === 'integer') {
213
+ schemaProps[key] = Schema.number();
214
+ }
215
+ else if (propType === 'boolean') {
216
+ schemaProps[key] = Schema.boolean();
217
+ }
218
+ else if (propType === 'array') {
219
+ const items = propObj.items;
220
+ if (items && items.type === 'string') {
221
+ schemaProps[key] = Schema.array(Schema.string());
222
+ }
223
+ else if (items && items.type === 'number') {
224
+ schemaProps[key] = Schema.array(Schema.number());
225
+ }
226
+ else if (items && items.type === 'boolean') {
227
+ schemaProps[key] = Schema.array(Schema.boolean());
228
+ }
229
+ else if (items && items.type === 'object') {
230
+ schemaProps[key] = Schema.array(convertJsonSchemaToSchemaBuilder(items, Schema));
231
+ }
232
+ else {
233
+ schemaProps[key] = Schema.array(Schema.string()); // Default to string array
234
+ }
235
+ }
236
+ else if (propType === 'object') {
237
+ schemaProps[key] = convertJsonSchemaToSchemaBuilder(propObj, Schema);
238
+ }
239
+ else {
240
+ // Default to string for unknown types
241
+ schemaProps[key] = Schema.string();
242
+ }
243
+ }
244
+ return Schema.object(schemaProps);
245
+ }
246
+ /**
247
+ * Convert plain object to nx-md-parser Schema builder format
248
+ * Infers types from example values
249
+ */
250
+ function convertPlainObjectToSchemaBuilder(obj, Schema) {
251
+ const schemaProps = {};
252
+ for (const [key, value] of Object.entries(obj)) {
253
+ if (typeof value === 'string') {
254
+ schemaProps[key] = Schema.string();
255
+ }
256
+ else if (typeof value === 'number') {
257
+ schemaProps[key] = Schema.number();
258
+ }
259
+ else if (typeof value === 'boolean') {
260
+ schemaProps[key] = Schema.boolean();
261
+ }
262
+ else if (Array.isArray(value)) {
263
+ if (value.length > 0) {
264
+ const firstItem = value[0];
265
+ if (typeof firstItem === 'string') {
266
+ schemaProps[key] = Schema.array(Schema.string());
267
+ }
268
+ else if (typeof firstItem === 'number') {
269
+ schemaProps[key] = Schema.array(Schema.number());
270
+ }
271
+ else if (typeof firstItem === 'boolean') {
272
+ schemaProps[key] = Schema.array(Schema.boolean());
273
+ }
274
+ else if (typeof firstItem === 'object') {
275
+ schemaProps[key] = Schema.array(convertPlainObjectToSchemaBuilder(firstItem, Schema));
276
+ }
277
+ else {
278
+ schemaProps[key] = Schema.array(Schema.string());
279
+ }
280
+ }
281
+ else {
282
+ schemaProps[key] = Schema.array(Schema.string()); // Empty array defaults to string array
283
+ }
284
+ }
285
+ else if (value && typeof value === 'object') {
286
+ schemaProps[key] = convertPlainObjectToSchemaBuilder(value, Schema);
287
+ }
288
+ else {
289
+ schemaProps[key] = Schema.string(); // Default to string
290
+ }
291
+ }
292
+ return Schema.object(schemaProps);
293
+ }
294
+ /**
295
+ * Extract flex-md native output format from instructions text
296
+ * Uses flex-md's parseOutputFormatSpec to parse format specifications
297
+ */
298
+ async function extractFlexMdOutputFormatFromInstructions(instructions, complianceLevel) {
299
+ try {
300
+ const flexMd = await loadFlexMd();
301
+ // Use flex-md's parseOutputFormatSpec if available
302
+ if (flexMd.parseOutputFormatSpec && typeof flexMd.parseOutputFormatSpec === 'function') {
303
+ try {
304
+ const parsedSpec = flexMd.parseOutputFormatSpec(instructions);
305
+ if (parsedSpec && typeof parsedSpec === 'object' && parsedSpec !== null) {
306
+ // Add compliance level if provided
307
+ if (complianceLevel) {
308
+ parsedSpec.strictness = complianceLevel;
309
+ }
310
+ return parsedSpec;
311
+ }
312
+ }
313
+ catch (e) {
314
+ // parseOutputFormatSpec failed, try manual extraction
315
+ }
316
+ }
317
+ // Fallback: Try to extract OUTPUT FORMAT section manually
318
+ const formatPatterns = [
319
+ /OUTPUT\s+FORMAT[:\s]*(?:\([^)]*\))?[:\s]*\n([\s\S]*?)$/im,
320
+ /OUTPUT\s+FORMAT[:\s]*(?:\([^)]*\))?[:\s]*\n([\s\S]*?)(?=\n\n(?:##|#|OUTPUT|$|\n[A-Z]{2,})|$)/i,
321
+ /^##+\s+Output\s+Format[:\s]*\n([\s\S]*?)(?=^##+\s+|$)/im,
322
+ ];
323
+ for (const pattern of formatPatterns) {
324
+ const match = instructions.match(pattern);
325
+ if (match && match[1]) {
326
+ const formatSpec = match[1].trim();
327
+ if (formatSpec.length >= 30) {
328
+ // Try to parse with flex-md if available
329
+ if (flexMd.parseOutputFormatSpec) {
330
+ try {
331
+ const parsedSpec = flexMd.parseOutputFormatSpec(formatSpec);
332
+ if (parsedSpec && typeof parsedSpec === 'object' && parsedSpec !== null) {
333
+ if (complianceLevel) {
334
+ parsedSpec.strictness = complianceLevel;
335
+ }
336
+ return parsedSpec;
337
+ }
338
+ }
339
+ catch (e) {
340
+ // Continue to next pattern
341
+ }
342
+ }
343
+ // Return raw format spec as fallback
344
+ return {
345
+ format: formatSpec,
346
+ strictness: complianceLevel || 'L0'
347
+ };
348
+ }
349
+ }
350
+ }
351
+ return null;
352
+ }
353
+ catch (error) {
354
+ return null;
355
+ }
356
+ }
357
+ /**
358
+ * Extract and parse JSON from flex-md content (unstructured parsing only)
359
+ * Uses flex-md 4.2.0 APIs with latest features for unstructured markdown conversion
360
+ *
361
+ * ARCHITECTURE: This function only handles unstructured parsing at ai-gateway layer
362
+ * Schema-driven parsing happens at skills layer with nx-md-parser
363
+ *
364
+ * Supports flex-md native output formats from instructions:
365
+ * - Extracts flex-md output format from instructions when provided
366
+ * - Uses the extracted format based on compliance level for parsing
367
+ * - Falls back to simple markdown parsing when flex-md is unavailable
368
+ *
369
+ * @param content - The markdown content to parse (unstructured)
370
+ * @param options - Optional flex-md options:
371
+ * - instructions: Instruction text to guide unstructured parsing
372
+ * - complianceLevel: Compliance level ('L0' | 'L1' | 'L2' | 'L3') for format extraction
373
+ * - spec: Pre-parsed flex-md output format spec
374
+ * - Other flex-md options
375
+ * @returns Parsed JSON object and method used, or null if parsing fails
376
+ */
377
+ async function extractJsonFromFlexMd(content) {
378
+ // ARCHITECTURE: ai-gateway layer only handles unstructured parsing
379
+ // Schema-driven parsing is handled at skills layer with nx-md-parser
380
+ // No schema parameter - this layer doesn't know about schemas
381
+ try {
382
+ const flexMd = await loadFlexMd();
383
+ // Primary approach: Use markdownToJson() for simple markdown → JSON conversion
384
+ if (flexMd.markdownToJson && typeof flexMd.markdownToJson === 'function') {
385
+ try {
386
+ const json = flexMd.markdownToJson(content);
387
+ if (json && typeof json === 'object' && Object.keys(json).length > 0) {
388
+ return {
389
+ json,
390
+ method: 'markdownToJson'
391
+ };
392
+ }
393
+ }
394
+ catch (error) {
395
+ // markdownToJson failed, continue to other methods
396
+ console.warn('markdownToJson failed:', error instanceof Error ? error.message : String(error));
397
+ }
398
+ }
399
+ // Secondary approach: Use extractFromMarkdown for low-level section extraction
400
+ if (flexMd.extractFromMarkdown && typeof flexMd.extractFromMarkdown === 'function') {
401
+ try {
402
+ const result = flexMd.extractFromMarkdown(content);
403
+ if (result && typeof result === 'object' && result !== null) {
404
+ const json = result.json || result.data || result.result || result;
405
+ if (json && typeof json === 'object' && json !== null) {
406
+ console.debug('extractJsonFromFlexMd: using extractFromMarkdown');
407
+ console.debug('extractJsonFromFlexMd: json:', json);
408
+ return { json, method: 'extractFromMarkdown' };
409
+ }
410
+ }
411
+ }
412
+ catch (error) {
413
+ // extractFromMarkdown failed, continue
414
+ console.warn('extractFromMarkdown failed:', error instanceof Error ? error.message : String(error));
415
+ }
416
+ }
417
+ }
418
+ catch (error) {
419
+ // flex-md loading failed, log once and continue to fallback.
420
+ if (!hasLoggedFlexMdLoadWarning) {
421
+ hasLoggedFlexMdLoadWarning = true;
422
+ console.warn('extractJsonFromFlexMd: flex-md library failed to load, using markdown parser fallback:', error instanceof Error ? error.message : String(error));
423
+ }
424
+ }
425
+ console.debug('extractJsonFromFlexMd: using markdown parser fallback');
426
+ console.debug('extractJsonFromFlexMd: content:', content);
427
+ // Final fallback: Parse markdown sections
428
+ return fallbackMarkdownParser(content);
429
+ }
430
+ /**
431
+ * Helper function to detect and parse markdown lists into arrays
432
+ */
433
+ function parseMarkdownList(content) {
434
+ if (content.length === 0)
435
+ return '';
436
+ // Check if content contains actual list markers
437
+ const hasBulletPoints = content.some(line => line.trim().startsWith('- '));
438
+ const hasNumberedItems = content.some(line => /^\d+\.\s/.test(line.trim()));
439
+ // If no list markers found, return as joined string
440
+ if (!hasBulletPoints && !hasNumberedItems) {
441
+ return content.join('\n').trim();
442
+ }
443
+ // Parse list items
444
+ const listItems = [];
445
+ let currentItem = '';
446
+ for (const line of content) {
447
+ const trimmed = line.trim();
448
+ // Check for bullet points (- item)
449
+ if (trimmed.startsWith('- ')) {
450
+ // Save previous item if exists
451
+ if (currentItem) {
452
+ listItems.push(currentItem.trim());
453
+ }
454
+ // Start new item
455
+ currentItem = trimmed.substring(2).trim();
456
+ }
457
+ // Check for numbered items (1. item, 2. item, etc.)
458
+ else if (/^\d+\.\s/.test(trimmed)) {
459
+ // Save previous item if exists
460
+ if (currentItem) {
461
+ listItems.push(currentItem.trim());
462
+ }
463
+ // Start new item
464
+ const match = trimmed.match(/^\d+\.\s(.+)$/);
465
+ currentItem = match ? match[1].trim() : trimmed;
466
+ }
467
+ // Continuation of current item
468
+ else if (currentItem && trimmed) {
469
+ currentItem += ' ' + trimmed;
470
+ }
471
+ // If we haven't started an item yet and this is content
472
+ else if (!currentItem && trimmed) {
473
+ currentItem = trimmed;
474
+ }
475
+ }
476
+ // Add final item
477
+ if (currentItem) {
478
+ listItems.push(currentItem.trim());
479
+ }
480
+ // Return array if we have multiple items or explicit list markers, otherwise string
481
+ return listItems.length > 1 || hasBulletPoints || hasNumberedItems ? listItems : content.join('\n').trim();
482
+ }
483
+ /**
484
+ * Simple markdown parser for unstructured content (fallback when flex-md unavailable)
485
+ */
486
+ function fallbackMarkdownParser(content) {
487
+ const result = {};
488
+ const detectedHeaders = [];
489
+ // Simple section extraction based on markdown headers
490
+ const lines = content.split('\n');
491
+ let currentSection = '';
492
+ let currentContent = [];
493
+ for (const line of lines) {
494
+ // Check for markdown headers (# ## ###) - improved regex
495
+ const headerMatch = line.match(/^(#{1,6})\s*(.+?)\s*$/);
496
+ if (headerMatch) {
497
+ // Save previous section if exists
498
+ if (currentSection && currentContent.length > 0) {
499
+ // Convert header to camelCase key
500
+ const key = currentSection
501
+ .toLowerCase()
502
+ .replace(/[^a-zA-Z0-9\s]/g, '') // Remove special chars
503
+ .replace(/\s+/g, ' ') // Normalize spaces
504
+ .trim()
505
+ .replace(/\s+(\w)/g, (_, letter) => letter.toUpperCase()); // camelCase
506
+ result[key] = parseMarkdownList(currentContent);
507
+ detectedHeaders.push(key);
508
+ }
509
+ // Start new section
510
+ currentSection = headerMatch[2].trim();
511
+ currentContent = [];
512
+ }
513
+ else if (currentSection) {
514
+ // Add line to current section
515
+ currentContent.push(line);
516
+ }
517
+ }
518
+ // Save last section
519
+ if (currentSection && currentContent.length > 0) {
520
+ const key = currentSection
521
+ .toLowerCase()
522
+ .replace(/[^a-zA-Z0-9\s]/g, '')
523
+ .replace(/\s+/g, ' ')
524
+ .trim()
525
+ .replace(/\s+(\w)/g, (_, letter) => letter.toUpperCase());
526
+ result[key] = parseMarkdownList(currentContent);
527
+ detectedHeaders.push(key);
528
+ }
529
+ // Debug logging for incomplete parsing
530
+ console.log('Fallback parser detected headers:', detectedHeaders);
531
+ const expectedHeaders = ['shortAnswer', 'fullAnswer', 'assumptions', 'unknowns', 'evidence'];
532
+ const missingHeaders = expectedHeaders.filter(h => !detectedHeaders.includes(h));
533
+ if (missingHeaders.length > 0) {
534
+ console.log('Fallback parser missing expected headers:', missingHeaders);
535
+ }
536
+ // If we extracted some structure, return it
537
+ if (Object.keys(result).length > 0) {
538
+ return {
539
+ json: result,
540
+ method: 'fallback-markdown-parser'
541
+ };
542
+ }
543
+ // No structure found, return raw text
544
+ return {
545
+ json: { rawText: content },
546
+ method: 'fallback-raw-text'
547
+ };
548
+ }
549
+ /**
550
+ * Fallback JSON extraction when flex-md is not available
551
+ */
552
+ function extractJsonFallback(content) {
553
+ // Look for fenced JSON blocks
554
+ const jsonBlockRegex = /```(?:json)?\s*\n?([\s\S]*?)\n?```/g;
555
+ let match;
556
+ while ((match = jsonBlockRegex.exec(content)) !== null) {
557
+ try {
558
+ const jsonContent = match[1].trim();
559
+ const parsed = JSON.parse(jsonContent);
560
+ if (parsed !== null && typeof parsed === 'object') {
561
+ return {
562
+ json: parsed,
563
+ method: 'fallback-json-block'
564
+ };
565
+ }
566
+ }
567
+ catch (e) {
568
+ // Continue to next block
569
+ }
570
+ }
571
+ // No structured data found, return raw text wrapper
572
+ return {
573
+ json: { rawText: content },
574
+ method: 'fallback-raw-text'
575
+ };
576
+ }
577
+ /**
578
+ * Check if flex-md module is available
579
+ */
580
+ function isFlexMdAvailable() {
581
+ return flexMdModule !== null;
582
+ }
583
+ /**
584
+ * Get maxTokens for a model from flex-md if available
585
+ * Tries various flex-md APIs to get model information and maxTokens
586
+ *
587
+ * @param provider - Provider name (e.g., 'openai', 'anthropic')
588
+ * @param model - Model name (e.g., 'gpt-4', 'claude-3-opus')
589
+ * @returns maxTokens if found, null otherwise
590
+ */
591
+ async function getModelMaxTokensFromFlexMd(provider, model) {
592
+ try {
593
+ const flexMd = await loadFlexMd();
594
+ if (!flexMd || typeof flexMd !== 'object') {
595
+ return null;
596
+ }
597
+ // Try various possible function names that flex-md might export
598
+ const possibleFunctions = [
599
+ 'getModel',
600
+ 'getModelInfo',
601
+ 'getModelContext',
602
+ 'getMaxTokens',
603
+ 'modelRegistry',
604
+ 'getModelMaxTokens'
605
+ ];
606
+ for (const funcName of possibleFunctions) {
607
+ if (flexMd[funcName] && typeof flexMd[funcName] === 'function') {
608
+ try {
609
+ const result = flexMd[funcName](provider, model);
610
+ // Handle different possible return formats
611
+ if (result) {
612
+ // Case 1: Direct number
613
+ if (typeof result === 'number') {
614
+ return result;
615
+ }
616
+ // Case 2: Object with maxTokens or maxOutputTokens property
617
+ if (typeof result === 'object') {
618
+ if (typeof result.maxTokens === 'number') {
619
+ return result.maxTokens;
620
+ }
621
+ if (typeof result.maxOutputTokens === 'number') {
622
+ return result.maxOutputTokens;
623
+ }
624
+ // Case 3: Nested context object
625
+ if (result.context) {
626
+ if (typeof result.context.maxTokens === 'number') {
627
+ return result.context.maxTokens;
628
+ }
629
+ if (typeof result.context.maxOutputTokens === 'number') {
630
+ return result.context.maxOutputTokens;
631
+ }
632
+ }
633
+ }
634
+ }
635
+ }
636
+ catch (e) {
637
+ // Function exists but call failed, continue to next
638
+ continue;
639
+ }
640
+ }
641
+ }
642
+ // Try registry pattern if available
643
+ if (flexMd.modelRegistry && typeof flexMd.modelRegistry === 'object') {
644
+ const registry = flexMd.modelRegistry;
645
+ const key = `${provider}/${model}`;
646
+ const altKey = `${provider}:${model}`;
647
+ const modelInfo = registry[key] || registry[altKey] || registry[model];
648
+ if (modelInfo) {
649
+ if (typeof modelInfo.maxTokens === 'number') {
650
+ return modelInfo.maxTokens;
651
+ }
652
+ if (typeof modelInfo.maxOutputTokens === 'number') {
653
+ return modelInfo.maxOutputTokens;
654
+ }
655
+ if (modelInfo.context && typeof modelInfo.context.maxOutputTokens === 'number') {
656
+ return modelInfo.context.maxOutputTokens;
657
+ }
658
+ }
659
+ }
660
+ return null;
661
+ }
662
+ catch (error) {
663
+ // flex-md not available or doesn't have model info functionality
664
+ return null;
665
+ }
666
+ }
667
+ /**
668
+ * Get flex-md module directly (must call loadFlexMd() first)
669
+ */
670
+ function getFlexMdModule() {
671
+ if (!flexMdModule) {
672
+ throw new Error('flex-md module not loaded. Call loadFlexMd() first.');
673
+ }
674
+ return flexMdModule;
675
+ }
676
+ /**
677
+ * Build dynamic instructions from an object/schema (flex-md 3.1+ feature)
678
+ * Takes an object (like a JSON schema) and generates flex-md format instructions
679
+ *
680
+ * @param schemaObject - The object/schema to generate instructions from
681
+ * @param options - Optional configuration for instruction generation
682
+ * @returns Generated flex-md format instructions as string
683
+ */
684
+ /**
685
+ * Manually generates OUTPUT FORMAT instructions from JSON Schema
686
+ * This is a fallback when flex-md's buildDynamicInstructions doesn't exist
687
+ */
688
+ function generateFormatInstructionsFromSchema(schema, options) {
689
+ const parts = [];
690
+ if (options?.title || options?.description) {
691
+ parts.push(`Output Format: ${options.title || 'Structured Data'}`);
692
+ if (options.description) {
693
+ parts.push(options.description);
694
+ }
695
+ }
696
+ // Extract properties from JSON Schema
697
+ if (schema.type === 'object' && schema.properties) {
698
+ parts.push('\nReturn a JSON object with the following structure:');
699
+ parts.push('\n```json');
700
+ parts.push(JSON.stringify(schema, null, 2));
701
+ parts.push('```');
702
+ // Add property descriptions if available
703
+ const propertyDescriptions = [];
704
+ for (const [key, prop] of Object.entries(schema.properties)) {
705
+ if (typeof prop === 'object' && prop !== null) {
706
+ const propObj = prop;
707
+ const desc = propObj.description || '';
708
+ const type = propObj.type || 'unknown';
709
+ const enumVals = propObj.enum ? ` (one of: ${propObj.enum.join(', ')})` : '';
710
+ if (desc || enumVals) {
711
+ propertyDescriptions.push(`- \`${key}\` (${type})${enumVals}: ${desc || 'No description'}`);
712
+ }
713
+ else {
714
+ propertyDescriptions.push(`- \`${key}\` (${type})`);
715
+ }
716
+ }
717
+ }
718
+ if (propertyDescriptions.length > 0) {
719
+ parts.push('\nProperties:');
720
+ parts.push(...propertyDescriptions);
721
+ }
722
+ // Add required fields note
723
+ if (schema.required && Array.isArray(schema.required) && schema.required.length > 0) {
724
+ parts.push(`\nRequired fields: ${schema.required.join(', ')}`);
725
+ }
726
+ }
727
+ else {
728
+ // Not a standard JSON Schema object, just show the schema
729
+ parts.push('\nReturn a JSON object matching this schema:');
730
+ parts.push('\n```json');
731
+ parts.push(JSON.stringify(schema, null, 2));
732
+ parts.push('```');
733
+ }
734
+ parts.push('\n\nIMPORTANT: Return your answer as JSON inside the markdown fenced block. The JSON must match the schema above exactly.');
735
+ return parts.join('\n');
736
+ }
737
+ async function buildDynamicInstructions(schemaObject, options) {
738
+ try {
739
+ const flexMd = await loadFlexMd();
740
+ // Check if dynamic instruction feature is available (flex-md 3.1+)
741
+ if (flexMd.buildDynamicInstructions && typeof flexMd.buildDynamicInstructions === 'function') {
742
+ const result = flexMd.buildDynamicInstructions(schemaObject, options);
743
+ const finalResult = typeof result === 'string' ? result : (result?.instructions || result?.format || null);
744
+ return finalResult;
745
+ }
746
+ // Fallback: Generate format instructions manually from JSON Schema
747
+ const manualInstructions = generateFormatInstructionsFromSchema(schemaObject, options);
748
+ return manualInstructions;
749
+ }
750
+ catch (error) {
751
+ // On error, try manual fallback
752
+ try {
753
+ const manualInstructions = generateFormatInstructionsFromSchema(schemaObject, options);
754
+ return manualInstructions;
755
+ }
756
+ catch (fallbackError) {
757
+ return null;
758
+ }
759
+ }
760
+ }
761
+ /**
762
+ * Enrich instructions with flex-md compliance guidance (flex-md 3.0+ feature)
763
+ * Adds appropriate flex-md format instructions based on strictness level
764
+ *
765
+ * @param instructions - Base instructions text
766
+ * @param strictnessLevel - Compliance level ('L0' | 'L1' | 'L2' | 'L3')
767
+ * @returns Enriched instructions with flex-md guidance
768
+ */
769
+ async function enrichInstructionsWithFlexMd(instructions, strictnessLevel = 'L0') {
770
+ try {
771
+ const flexMd = await loadFlexMd();
772
+ // Use native flex-md function if available (flex-md 3.2+)
773
+ if (flexMd.enrichInstructionsWithFlexMd && typeof flexMd.enrichInstructionsWithFlexMd === 'function') {
774
+ const result = flexMd.enrichInstructionsWithFlexMd(instructions, strictnessLevel);
775
+ return typeof result === 'string' ? result : instructions;
776
+ }
777
+ // Fallback to enrichInstructions if native function not available
778
+ if (flexMd.enrichInstructions && typeof flexMd.enrichInstructions === 'function') {
779
+ const result = flexMd.enrichInstructions(instructions, { strictness: strictnessLevel });
780
+ return typeof result === 'string' ? result : instructions;
781
+ }
782
+ // Fallback: return original instructions if function not available
783
+ return instructions;
784
+ }
785
+ catch (error) {
786
+ // On error, return original instructions
787
+ return instructions;
788
+ }
789
+ }
790
+ /**
791
+ * Get strictness defaults for a compliance level (flex-md 3.0+ feature)
792
+ * Returns configuration object for the specified compliance level
793
+ *
794
+ * @param strictnessLevel - Compliance level ('L0' | 'L1' | 'L2' | 'L3')
795
+ * @returns Configuration object with level-specific defaults
796
+ */
797
+ async function getStrictnessDefaults(strictnessLevel = 'L0') {
798
+ try {
799
+ const flexMd = await loadFlexMd();
800
+ if (flexMd.strictnessDefaults && typeof flexMd.strictnessDefaults === 'function') {
801
+ return flexMd.strictnessDefaults(strictnessLevel);
802
+ }
803
+ return null;
804
+ }
805
+ catch (error) {
806
+ return null;
807
+ }
808
+ }
809
+ /**
810
+ * Enforce flex-md format in instructions (flex-md 3.0+ feature)
811
+ * Adds enforcement rules based on output format spec and strictness level
812
+ *
813
+ * @param instructions - Base instructions text
814
+ * @param outputFormatSpec - Output format specification (optional)
815
+ * @param strictnessLevel - Compliance level ('L0' | 'L1' | 'L2' | 'L3')
816
+ * @returns Instructions with flex-md enforcement rules
817
+ */
818
+ async function enforceFlexMdFormat(instructions, outputFormatSpec, strictnessLevel = 'L0') {
819
+ try {
820
+ const flexMd = await loadFlexMd();
821
+ if (flexMd.enforceFlexMd && typeof flexMd.enforceFlexMd === 'function') {
822
+ const spec = outputFormatSpec || { strictness: strictnessLevel };
823
+ if (!spec.strictness) {
824
+ spec.strictness = strictnessLevel;
825
+ }
826
+ const result = flexMd.enforceFlexMd(instructions, spec);
827
+ return typeof result === 'string' ? result : instructions;
828
+ }
829
+ // Fallback: return original instructions if function not available
830
+ return instructions;
831
+ }
832
+ catch (error) {
833
+ // On error, return original instructions
834
+ return instructions;
835
+ }
836
+ }
837
+ /**
838
+ * Check if instructions meet a specific flex-md compliance level
839
+ *
840
+ * @param instructions - Instructions text to check
841
+ * @param complianceLevel - Required compliance level
842
+ * @returns true if instructions meet the compliance level
843
+ */
844
+ async function hasFlexMdContract(instructions, complianceLevel = 'L0') {
845
+ if (!instructions || instructions.trim() === '') {
846
+ return false;
847
+ }
848
+ try {
849
+ const flexMd = await loadFlexMd();
850
+ // Use native flex-md function if available (flex-md 3.2+)
851
+ if (flexMd.hasFlexMdContract && typeof flexMd.hasFlexMdContract === 'function') {
852
+ try {
853
+ const result = flexMd.hasFlexMdContract(instructions, complianceLevel);
854
+ return typeof result === 'boolean' ? result : false;
855
+ }
856
+ catch (e) {
857
+ // Native function might have different signature, fall through
858
+ }
859
+ }
860
+ // Fallback: Check if validateMarkdownAgainstOfs is available
861
+ if (flexMd.validateMarkdownAgainstOfs && typeof flexMd.validateMarkdownAgainstOfs === 'function') {
862
+ try {
863
+ // Try to validate - this might require a spec, so we catch errors
864
+ const result = flexMd.validateMarkdownAgainstOfs(instructions, { strictness: complianceLevel });
865
+ // If validation passes, instructions meet compliance
866
+ if (result && result.valid !== false) {
867
+ return true;
868
+ }
869
+ }
870
+ catch (e) {
871
+ // Validation function might need more parameters, fall through to pattern matching
872
+ }
873
+ }
874
+ }
875
+ catch (error) {
876
+ // flex-md SDK not available or error - fall through to pattern matching
877
+ }
878
+ // Fallback: Pattern-based checking
879
+ const text = instructions.toLowerCase();
880
+ // Check for key flex-md enforcement phrases
881
+ const flexMdIndicators = [
882
+ 'markdown',
883
+ 'fenced block',
884
+ '```markdown',
885
+ '```json',
886
+ 'reply in markdown',
887
+ 'return your entire answer'
888
+ ];
889
+ // Check for enforcement language based on compliance level
890
+ const enforcementIndicators = [];
891
+ if (complianceLevel === 'L2' || complianceLevel === 'L3') {
892
+ // L2/L3 require fenced block container
893
+ enforcementIndicators.push('fenced block', '```markdown', 'single ```markdown', 'entire answer inside', 'nothing else');
894
+ }
895
+ if (complianceLevel === 'L1' || complianceLevel === 'L2' || complianceLevel === 'L3') {
896
+ // L1+ requires section headings
897
+ enforcementIndicators.push('section', 'heading', 'include these');
898
+ }
899
+ // Must have at least one flex-md indicator AND appropriate enforcement indicators
900
+ const hasFlexMd = flexMdIndicators.some(indicator => text.includes(indicator));
901
+ const hasEnforcement = enforcementIndicators.length === 0 ||
902
+ enforcementIndicators.some(indicator => text.includes(indicator));
903
+ return hasFlexMd && hasEnforcement;
904
+ }
905
+ /**
906
+ * Detect the actual compliance level that instructions meet
907
+ * Checks from highest (L3) to lowest (L0) to find the highest level met
908
+ *
909
+ * @param instructions - Instructions text to check
910
+ * @returns The highest compliance level met ('L0' | 'L1' | 'L2' | 'L3' | null if none)
911
+ */
912
+ async function detectComplianceLevel(instructions) {
913
+ if (!instructions || instructions.trim() === '') {
914
+ return null;
915
+ }
916
+ try {
917
+ const flexMd = await loadFlexMd();
918
+ // Check from highest to lowest level using native hasFlexMdContract
919
+ const levels = ['L3', 'L2', 'L1', 'L0'];
920
+ // Use native hasFlexMdContract if available (flex-md 3.2+)
921
+ if (flexMd.hasFlexMdContract && typeof flexMd.hasFlexMdContract === 'function') {
922
+ for (const level of levels) {
923
+ try {
924
+ const meetsLevel = flexMd.hasFlexMdContract(instructions, level);
925
+ if (meetsLevel === true) {
926
+ return level;
927
+ }
928
+ }
929
+ catch (e) {
930
+ // Continue to next level
931
+ }
932
+ }
933
+ }
934
+ else {
935
+ // Fallback: Use our wrapper function
936
+ for (const level of levels) {
937
+ const meetsLevel = await hasFlexMdContract(instructions, level);
938
+ if (meetsLevel) {
939
+ return level;
940
+ }
941
+ }
942
+ }
943
+ // If flex-md SDK is available, try using validateMarkdownAgainstOfs directly
944
+ if (flexMd.validateMarkdownAgainstOfs && typeof flexMd.validateMarkdownAgainstOfs === 'function') {
945
+ // Try L3 first (highest)
946
+ for (const level of levels) {
947
+ try {
948
+ const result = flexMd.validateMarkdownAgainstOfs(instructions, { strictness: level });
949
+ if (result && result.valid !== false) {
950
+ return level;
951
+ }
952
+ }
953
+ catch (e) {
954
+ // Continue to next level
955
+ }
956
+ }
957
+ }
958
+ // Fallback: Pattern-based detection
959
+ const text = instructions.toLowerCase();
960
+ // Check for L3 indicators (strictest)
961
+ const hasL3Indicators = text.includes('fenced block') &&
962
+ text.includes('section') &&
963
+ (text.includes('required') || text.includes('must'));
964
+ // Check for L2 indicators (container + sections)
965
+ const hasL2Indicators = (text.includes('```markdown') || text.includes('fenced block')) &&
966
+ (text.includes('section') || text.includes('heading'));
967
+ // Check for L1 indicators (sections only)
968
+ const hasL1Indicators = (text.includes('section') || text.includes('heading')) &&
969
+ text.includes('markdown');
970
+ // Check for L0 indicators (basic markdown)
971
+ const hasL0Indicators = text.includes('markdown');
972
+ if (hasL3Indicators)
973
+ return 'L3';
974
+ if (hasL2Indicators)
975
+ return 'L2';
976
+ if (hasL1Indicators)
977
+ return 'L1';
978
+ if (hasL0Indicators)
979
+ return 'L0';
980
+ return null;
981
+ }
982
+ catch (error) {
983
+ // On error, return null (unknown)
984
+ return null;
985
+ }
986
+ }