neuralos 3.9.5 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +103 -337
- package/cli.js +103 -0
- package/engine.js +124 -0
- package/index.js +230 -0
- package/package.json +38 -43
- package/test.js +20 -0
- package/tools.js +89 -0
- package/validate.js +263 -0
- package/bin/gybackend.cjs +0 -411663
- package/plugins/agentspan-bridge/agentspan-bridge.extreme.spec.ts +0 -328
- package/plugins/agentspan-bridge/agentspan-bridge.phase2.extreme.spec.ts +0 -282
- package/plugins/agentspan-bridge/conductorClient.mjs +0 -182
- package/plugins/agentspan-bridge/index.mjs +0 -389
- package/plugins/agentspan-bridge/playbookToWorkflowDef.mjs +0 -209
- package/plugins/agentspan-bridge/plugin.json +0 -27
- package/plugins/fraudops/index.d.mts +0 -9
- package/plugins/fraudops/index.mjs +0 -185
- package/plugins/fraudops/plugin.json +0 -10
- package/plugins/iam-connector/index.d.mts +0 -10
- package/plugins/iam-connector/index.mjs +0 -229
- package/plugins/iam-connector/plugin.json +0 -10
- package/plugins/mitmproxy-bridge/index.d.mts +0 -20
- package/plugins/mitmproxy-bridge/index.mjs +0 -256
- package/plugins/mitmproxy-bridge/plugin.json +0 -17
- package/plugins/monid-bridge/index.mjs +0 -228
- package/plugins/monid-bridge/monid-bridge.extreme.spec.mjs +0 -75
- package/plugins/monid-bridge/plugin.json +0 -15
- package/plugins/netdata-rterm/index.mjs +0 -229
- package/plugins/netdata-rterm/netdata-rterm.extreme.spec.ts +0 -252
- package/plugins/netdata-rterm/plugin.json +0 -22
- package/plugins/netexec-bridge/index.d.mts +0 -28
- package/plugins/netexec-bridge/index.mjs +0 -325
- package/plugins/netexec-bridge/plugin.json +0 -20
- package/plugins/neuralos/index.mjs +0 -137
- package/plugins/neuralos/neuralos.extreme.spec.mts +0 -386
- package/plugins/neuralos/neuralosEngine.mjs +0 -369
- package/plugins/neuralos/plugin.json +0 -15
- package/plugins/numbat-bridge/index.mjs +0 -229
- package/plugins/numbat-bridge/numbat-bridge.extreme.spec.mjs +0 -148
- package/plugins/numbat-bridge/plugin.json +0 -21
- package/plugins/patch-manager/index.d.mts +0 -10
- package/plugins/patch-manager/index.mjs +0 -288
- package/plugins/patch-manager/plugin.json +0 -10
- package/plugins/promptfoo-redteam/index.d.mts +0 -15
- package/plugins/promptfoo-redteam/index.mjs +0 -277
- package/plugins/promptfoo-redteam/plugin.json +0 -16
- package/plugins/reactorpro-bridge/index.mjs +0 -499
- package/plugins/reactorpro-bridge/plugin.json +0 -26
- package/plugins/reactorpro-bridge/reactorpro-bridge.extreme.spec.mts +0 -409
- package/plugins/reactorpro-bridge/reactorproAgent.mjs +0 -399
- package/plugins/request-router/index.d.mts +0 -9
- package/plugins/request-router/index.mjs +0 -239
- package/plugins/request-router/plugin.json +0 -10
- package/plugins/sample-k8s-slo/index.ts +0 -58
- package/plugins/sample-k8s-slo/plugin.json +0 -11
- package/plugins/sop-assistant/index.d.mts +0 -9
- package/plugins/sop-assistant/index.mjs +0 -325
- package/plugins/sop-assistant/plugin.json +0 -10
- package/plugins/synapse-bridge/index.mjs +0 -484
- package/plugins/synapse-bridge/plugin.json +0 -29
- package/plugins/synapse-bridge/synapse-bridge.extreme.spec.mjs +0 -204
- package/plugins/synapse-bridge/synapseAgent.extreme.spec.mjs +0 -236
- package/plugins/synapse-bridge/synapseAgent.mjs +0 -254
- package/plugins/web-intel/index.mjs +0 -381
- package/plugins/web-intel/plugin.json +0 -28
- package/plugins/web-intel/sidecar.mjs +0 -134
- package/plugins/web-intel/web-intel.extreme.spec.ts +0 -263
- package/plugins/web-intel/wigoloClient.mjs +0 -145
package/validate.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grounding + extraction validation — the Node port of the Python
|
|
3
|
+
* `needle` validation core (needle/__init__.py), kept behaviour-compatible:
|
|
4
|
+
*
|
|
5
|
+
* * sourceYears() — years literally written in the input
|
|
6
|
+
* * licensedYears() — those years, plus the system date's year when the
|
|
7
|
+
* input reasons relatively ("tomorrow", "next week")
|
|
8
|
+
* * walkGrounding() — date arguments must carry a licensed year
|
|
9
|
+
* * groundedNumberPaths() — numbers a value may carry without being invented
|
|
10
|
+
* * annotateUngrounded() — the engine's own fabrication report, re-checked
|
|
11
|
+
* * validateExtraction() — strict mode: raise instead of returning
|
|
12
|
+
* ungrounded values silently
|
|
13
|
+
*
|
|
14
|
+
* Python raises ExtractionValidationError; so do we.
|
|
15
|
+
*/
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
class ExtractionValidationError extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'ExtractionValidationError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MONTHS =
|
|
26
|
+
'(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|' +
|
|
27
|
+
'jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|' +
|
|
28
|
+
'oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)';
|
|
29
|
+
|
|
30
|
+
// The number after a day+month is its year, unless it is the hour of a time
|
|
31
|
+
// ("5 June 19:30", "June 5 7 pm"): no ISO argument can carry that as a year,
|
|
32
|
+
// so licensing it would reject every date. Year-first numeric dates only
|
|
33
|
+
// (2024-03-15) — a day/month-first date must not mint its leading component.
|
|
34
|
+
const SOURCE_YEAR_PATTERNS = [
|
|
35
|
+
new RegExp(`\\b\\d{1,2}(?:st|nd|rd|th)?\\s+${MONTHS}[\\s,]+(\\d{1,4})(?![0-9A-Za-z]|:\\d|\\s*[ap]\\.?m\\b)`, 'g'),
|
|
36
|
+
new RegExp(`\\b${MONTHS}\\s+\\d{1,2}(?:st|nd|rd|th)?\\s*,?\\s+(\\d{1,4})(?![0-9A-Za-z]|:\\d|\\s*[ap]\\.?m\\b)`, 'g'),
|
|
37
|
+
new RegExp(`\\b${MONTHS}[\\s,]+(\\d{3,4})(?![0-9A-Za-z])`, 'g'),
|
|
38
|
+
/\byear\s+(\d{1,4})(?![0-9A-Za-z])/g,
|
|
39
|
+
/(?<![0-9])(\d{4})(?=[-/]\d{1,2}[-/]\d{1,2}(?![0-9]))/g,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const RELATIVE_CUE = new RegExp(
|
|
43
|
+
'\\b(today|tonight|tomorrow|yesterday|next|this|coming|now|in \\d+ (?:days?|weeks?|months?|years?)|' +
|
|
44
|
+
'monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\b', 'i');
|
|
45
|
+
|
|
46
|
+
const NUMBER_TOKEN =
|
|
47
|
+
/(?<![\w.,])(?:[-+]?\d{1,3}(?:,\d{3})+(?:\.\d+)?|[-+]?\d+(?:\.\d+)?)(?!\d)/g;
|
|
48
|
+
|
|
49
|
+
const DATE_FACT =
|
|
50
|
+
/date:\s*\d{4}-\d{2}-\d{2}(?:\s+[A-Za-z]{3})?(?:\s+\d{2}:\d{2})?|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?/g;
|
|
51
|
+
|
|
52
|
+
/** Years literally written in the text. */
|
|
53
|
+
function sourceYears(text) {
|
|
54
|
+
const lowered = String(text || '').toLowerCase();
|
|
55
|
+
const years = new Set();
|
|
56
|
+
for (const pattern of SOURCE_YEAR_PATTERNS) {
|
|
57
|
+
pattern.lastIndex = 0;
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = pattern.exec(lowered)) !== null) {
|
|
60
|
+
years.add(parseInt(match[1], 10));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return years;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function relativeCue(text) {
|
|
67
|
+
return !!text && RELATIVE_CUE.test(String(text));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Years a date argument may carry without being fabricated. */
|
|
71
|
+
function licensedYears(seenYears, system, relative = true) {
|
|
72
|
+
const years = new Set(seenYears || []);
|
|
73
|
+
if (years.size && system && relative) {
|
|
74
|
+
for (const year of sourceYears(system)) years.add(year);
|
|
75
|
+
}
|
|
76
|
+
return years;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function schemaParameters(schema) {
|
|
80
|
+
if (schema && typeof schema === 'object' && schema.parameters) return schema.parameters;
|
|
81
|
+
return schema || {};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveRef(node, root) {
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
let current = node;
|
|
87
|
+
while (current && typeof current === 'object' && current.$ref) {
|
|
88
|
+
if (seen.has(current.$ref)) break;
|
|
89
|
+
seen.add(current.$ref);
|
|
90
|
+
const parts = String(current.$ref).replace(/^#\//, '').split('/');
|
|
91
|
+
let target = root;
|
|
92
|
+
for (const part of parts) {
|
|
93
|
+
target = (target || {})[part.replace(/~1/g, '/').replace(/~0/g, '~')] || {};
|
|
94
|
+
}
|
|
95
|
+
if (target === current) break;
|
|
96
|
+
current = target;
|
|
97
|
+
}
|
|
98
|
+
return current;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Date arguments must carry a year that is licensed by the input. */
|
|
102
|
+
function walkGrounding(schema, arguments_, years) {
|
|
103
|
+
const root = schemaParameters(schema);
|
|
104
|
+
const checked = new Set();
|
|
105
|
+
const failures = new Set();
|
|
106
|
+
|
|
107
|
+
const walk = (value, node, path) => {
|
|
108
|
+
node = resolveRef(node, root) || {};
|
|
109
|
+
const variants = node.anyOf || node.oneOf || [];
|
|
110
|
+
const concrete = variants.filter((v) => (resolveRef(v, root) || {}).type !== 'null');
|
|
111
|
+
if (concrete.length === 1) node = resolveRef(concrete[0], root) || {};
|
|
112
|
+
|
|
113
|
+
if ((node.format === 'date' || node.format === 'date-time') && typeof value === 'string') {
|
|
114
|
+
const match = /^(\d{4})-/.exec(value);
|
|
115
|
+
if (match && years.size) {
|
|
116
|
+
checked.add(path);
|
|
117
|
+
if (!years.has(parseInt(match[1], 10))) failures.add(path);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
122
|
+
const properties = node.properties || {};
|
|
123
|
+
for (const [key, item] of Object.entries(value)) {
|
|
124
|
+
if (key in properties) walk(item, properties[key], path ? `${path}.${key}` : key);
|
|
125
|
+
}
|
|
126
|
+
} else if (Array.isArray(value) && node.items) {
|
|
127
|
+
value.forEach((item, index) => walk(item, node.items, `${path}[${index}]`));
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
walk(arguments_, root, '');
|
|
132
|
+
return { checked, failures };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function temporalGrounding(text, schema, arguments_, system) {
|
|
136
|
+
const years = licensedYears(sourceYears(text), system, relativeCue(text));
|
|
137
|
+
return walkGrounding(schema, arguments_, years);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function withoutDateFacts(text) {
|
|
141
|
+
return String(text || '').replace(DATE_FACT, ' ');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** (path, value) for every numeric leaf, arguments-relative. */
|
|
145
|
+
function numericPaths(arguments_, path = '') {
|
|
146
|
+
const out = [];
|
|
147
|
+
const walk = (value, currentPath) => {
|
|
148
|
+
if (typeof value === 'boolean') return;
|
|
149
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
150
|
+
out.push([currentPath, value]);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (Array.isArray(value)) {
|
|
154
|
+
value.forEach((item, index) => walk(item, `${currentPath}[${index}]`));
|
|
155
|
+
} else if (value && typeof value === 'object') {
|
|
156
|
+
for (const [key, item] of Object.entries(value)) {
|
|
157
|
+
walk(item, currentPath ? `${currentPath}.${key}` : String(key));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
walk(arguments_, path);
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Numeric values written in the source texts (separators normalized). */
|
|
166
|
+
function sourceNumbers(...sources) {
|
|
167
|
+
const text = sources
|
|
168
|
+
.map((source) => withoutDateFacts(source))
|
|
169
|
+
.filter((source) => source)
|
|
170
|
+
.join('\n');
|
|
171
|
+
const numbers = new Set();
|
|
172
|
+
NUMBER_TOKEN.lastIndex = 0;
|
|
173
|
+
let match;
|
|
174
|
+
while ((match = NUMBER_TOKEN.exec(text)) !== null) {
|
|
175
|
+
const normalized = match[0].replace(/,/g, '');
|
|
176
|
+
const asNumber = Number(normalized);
|
|
177
|
+
if (Number.isFinite(asNumber)) numbers.add(asNumber);
|
|
178
|
+
}
|
|
179
|
+
return numbers;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function groundedNumberPaths(arguments_, ...sources) {
|
|
183
|
+
const numbers = sourceNumbers(...sources);
|
|
184
|
+
const grounded = new Set();
|
|
185
|
+
if (!numbers.size) return grounded;
|
|
186
|
+
for (const [path, value] of numericPaths(arguments_)) {
|
|
187
|
+
if (numbers.has(value)) grounded.add(path);
|
|
188
|
+
}
|
|
189
|
+
return grounded;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The engine's fabrication report, grouped by tool. */
|
|
193
|
+
function ungroundedPaths(response) {
|
|
194
|
+
const validation = (response && response.validation) || {};
|
|
195
|
+
const grouped = new Map();
|
|
196
|
+
for (const name of validation.ungrounded || []) {
|
|
197
|
+
const text = String(name);
|
|
198
|
+
const dot = text.indexOf('.');
|
|
199
|
+
const tool = dot === -1 ? text : text.slice(0, dot);
|
|
200
|
+
const path = dot === -1 ? text : text.slice(dot + 1);
|
|
201
|
+
if (!grouped.has(tool)) grouped.set(tool, new Set());
|
|
202
|
+
grouped.get(tool).add(path || tool);
|
|
203
|
+
}
|
|
204
|
+
return grouped;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Add engine-reported ungrounded values to response.validation.ungrounded. */
|
|
208
|
+
function annotateUngrounded(response, toolSchemas, seenYears, system, text) {
|
|
209
|
+
const calls = response.function_calls || [];
|
|
210
|
+
const years = licensedYears(seenYears, system, text !== undefined && text !== null ? relativeCue(text) : true);
|
|
211
|
+
if (!calls.length || !years.size) return;
|
|
212
|
+
const schemas = new Map();
|
|
213
|
+
for (const entry of toolSchemas || []) {
|
|
214
|
+
if (entry && entry.name) schemas.set(entry.name, entry);
|
|
215
|
+
}
|
|
216
|
+
const found = [];
|
|
217
|
+
for (const call of calls) {
|
|
218
|
+
const schema = schemas.get(call.name);
|
|
219
|
+
if (!schema) continue;
|
|
220
|
+
const { failures } = walkGrounding(schema, call.arguments || {}, years);
|
|
221
|
+
for (const path of [...failures].sort()) found.push(`${call.name}.${path}`);
|
|
222
|
+
}
|
|
223
|
+
if (!found.length) return;
|
|
224
|
+
const validation = response.validation || {};
|
|
225
|
+
const existing = [...(validation.ungrounded || [])];
|
|
226
|
+
validation.ungrounded = existing.concat(found.filter((name) => !existing.includes(name)));
|
|
227
|
+
response.validation = validation;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Strict mode: raise instead of returning values the input does not license. */
|
|
231
|
+
function validateExtraction(text, schema, arguments_, response, system) {
|
|
232
|
+
const { checked, failures } = temporalGrounding(text, schema, arguments_, system);
|
|
233
|
+
const validation = (response && response.validation) || {};
|
|
234
|
+
const flagged = validation.ungrounded || [];
|
|
235
|
+
const grounded = flagged.length ? groundedNumberPaths(arguments_, text, system) : new Set();
|
|
236
|
+
for (const name of flagged) {
|
|
237
|
+
const path = String(name).includes('.') ? String(name).split('.').slice(1).join('.') : String(name);
|
|
238
|
+
if (grounded.has(path)) continue;
|
|
239
|
+
if (!checked.has(path) || failures.has(path)) failures.add(path);
|
|
240
|
+
}
|
|
241
|
+
if (validation.negation) failures.add('negated request');
|
|
242
|
+
if (failures.size) {
|
|
243
|
+
const detail = [...failures].sort().join(', ');
|
|
244
|
+
throw new ExtractionValidationError(
|
|
245
|
+
`extraction returned values not grounded in the input: ${detail}`);
|
|
246
|
+
}
|
|
247
|
+
return arguments_;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
module.exports = {
|
|
251
|
+
ExtractionValidationError,
|
|
252
|
+
sourceYears,
|
|
253
|
+
relativeCue,
|
|
254
|
+
licensedYears,
|
|
255
|
+
walkGrounding,
|
|
256
|
+
temporalGrounding,
|
|
257
|
+
groundedNumberPaths,
|
|
258
|
+
sourceNumbers,
|
|
259
|
+
numericPaths,
|
|
260
|
+
ungroundedPaths,
|
|
261
|
+
annotateUngrounded,
|
|
262
|
+
validateExtraction,
|
|
263
|
+
};
|