nexus-ai-pro 2.3.0 → 2.4.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/API_STABILITY.md +186 -0
- package/CHANGELOG.md +184 -1
- package/README.md +14 -5
- package/SECURITY.md +140 -0
- package/dist/adapter-kit/contracts.d.ts +87 -0
- package/dist/adapter-kit/contracts.js +237 -0
- package/dist/adapter-kit/errors.d.ts +39 -0
- package/dist/adapter-kit/errors.js +52 -0
- package/dist/adapter-kit/index.d.ts +144 -0
- package/dist/adapter-kit/index.js +178 -0
- package/dist/adapter-kit/semver.d.ts +12 -0
- package/dist/adapter-kit/semver.js +55 -0
- package/dist/agent/middleware/pii.js +2 -33
- package/dist/core/secure-stream.js +4 -3
- package/dist/embeddings/adapters.js +2 -1
- package/dist/evals/judge.js +2 -5
- package/dist/evaluate/cache.d.ts +0 -14
- package/dist/evaluate/cache.js +0 -30
- package/dist/evaluate/file-cache.d.ts +15 -0
- package/dist/evaluate/file-cache.js +30 -0
- package/dist/evaluate/index.d.ts +2 -1
- package/dist/evaluate/index.js +2 -1
- package/dist/evaluate/review.js +2 -2
- package/dist/evaluate/run.js +2 -2
- package/dist/evaluate/version.js +3 -3
- package/dist/graph/graph.js +2 -2
- package/dist/graph/node-cache.js +3 -3
- package/dist/hallucination/rag.d.ts +4 -1
- package/dist/hallucination/rag.js +6 -1
- package/dist/images/asset-stores.js +2 -3
- package/dist/images/asset-support.js +2 -2
- package/dist/images/assets.js +1 -2
- package/dist/images/evals.js +4 -4
- package/dist/insights/proposals.js +1 -2
- package/dist/insights/regressions.d.ts +26 -2
- package/dist/insights/regressions.js +129 -3
- package/dist/loaders/git.js +8 -1
- package/dist/loaders/web.js +7 -1
- package/dist/mcp/client.d.ts +14 -0
- package/dist/mcp/client.js +59 -1
- package/dist/mcp/registry.d.ts +6 -0
- package/dist/mcp/registry.js +7 -0
- package/dist/prompts/file.js +28 -11
- package/dist/protocols/a2a.d.ts +235 -0
- package/dist/protocols/a2a.js +386 -0
- package/dist/protocols/acp.d.ts +87 -0
- package/dist/protocols/acp.js +322 -0
- package/dist/protocols/ag-ui.d.ts +120 -0
- package/dist/protocols/ag-ui.js +180 -0
- package/dist/protocols/shared.d.ts +80 -0
- package/dist/protocols/shared.js +49 -0
- package/dist/providers/anthropic.js +1 -0
- package/dist/providers/cohere.js +2 -2
- package/dist/providers/google.js +2 -11
- package/dist/providers/openai.js +1 -0
- package/dist/rag/file-ingestion.js +15 -9
- package/dist/runtime/detect.d.ts +17 -0
- package/dist/runtime/detect.js +16 -0
- package/dist/runtime/index.d.ts +19 -0
- package/dist/runtime/index.js +7 -0
- package/dist/security/injection-detector.d.ts +2 -0
- package/dist/security/injection-detector.js +7 -1
- package/dist/security/input-guard.js +5 -2
- package/dist/security/output-guard.js +8 -4
- package/dist/security/pii-detector.js +22 -6
- package/dist/security/prompt-hardening.js +6 -2
- package/dist/security/semantic-injection-classifier.d.ts +2 -1
- package/dist/security/semantic-injection-classifier.js +31 -35
- package/dist/security/upload-scanner.d.ts +8 -0
- package/dist/security/upload-scanner.js +51 -9
- package/dist/server/deployments.d.ts +63 -1
- package/dist/server/deployments.js +75 -2
- package/dist/server/runs.d.ts +9 -0
- package/dist/server/runs.js +20 -0
- package/dist/server/server.js +5 -0
- package/dist/testing/record.js +2 -2
- package/dist/tracing/stores.js +2 -1
- package/dist/tracing/tracer.js +2 -2
- package/dist/types/config.d.ts +20 -0
- package/dist/types/server.d.ts +6 -0
- package/dist/utils/base64.d.ts +4 -0
- package/dist/utils/base64.js +21 -0
- package/dist/utils/checksums.d.ts +22 -0
- package/dist/utils/checksums.js +39 -0
- package/dist/utils/ids.d.ts +8 -0
- package/dist/utils/ids.js +26 -2
- package/dist/utils/sha256.d.ts +22 -0
- package/dist/utils/sha256.js +120 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
- package/dist-cjs/adapter-kit/contracts.js +243 -0
- package/dist-cjs/adapter-kit/errors.d.ts +39 -0
- package/dist-cjs/adapter-kit/errors.js +57 -0
- package/dist-cjs/adapter-kit/index.d.ts +144 -0
- package/dist-cjs/adapter-kit/index.js +194 -0
- package/dist-cjs/adapter-kit/semver.d.ts +12 -0
- package/dist-cjs/adapter-kit/semver.js +59 -0
- package/dist-cjs/agent/middleware/pii.js +4 -35
- package/dist-cjs/core/secure-stream.js +4 -3
- package/dist-cjs/embeddings/adapters.js +2 -1
- package/dist-cjs/evals/judge.js +2 -5
- package/dist-cjs/evaluate/cache.d.ts +0 -14
- package/dist-cjs/evaluate/cache.js +1 -65
- package/dist-cjs/evaluate/file-cache.d.ts +15 -0
- package/dist-cjs/evaluate/file-cache.js +67 -0
- package/dist-cjs/evaluate/index.d.ts +2 -1
- package/dist-cjs/evaluate/index.js +3 -2
- package/dist-cjs/evaluate/review.js +2 -2
- package/dist-cjs/evaluate/run.js +2 -2
- package/dist-cjs/evaluate/version.js +3 -3
- package/dist-cjs/graph/graph.js +2 -2
- package/dist-cjs/graph/node-cache.js +3 -3
- package/dist-cjs/hallucination/rag.d.ts +4 -1
- package/dist-cjs/hallucination/rag.js +6 -1
- package/dist-cjs/images/asset-stores.js +2 -3
- package/dist-cjs/images/asset-support.js +2 -2
- package/dist-cjs/images/assets.js +1 -2
- package/dist-cjs/images/evals.js +4 -4
- package/dist-cjs/insights/proposals.js +1 -2
- package/dist-cjs/insights/regressions.d.ts +26 -2
- package/dist-cjs/insights/regressions.js +129 -3
- package/dist-cjs/loaders/git.js +8 -1
- package/dist-cjs/loaders/web.js +7 -1
- package/dist-cjs/mcp/client.d.ts +14 -0
- package/dist-cjs/mcp/client.js +60 -1
- package/dist-cjs/mcp/registry.d.ts +6 -0
- package/dist-cjs/mcp/registry.js +7 -0
- package/dist-cjs/prompts/file.js +28 -11
- package/dist-cjs/protocols/a2a.d.ts +235 -0
- package/dist-cjs/protocols/a2a.js +393 -0
- package/dist-cjs/protocols/acp.d.ts +87 -0
- package/dist-cjs/protocols/acp.js +326 -0
- package/dist-cjs/protocols/ag-ui.d.ts +120 -0
- package/dist-cjs/protocols/ag-ui.js +186 -0
- package/dist-cjs/protocols/shared.d.ts +80 -0
- package/dist-cjs/protocols/shared.js +59 -0
- package/dist-cjs/providers/anthropic.js +1 -0
- package/dist-cjs/providers/cohere.js +2 -2
- package/dist-cjs/providers/google.js +3 -12
- package/dist-cjs/providers/openai.js +1 -0
- package/dist-cjs/rag/file-ingestion.js +14 -8
- package/dist-cjs/runtime/detect.d.ts +17 -0
- package/dist-cjs/runtime/detect.js +19 -0
- package/dist-cjs/runtime/index.d.ts +19 -0
- package/dist-cjs/runtime/index.js +31 -0
- package/dist-cjs/security/injection-detector.d.ts +2 -0
- package/dist-cjs/security/injection-detector.js +7 -1
- package/dist-cjs/security/input-guard.js +5 -2
- package/dist-cjs/security/output-guard.js +8 -4
- package/dist-cjs/security/pii-detector.js +22 -6
- package/dist-cjs/security/prompt-hardening.js +6 -2
- package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
- package/dist-cjs/security/semantic-injection-classifier.js +31 -35
- package/dist-cjs/security/upload-scanner.d.ts +8 -0
- package/dist-cjs/security/upload-scanner.js +53 -9
- package/dist-cjs/server/deployments.d.ts +63 -1
- package/dist-cjs/server/deployments.js +75 -2
- package/dist-cjs/server/runs.d.ts +9 -0
- package/dist-cjs/server/runs.js +20 -0
- package/dist-cjs/server/server.js +5 -0
- package/dist-cjs/testing/record.js +2 -2
- package/dist-cjs/tracing/stores.js +2 -1
- package/dist-cjs/tracing/tracer.js +2 -2
- package/dist-cjs/types/config.d.ts +20 -0
- package/dist-cjs/types/server.d.ts +6 -0
- package/dist-cjs/utils/base64.d.ts +4 -0
- package/dist-cjs/utils/base64.js +25 -0
- package/dist-cjs/utils/checksums.d.ts +22 -0
- package/dist-cjs/utils/checksums.js +46 -0
- package/dist-cjs/utils/ids.d.ts +8 -0
- package/dist-cjs/utils/ids.js +28 -2
- package/dist-cjs/utils/sha256.d.ts +22 -0
- package/dist-cjs/utils/sha256.js +125 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +5 -2
- package/package.json +76 -6
package/dist/images/evals.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Sha256, sha256Hex } from '../utils/sha256.js';
|
|
2
2
|
import { linkSignals } from '../utils/signals.js';
|
|
3
3
|
import { ImageValidationError } from './errors.js';
|
|
4
4
|
export class MemoryReviewQueue {
|
|
@@ -325,14 +325,14 @@ function runScores(report) {
|
|
|
325
325
|
];
|
|
326
326
|
}
|
|
327
327
|
function mediaVersion(examples) {
|
|
328
|
-
const hash =
|
|
328
|
+
const hash = new Sha256();
|
|
329
329
|
for (const example of examples) {
|
|
330
330
|
hash.update(JSON.stringify({ id: example.id, inputs: example.inputs }, function (key, value) {
|
|
331
331
|
const original = this[key];
|
|
332
|
-
return original instanceof Uint8Array ? `bytes:${
|
|
332
|
+
return original instanceof Uint8Array ? `bytes:${sha256Hex(original)}` : value;
|
|
333
333
|
}));
|
|
334
334
|
}
|
|
335
|
-
return `v${hash.digest(
|
|
335
|
+
return `v${hash.digest().slice(0, 12)}`;
|
|
336
336
|
}
|
|
337
337
|
function uniqueIds(values) {
|
|
338
338
|
const seen = new Map();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
export class MemoryProposalStore {
|
|
3
2
|
proposals = new Map();
|
|
4
3
|
save(proposal) {
|
|
@@ -93,7 +92,7 @@ export async function proposeFix(options) {
|
|
|
93
92
|
? 'improved'
|
|
94
93
|
: 'unchanged';
|
|
95
94
|
const proposal = {
|
|
96
|
-
id: `fix-${randomUUID()}`,
|
|
95
|
+
id: `fix-${globalThis.crypto.randomUUID()}`,
|
|
97
96
|
issueId: options.issue.id,
|
|
98
97
|
issue: options.issue.summary,
|
|
99
98
|
kind: subject.kind,
|
|
@@ -33,7 +33,10 @@ export interface CompareRunsOptions {
|
|
|
33
33
|
metrics?: readonly RegressionMetric[];
|
|
34
34
|
/** Feedback keys whose mean score is compared, such as `helpfulness`. */
|
|
35
35
|
feedback?: readonly string[];
|
|
36
|
-
/**
|
|
36
|
+
/**
|
|
37
|
+
* Runs each side needs before it is judged; for a feedback score, runs that have one. Defaults to
|
|
38
|
+
* 20. A run counts once per feedback key, however many times it was rated.
|
|
39
|
+
*/
|
|
37
40
|
minRuns?: number;
|
|
38
41
|
/** A rise in p95 latency, relative, that counts. Defaults to 0.25, a quarter slower. */
|
|
39
42
|
latencyIncrease?: number;
|
|
@@ -44,6 +47,22 @@ export interface CompareRunsOptions {
|
|
|
44
47
|
minLatencyChangeMs?: number;
|
|
45
48
|
/** A rise in mean cost, relative, that counts. Defaults to 0.25. */
|
|
46
49
|
costIncrease?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Judges every metric the way an evaluation comparison does, at this confidence, such as 0.95. Each
|
|
52
|
+
* metric is resampled by bootstrap, and it regresses only when two things hold: the change's
|
|
53
|
+
* interval does not reach zero, and the change is at least that metric's minimum effect. Noise and
|
|
54
|
+
* trivia both stay below the bar. Without it, the error rate uses a z-test at 95%, and latency and
|
|
55
|
+
* cost use their margins alone.
|
|
56
|
+
*/
|
|
57
|
+
confidence?: number;
|
|
58
|
+
/** The smallest rise in the error rate, absolute, that counts under `confidence`. Defaults to 0.02. */
|
|
59
|
+
minErrorRateIncrease?: number;
|
|
60
|
+
/** The smallest fall in a feedback score's mean, absolute, that counts under `confidence`. Defaults to 0.05. */
|
|
61
|
+
minFeedbackDrop?: number;
|
|
62
|
+
/** Bootstrap resamples under `confidence`. Defaults to 1,000. */
|
|
63
|
+
resamples?: number;
|
|
64
|
+
/** Seeds the resampling, so the same runs always get the same verdict. Defaults to 1. */
|
|
65
|
+
seed?: number;
|
|
47
66
|
}
|
|
48
67
|
/** Options for `detectRegressions()`. */
|
|
49
68
|
export interface DetectRegressionsOptions {
|
|
@@ -61,7 +80,10 @@ export interface DetectRegressionsOptions {
|
|
|
61
80
|
metrics?: readonly RegressionMetric[];
|
|
62
81
|
/** Feedback keys whose mean score is compared, such as `helpfulness`. */
|
|
63
82
|
feedback?: readonly string[];
|
|
64
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Runs each window needs before a group is judged; for a feedback score, runs that have one.
|
|
85
|
+
* Defaults to 20.
|
|
86
|
+
*/
|
|
65
87
|
minRuns?: number;
|
|
66
88
|
/** A rise in p95 latency, relative, that counts. Defaults to 0.25, a quarter slower. */
|
|
67
89
|
latencyIncrease?: number;
|
|
@@ -96,6 +118,8 @@ export interface Regression {
|
|
|
96
118
|
};
|
|
97
119
|
/** One readable line. */
|
|
98
120
|
summary: string;
|
|
121
|
+
/** The change's interval, in the units of `change`, when the comparison was judged with `confidence`. */
|
|
122
|
+
interval?: [number, number];
|
|
99
123
|
}
|
|
100
124
|
/**
|
|
101
125
|
* Compares a current window of traces with a baseline window and reports what got worse, per run
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
function runScores(runs, key) {
|
|
2
|
+
return runs.flatMap((run) => {
|
|
3
|
+
const given = (run.feedback ?? [])
|
|
4
|
+
.filter((item) => item.key === key)
|
|
5
|
+
.map((item) => item.score)
|
|
6
|
+
.filter(isNumber);
|
|
7
|
+
return given.length ? [given.reduce((sum, score) => sum + score, 0) / given.length] : [];
|
|
8
|
+
});
|
|
9
|
+
}
|
|
1
10
|
export async function detectRegressions(options) {
|
|
2
11
|
const read = async (window) => {
|
|
3
12
|
const runs = await options.store.query({
|
|
@@ -27,6 +36,8 @@ export async function detectRegressions(options) {
|
|
|
27
36
|
return regressions;
|
|
28
37
|
}
|
|
29
38
|
export function compareRuns(baseline, current, options = {}) {
|
|
39
|
+
if (options.confidence !== undefined)
|
|
40
|
+
return judgedRegressions(baseline, current, options, options.confidence);
|
|
30
41
|
const name = options.group ?? 'runs';
|
|
31
42
|
const minRuns = options.minRuns ?? 20;
|
|
32
43
|
const metrics = new Set(options.metrics ?? ['error-rate', 'latency', 'cost']);
|
|
@@ -90,9 +101,7 @@ export function compareRuns(baseline, current, options = {}) {
|
|
|
90
101
|
}
|
|
91
102
|
}
|
|
92
103
|
for (const feedbackKey of options.feedback ?? []) {
|
|
93
|
-
const scores = (runs) => runs
|
|
94
|
-
.flatMap((run) => (run.feedback ?? []).filter((item) => item.key === feedbackKey).map((item) => item.score))
|
|
95
|
-
.filter(isNumber);
|
|
104
|
+
const scores = (runs) => runScores(runs, feedbackKey);
|
|
96
105
|
const before = scores(baseline);
|
|
97
106
|
const after = scores(current);
|
|
98
107
|
if (before.length < minRuns || after.length < minRuns)
|
|
@@ -114,6 +123,123 @@ export function compareRuns(baseline, current, options = {}) {
|
|
|
114
123
|
}
|
|
115
124
|
return regressions;
|
|
116
125
|
}
|
|
126
|
+
function judgedRegressions(baseline, current, options, confidence) {
|
|
127
|
+
if (!(confidence > 0 && confidence < 1))
|
|
128
|
+
throw new RangeError('confidence is between 0 and 1, such as 0.95');
|
|
129
|
+
const name = options.group ?? 'runs';
|
|
130
|
+
const minRuns = options.minRuns ?? 20;
|
|
131
|
+
const metrics = new Set(options.metrics ?? ['error-rate', 'latency', 'cost']);
|
|
132
|
+
const judge = (before, after, shape) => before.length >= minRuns && after.length >= minRuns
|
|
133
|
+
? metricVerdict(before, after, {
|
|
134
|
+
...shape,
|
|
135
|
+
confidence,
|
|
136
|
+
resamples: options.resamples ?? 1_000,
|
|
137
|
+
seed: options.seed ?? 1,
|
|
138
|
+
})
|
|
139
|
+
: undefined;
|
|
140
|
+
const regressions = [];
|
|
141
|
+
const add = (metric, verdict, counts, line) => {
|
|
142
|
+
if (!verdict?.regressed)
|
|
143
|
+
return;
|
|
144
|
+
regressions.push({
|
|
145
|
+
group: name,
|
|
146
|
+
metric,
|
|
147
|
+
baseline: verdict.baseline,
|
|
148
|
+
current: verdict.current,
|
|
149
|
+
change: verdict.change,
|
|
150
|
+
samples: counts,
|
|
151
|
+
interval: verdict.interval,
|
|
152
|
+
summary: `${name}: ${line} (${Math.round(confidence * 100)}% interval ${formatInterval(verdict.interval, metric)})`,
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const counts = { baseline: baseline.length, current: current.length };
|
|
156
|
+
if (metrics.has('error-rate')) {
|
|
157
|
+
const errors = (runs) => runs.map((run) => (run.status === 'error' ? 1 : 0));
|
|
158
|
+
const verdict = judge(errors(baseline), errors(current), {
|
|
159
|
+
statistic: meanOf,
|
|
160
|
+
relative: false,
|
|
161
|
+
worse: 'rise',
|
|
162
|
+
minEffect: options.minErrorRateIncrease ?? 0.02,
|
|
163
|
+
});
|
|
164
|
+
add('error-rate', verdict, counts, `error rate rose from ${percent(verdict?.baseline ?? 0)} to ${percent(verdict?.current ?? 0)}`);
|
|
165
|
+
}
|
|
166
|
+
if (metrics.has('latency')) {
|
|
167
|
+
const latencies = (runs) => runs.map((run) => run.latencyMs).filter(isNumber);
|
|
168
|
+
const before = latencies(baseline);
|
|
169
|
+
const after = latencies(current);
|
|
170
|
+
const verdict = judge(before, after, {
|
|
171
|
+
statistic: (values) => percentile(values, 0.95) ?? 0,
|
|
172
|
+
relative: true,
|
|
173
|
+
worse: 'rise',
|
|
174
|
+
minEffect: options.latencyIncrease ?? 0.25,
|
|
175
|
+
});
|
|
176
|
+
const large = verdict && verdict.current - verdict.baseline >= (options.minLatencyChangeMs ?? 0);
|
|
177
|
+
add('latency-p95', large ? verdict : undefined, { baseline: before.length, current: after.length }, `p95 latency rose from ${Math.round(verdict?.baseline ?? 0)}ms to ${Math.round(verdict?.current ?? 0)}ms`);
|
|
178
|
+
}
|
|
179
|
+
if (metrics.has('cost')) {
|
|
180
|
+
const costs = (runs) => runs.map((run) => run.cost).filter(isNumber);
|
|
181
|
+
const before = costs(baseline);
|
|
182
|
+
const after = costs(current);
|
|
183
|
+
const verdict = judge(before, after, {
|
|
184
|
+
statistic: meanOf,
|
|
185
|
+
relative: true,
|
|
186
|
+
worse: 'rise',
|
|
187
|
+
minEffect: options.costIncrease ?? 0.25,
|
|
188
|
+
});
|
|
189
|
+
add('cost', verdict, { baseline: before.length, current: after.length }, `mean cost rose from $${(verdict?.baseline ?? 0).toFixed(4)} to $${(verdict?.current ?? 0).toFixed(4)}`);
|
|
190
|
+
}
|
|
191
|
+
for (const feedbackKey of options.feedback ?? []) {
|
|
192
|
+
const scores = (runs) => runScores(runs, feedbackKey);
|
|
193
|
+
const before = scores(baseline);
|
|
194
|
+
const after = scores(current);
|
|
195
|
+
const verdict = judge(before, after, {
|
|
196
|
+
statistic: meanOf,
|
|
197
|
+
relative: false,
|
|
198
|
+
worse: 'fall',
|
|
199
|
+
minEffect: options.minFeedbackDrop ?? 0.05,
|
|
200
|
+
});
|
|
201
|
+
add(`feedback:${feedbackKey}`, verdict, { baseline: before.length, current: after.length }, `${feedbackKey} fell from ${(verdict?.baseline ?? 0).toFixed(2)} to ${(verdict?.current ?? 0).toFixed(2)}`);
|
|
202
|
+
}
|
|
203
|
+
return regressions;
|
|
204
|
+
}
|
|
205
|
+
function metricVerdict(baseline, current, options) {
|
|
206
|
+
const change = (before, after) => options.relative ? (before === 0 ? 0 : after / before - 1) : after - before;
|
|
207
|
+
const before = options.statistic(baseline);
|
|
208
|
+
const after = options.statistic(current);
|
|
209
|
+
const random = seeded(options.seed);
|
|
210
|
+
const resample = (values) => Array.from({ length: values.length }, () => values[Math.floor(random() * values.length)]);
|
|
211
|
+
const draws = [];
|
|
212
|
+
for (let index = 0; index < options.resamples; index += 1) {
|
|
213
|
+
draws.push(change(options.statistic(resample(baseline)), options.statistic(resample(current))));
|
|
214
|
+
}
|
|
215
|
+
draws.sort((a, b) => a - b);
|
|
216
|
+
const tail = (1 - options.confidence) / 2;
|
|
217
|
+
const at = (quantile) => draws[Math.min(draws.length - 1, Math.max(0, Math.floor(quantile * draws.length)))];
|
|
218
|
+
const interval = [at(tail), at(1 - tail)];
|
|
219
|
+
const observed = change(before, after);
|
|
220
|
+
const regressed = options.worse === 'rise'
|
|
221
|
+
? observed >= options.minEffect && interval[0] > 0
|
|
222
|
+
: -observed >= options.minEffect && interval[1] < 0;
|
|
223
|
+
return { baseline: before, current: after, change: observed, interval, regressed };
|
|
224
|
+
}
|
|
225
|
+
function seeded(seed) {
|
|
226
|
+
let state = seed >>> 0;
|
|
227
|
+
return () => {
|
|
228
|
+
state = (state + 0x6d2b79f5) >>> 0;
|
|
229
|
+
let value = state;
|
|
230
|
+
value = Math.imul(value ^ (value >>> 15), value | 1);
|
|
231
|
+
value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
|
|
232
|
+
return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function meanOf(values) {
|
|
236
|
+
return mean(values) ?? 0;
|
|
237
|
+
}
|
|
238
|
+
function formatInterval(interval, metric) {
|
|
239
|
+
const relative = metric === 'latency-p95' || metric === 'cost';
|
|
240
|
+
const show = (value) => (relative ? `${value >= 0 ? '+' : ''}${Math.round(value * 100)}%` : value.toFixed(3));
|
|
241
|
+
return `${show(interval[0])} to ${show(interval[1])}`;
|
|
242
|
+
}
|
|
117
243
|
function isNumber(value) {
|
|
118
244
|
return typeof value === 'number' && Number.isFinite(value);
|
|
119
245
|
}
|
package/dist/loaders/git.js
CHANGED
|
@@ -32,6 +32,8 @@ export async function* loadGitRepository(repository, options = {}) {
|
|
|
32
32
|
const files = (await git(['ls-files', '-z'], directory, options)).split('\0').filter(Boolean).sort();
|
|
33
33
|
const extensions = options.extensions && new Set(options.extensions.map((extension) => extension.toLowerCase()));
|
|
34
34
|
const maxFileBytes = options.maxFileBytes ?? 1_000_000;
|
|
35
|
+
const { lstat, realpath } = await import('node:fs/promises');
|
|
36
|
+
const checkout = await realpath(directory);
|
|
35
37
|
for (const file of files) {
|
|
36
38
|
const extension = path.extname(file).toLowerCase();
|
|
37
39
|
if (extensions && !extensions.has(extension))
|
|
@@ -40,7 +42,12 @@ export async function* loadGitRepository(repository, options = {}) {
|
|
|
40
42
|
continue;
|
|
41
43
|
let bytes;
|
|
42
44
|
try {
|
|
43
|
-
|
|
45
|
+
const full = path.join(directory, file);
|
|
46
|
+
if ((await lstat(full)).isSymbolicLink())
|
|
47
|
+
continue;
|
|
48
|
+
if (!(await realpath(full)).startsWith(checkout + path.sep))
|
|
49
|
+
continue;
|
|
50
|
+
bytes = new Uint8Array(await readFile(full));
|
|
44
51
|
}
|
|
45
52
|
catch {
|
|
46
53
|
continue;
|
package/dist/loaders/web.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseUrl, safeFetch } from '../utils/safe-fetch.js';
|
|
2
2
|
import { htmlToText } from './html.js';
|
|
3
|
+
const MAX_SITEMAP_BYTES = 50 * 1024 * 1024;
|
|
3
4
|
export class WebLoaderError extends Error {
|
|
4
5
|
url;
|
|
5
6
|
status;
|
|
@@ -49,7 +50,12 @@ export async function sitemapUrls(url, options = {}) {
|
|
|
49
50
|
let xml;
|
|
50
51
|
if (bytes[0] === 0x1f && bytes[1] === 0x8b) {
|
|
51
52
|
const { gunzipSync } = await import('node:zlib');
|
|
52
|
-
|
|
53
|
+
try {
|
|
54
|
+
xml = gunzipSync(bytes, { maxOutputLength: MAX_SITEMAP_BYTES }).toString('utf8');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new WebLoaderError(`${sitemap} does not decompress to a sitemap of at most 50 MB: ${error instanceof Error ? error.message : String(error)}`, sitemap);
|
|
58
|
+
}
|
|
53
59
|
}
|
|
54
60
|
else
|
|
55
61
|
xml = new TextDecoder().decode(bytes);
|
package/dist/mcp/client.d.ts
CHANGED
|
@@ -122,7 +122,21 @@ export interface StdioClientOptions {
|
|
|
122
122
|
env?: Record<string, string>;
|
|
123
123
|
/** Working directory for the process. */
|
|
124
124
|
cwd?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Which of this process's environment variables the server inherits, besides `env`. By default,
|
|
127
|
+
* only what a process needs to start: the path, the home and temporary directories, the user, the
|
|
128
|
+
* locale and terminal, the Windows system variables, proxies, and certificate settings. A server is
|
|
129
|
+
* often third-party code, and must not see this process's API keys or database URLs. A list adds
|
|
130
|
+
* the variables it names, `true` passes the whole environment, as releases before 2.4 did, and
|
|
131
|
+
* `false` passes nothing but `env`.
|
|
132
|
+
*/
|
|
133
|
+
inheritEnv?: boolean | readonly string[];
|
|
125
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The environment a server process starts with. Names compare without case, as Windows compares
|
|
137
|
+
* them, and `LC_` locale settings are kept with the rest of the locale.
|
|
138
|
+
*/
|
|
139
|
+
export declare function serverEnvironment(source: Record<string, string | undefined>, options?: Pick<StdioClientOptions, 'env' | 'inheritEnv'>): Record<string, string>;
|
|
126
140
|
/**
|
|
127
141
|
* Runs an MCP server as a child process and speaks to it over its stdin and stdout.
|
|
128
142
|
*
|
package/dist/mcp/client.js
CHANGED
|
@@ -131,6 +131,64 @@ export class McpClient {
|
|
|
131
131
|
this.settle(id, undefined, error);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
+
const INHERITED_ENV = new Set([
|
|
135
|
+
'PATH',
|
|
136
|
+
'PATHEXT',
|
|
137
|
+
'HOME',
|
|
138
|
+
'USER',
|
|
139
|
+
'USERNAME',
|
|
140
|
+
'LOGNAME',
|
|
141
|
+
'SHELL',
|
|
142
|
+
'TERM',
|
|
143
|
+
'LANG',
|
|
144
|
+
'LANGUAGE',
|
|
145
|
+
'TZ',
|
|
146
|
+
'TMPDIR',
|
|
147
|
+
'TEMP',
|
|
148
|
+
'TMP',
|
|
149
|
+
'XDG_CONFIG_HOME',
|
|
150
|
+
'XDG_CACHE_HOME',
|
|
151
|
+
'XDG_DATA_HOME',
|
|
152
|
+
'XDG_RUNTIME_DIR',
|
|
153
|
+
'SYSTEMROOT',
|
|
154
|
+
'SYSTEMDRIVE',
|
|
155
|
+
'WINDIR',
|
|
156
|
+
'COMSPEC',
|
|
157
|
+
'APPDATA',
|
|
158
|
+
'LOCALAPPDATA',
|
|
159
|
+
'USERPROFILE',
|
|
160
|
+
'HOMEDRIVE',
|
|
161
|
+
'HOMEPATH',
|
|
162
|
+
'PROGRAMDATA',
|
|
163
|
+
'PROGRAMFILES',
|
|
164
|
+
'PROGRAMFILES(X86)',
|
|
165
|
+
'COMMONPROGRAMFILES',
|
|
166
|
+
'PROCESSOR_ARCHITECTURE',
|
|
167
|
+
'NUMBER_OF_PROCESSORS',
|
|
168
|
+
'OS',
|
|
169
|
+
'HTTP_PROXY',
|
|
170
|
+
'HTTPS_PROXY',
|
|
171
|
+
'NO_PROXY',
|
|
172
|
+
'ALL_PROXY',
|
|
173
|
+
'NODE_EXTRA_CA_CERTS',
|
|
174
|
+
'SSL_CERT_FILE',
|
|
175
|
+
'SSL_CERT_DIR',
|
|
176
|
+
]);
|
|
177
|
+
export function serverEnvironment(source, options = {}) {
|
|
178
|
+
if (options.inheritEnv === false)
|
|
179
|
+
return { ...options.env };
|
|
180
|
+
const named = new Set((Array.isArray(options.inheritEnv) ? options.inheritEnv : []).map((name) => name.toUpperCase()));
|
|
181
|
+
const inherited = {};
|
|
182
|
+
for (const [name, value] of Object.entries(source)) {
|
|
183
|
+
if (value === undefined)
|
|
184
|
+
continue;
|
|
185
|
+
const upper = name.toUpperCase();
|
|
186
|
+
if (options.inheritEnv === true || INHERITED_ENV.has(upper) || upper.startsWith('LC_') || named.has(upper)) {
|
|
187
|
+
inherited[name] = value;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return { ...inherited, ...options.env };
|
|
191
|
+
}
|
|
134
192
|
export function createStdioTransport(options) {
|
|
135
193
|
let child;
|
|
136
194
|
const decoder = new LineDecoder();
|
|
@@ -141,7 +199,7 @@ export function createStdioTransport(options) {
|
|
|
141
199
|
const { spawn } = await import('node:child_process');
|
|
142
200
|
const spawned = spawn(options.command, options.args ?? [], {
|
|
143
201
|
cwd: options.cwd,
|
|
144
|
-
env:
|
|
202
|
+
env: serverEnvironment(process.env, options),
|
|
145
203
|
stdio: ['pipe', 'pipe', 'inherit'],
|
|
146
204
|
});
|
|
147
205
|
spawned.stdout.setEncoding('utf8');
|
package/dist/mcp/registry.d.ts
CHANGED
|
@@ -15,6 +15,12 @@ export interface McpServerConfig {
|
|
|
15
15
|
env?: Record<string, string>;
|
|
16
16
|
/** Working directory for the process. */
|
|
17
17
|
cwd?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Which of this process's own environment variables a local server inherits, besides `env`. By
|
|
20
|
+
* default, only what a process needs to start, never an API key; a list adds variables by name,
|
|
21
|
+
* `true` passes the whole environment, and `false` nothing but `env`.
|
|
22
|
+
*/
|
|
23
|
+
inheritEnv?: boolean | string[];
|
|
18
24
|
/** The endpoint of a remote server, reached over HTTP. */
|
|
19
25
|
url?: string;
|
|
20
26
|
/** Headers sent to a remote server, such as authorization. */
|
package/dist/mcp/registry.js
CHANGED
|
@@ -59,6 +59,7 @@ export class McpRegistry {
|
|
|
59
59
|
...(resolved.args ? { args: resolved.args } : {}),
|
|
60
60
|
...(resolved.env ? { env: resolved.env } : {}),
|
|
61
61
|
...(resolved.cwd ? { cwd: resolved.cwd } : {}),
|
|
62
|
+
...(resolved.inheritEnv !== undefined ? { inheritEnv: resolved.inheritEnv } : {}),
|
|
62
63
|
})
|
|
63
64
|
: createHttpTransport({
|
|
64
65
|
url: resolved.url,
|
|
@@ -163,6 +164,12 @@ export function validateMcpConfig(value) {
|
|
|
163
164
|
throw new McpRegistryError(`Server "${name}": "${field}" is a list of strings`, name);
|
|
164
165
|
}
|
|
165
166
|
}
|
|
167
|
+
const inherit = server.inheritEnv;
|
|
168
|
+
if (inherit !== undefined &&
|
|
169
|
+
typeof inherit !== 'boolean' &&
|
|
170
|
+
!(Array.isArray(inherit) && inherit.every((item) => typeof item === 'string'))) {
|
|
171
|
+
throw new McpRegistryError(`Server "${name}": "inheritEnv" is true, false, or a list of names`, name);
|
|
172
|
+
}
|
|
166
173
|
for (const field of ['env', 'headers']) {
|
|
167
174
|
const map = server[field];
|
|
168
175
|
if (map !== undefined &&
|
package/dist/prompts/file.js
CHANGED
|
@@ -38,20 +38,24 @@ export class FilePromptStore {
|
|
|
38
38
|
}
|
|
39
39
|
async setLabel(label, expected) {
|
|
40
40
|
const file = this.labelFile(label.name, label.label);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
return serialized(file, async () => {
|
|
42
|
+
if (expected !== undefined) {
|
|
43
|
+
const current = await readJson(file);
|
|
44
|
+
if (expected === null ? current !== undefined : current?.version !== expected)
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
await writeJson(file, label);
|
|
48
|
+
return true;
|
|
49
|
+
});
|
|
48
50
|
}
|
|
49
51
|
async deleteLabel(name, label) {
|
|
50
52
|
const file = this.labelFile(name, label);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
return serialized(file, async () => {
|
|
54
|
+
if ((await readJson(file)) === undefined)
|
|
55
|
+
return false;
|
|
56
|
+
await rm(file, { force: true });
|
|
57
|
+
return true;
|
|
58
|
+
});
|
|
55
59
|
}
|
|
56
60
|
async appendHistory(entry) {
|
|
57
61
|
await mkdir(this.promptDir(entry.name), { recursive: true });
|
|
@@ -109,6 +113,19 @@ async function readJson(file) {
|
|
|
109
113
|
return undefined;
|
|
110
114
|
}
|
|
111
115
|
}
|
|
116
|
+
const labelWrites = new Map();
|
|
117
|
+
async function serialized(file, work) {
|
|
118
|
+
const key = path.resolve(file);
|
|
119
|
+
const next = (labelWrites.get(key) ?? Promise.resolve()).catch(() => undefined).then(work);
|
|
120
|
+
labelWrites.set(key, next);
|
|
121
|
+
try {
|
|
122
|
+
return await next;
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
if (labelWrites.get(key) === next)
|
|
126
|
+
labelWrites.delete(key);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
112
129
|
async function writeJson(file, value) {
|
|
113
130
|
await mkdir(path.dirname(file), { recursive: true });
|
|
114
131
|
const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
|