nexus-ai-pro 1.18.0 → 1.19.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 +13 -0
- package/CHANGELOG.md +69 -1
- package/README.md +1 -0
- package/dist/graph/checkpointer.d.ts +5 -0
- package/dist/graph/checkpointer.js +9 -0
- package/dist/mcp/client.d.ts +6 -1
- package/dist/mcp/client.js +7 -0
- package/dist/mcp/server.js +4 -4
- package/dist/security/index.js +3 -3
- package/dist/security/input-guard.js +6 -1
- package/dist/security/pii-detector.d.ts +1 -1
- package/dist/security/pii-detector.js +9 -3
- package/dist/security/prompt-hardening.d.ts +3 -0
- package/dist/security/semantic-injection-classifier.d.ts +6 -0
- package/dist/security/semantic-injection-classifier.js +3 -1
- package/dist/tracing/instrument.js +2 -0
- package/dist/tracing/tracer.d.ts +4 -0
- package/dist/tracing/tracer.js +2 -0
- package/dist/types/graph.d.ts +2 -0
- package/dist/types/security.d.ts +74 -5
- package/dist/workflow/chains.d.ts +3 -3
- package/dist/workflow/chains.js +18 -7
- package/dist-cjs/graph/checkpointer.d.ts +5 -0
- package/dist-cjs/graph/checkpointer.js +9 -0
- package/dist-cjs/mcp/client.d.ts +6 -1
- package/dist-cjs/mcp/client.js +7 -0
- package/dist-cjs/mcp/server.js +3 -3
- package/dist-cjs/security/index.js +3 -3
- package/dist-cjs/security/input-guard.js +6 -1
- package/dist-cjs/security/pii-detector.d.ts +1 -1
- package/dist-cjs/security/pii-detector.js +9 -3
- package/dist-cjs/security/prompt-hardening.d.ts +3 -0
- package/dist-cjs/security/semantic-injection-classifier.d.ts +6 -0
- package/dist-cjs/security/semantic-injection-classifier.js +3 -1
- package/dist-cjs/tracing/instrument.js +2 -0
- package/dist-cjs/tracing/tracer.d.ts +4 -0
- package/dist-cjs/tracing/tracer.js +2 -0
- package/dist-cjs/types/graph.d.ts +2 -0
- package/dist-cjs/types/security.d.ts +74 -5
- package/dist-cjs/workflow/chains.d.ts +3 -3
- package/dist-cjs/workflow/chains.js +18 -7
- package/package.json +10 -5
package/API_STABILITY.md
CHANGED
|
@@ -442,6 +442,19 @@ from the root.
|
|
|
442
442
|
- Recovery re-runs a whole run rather than the step it died on, so an assistant with side effects
|
|
443
443
|
should be idempotent or be left at a single attempt.
|
|
444
444
|
|
|
445
|
+
## Studio stage (1.19.0)
|
|
446
|
+
|
|
447
|
+
`nexus-ai-pro-studio` is a separate package, experimental in production readiness. It is versioned
|
|
448
|
+
with nexus-ai-pro and declares the core versions it works with as a peer dependency.
|
|
449
|
+
|
|
450
|
+
- The `nexus-studio` command, its flags, and the shape of the config module's default export follow
|
|
451
|
+
the 1.x rules. A minor release may add sources and flags.
|
|
452
|
+
- `createStudio()`, `startStudio()`, and the exported types follow the 1.x rules.
|
|
453
|
+
- The JSON routes behind the page are for the page and may change in any release; build on the
|
|
454
|
+
stores themselves rather than on the studio's routes.
|
|
455
|
+
- The access rules are a guarantee: loopback binding by default, a token on every request, the token
|
|
456
|
+
in a header for every change, and loopback hosts only unless others are allowed.
|
|
457
|
+
|
|
445
458
|
## Deprecation process
|
|
446
459
|
|
|
447
460
|
Deprecated APIs are marked with `@deprecated` in declarations and described in the changelog. Removals
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,73 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.19.0] - 2026-09-28
|
|
8
|
+
|
|
9
|
+
A studio to look at it all. `nexus-ai-pro-studio` is a separate package — so the core install never
|
|
10
|
+
carries a UI — that shows what an application records, from its own stores, on the developer's
|
|
11
|
+
machine, with no hosted service.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`nexus-ai-pro-studio`**, experimental, with the `nexus-studio` command. Given a config module
|
|
16
|
+
whose default export lists the application's stores, it serves eight views:
|
|
17
|
+
- **Traces**: filtered run lists, run trees with inputs, outputs, cost, and latency, trace
|
|
18
|
+
comparison, and feedback on a run.
|
|
19
|
+
- **Threads**: each graph's diagram with the next nodes highlighted, state at any step, and fork,
|
|
20
|
+
edit, and resume.
|
|
21
|
+
- **Inbox**: every thread waiting for a human and every review item waiting for a grade.
|
|
22
|
+
- **Experiments**: experiment and dataset lists, and comparisons with per-metric intervals and a
|
|
23
|
+
verdict.
|
|
24
|
+
- **Prompts**: versions, labels, history, diffs, gated promotion, rollback, and a playground.
|
|
25
|
+
- **Costs**: cost per day and per model, the most expensive runs, and budgets.
|
|
26
|
+
- **Health**: provider health, local and shared circuit state, metrics, and cache statistics.
|
|
27
|
+
- **Operations**: the operation queue by status, and asset store totals.
|
|
28
|
+
- **Locked down for a local tool.** It binds `127.0.0.1`, requires a token that moves from the URL
|
|
29
|
+
into an HTTP-only same-site cookie, requires the token in a header for every change, refuses
|
|
30
|
+
non-loopback `Host` headers against DNS rebinding, and serves every page under a strict content
|
|
31
|
+
security policy with every value inserted as text.
|
|
32
|
+
- `startStudio()` and `createStudio()` start it from code or mount it in an existing server.
|
|
33
|
+
- `npm run studio:demo` starts it on a demo application with something in every view.
|
|
34
|
+
- The operation-store checkpointer — the one behind Redis and Postgres — lists its threads through
|
|
35
|
+
`threadIds()`, as the in-memory one already did, and `GraphCheckpointer` declares it as optional.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **Secret detection on input now detects under every action.** It only scanned when the action
|
|
40
|
+
was `mask`. Under the default action, `block`, and under `flag`, a request carrying a private
|
|
41
|
+
key, a cloud or payment key, a token, or a secret assignment went out with no finding. Such a
|
|
42
|
+
request is now blocked by default, at every level but `off`. Set `input.secrets.action` to `flag`
|
|
43
|
+
to record secrets without blocking.
|
|
44
|
+
- The PII action `remove` deletes the matched data. It was accepted but behaved like `flag`, so the
|
|
45
|
+
data stayed in the request.
|
|
46
|
+
- The semantic injection check no longer re-embeds its example attacks on every request.
|
|
47
|
+
- `McpServer` answers an unknown method with the JSON-RPC method-not-found code, and an unknown
|
|
48
|
+
resource with invalid-params, instead of an internal error, so a client can tell an unsupported
|
|
49
|
+
feature from a failure.
|
|
50
|
+
- `McpClient` requests made together before the handshake finished each sent `initialize`; they now
|
|
51
|
+
share one handshake.
|
|
52
|
+
- `summarizeVerifyFormat()`'s verify step sent the summary to the model with no instruction, so the
|
|
53
|
+
model answered the summary instead of restating it, and that reply was what got checked against
|
|
54
|
+
the sources. It now asks for a restatement against the sources. The final format is applied only in
|
|
55
|
+
the last step, and a verify step that was skipped is no longer recorded in `steps`.
|
|
56
|
+
- `traceModelClient()` records the model and provider that answered on the run itself. It recorded
|
|
57
|
+
the model the request asked for — often `auto` — and left `provider` empty, so a `provider` query
|
|
58
|
+
never matched a model run and per-model views showed `auto`. `FinishRunOptions` gains `model` and
|
|
59
|
+
`provider` for the same purpose in your own instrumentation.
|
|
60
|
+
|
|
61
|
+
### Deprecated
|
|
62
|
+
|
|
63
|
+
- `InjectionDetectionConfig.sensitivity` and `ToolPolicyConfig.requiresApproval` have never been
|
|
64
|
+
read. Both will be removed in 2.0; approval before a tool runs belongs to the agent loop.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Nineteen guides under `docs/` — agents, batch, caching, core, graphs, grounding, MCP, memory,
|
|
69
|
+
Postgres, prompts, providers, resilience, security, server, telephony, testing, tracing, voice, and
|
|
70
|
+
workflows — now explain each export they list in their text, not only in the generated reference.
|
|
71
|
+
The security configuration's nested settings have doc comments.
|
|
72
|
+
- The documentation coverage check also covers the studio package's exports.
|
|
73
|
+
|
|
7
74
|
## [1.18.0] - 2026-09-25
|
|
8
75
|
|
|
9
76
|
A server you can host. Assistants get an HTTP surface — threads, background runs, resumable event
|
|
@@ -941,7 +1008,8 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
941
1008
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
942
1009
|
- CLI and audit findings no longer disclose detected secret values.
|
|
943
1010
|
|
|
944
|
-
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.
|
|
1011
|
+
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.19.0...HEAD
|
|
1012
|
+
[1.19.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.18.0...v1.19.0
|
|
945
1013
|
[1.18.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.17.0...v1.18.0
|
|
946
1014
|
[1.17.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.16.0...v1.17.0
|
|
947
1015
|
[1.16.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.15.0...v1.16.0
|
package/README.md
CHANGED
|
@@ -125,6 +125,7 @@ from the doc comments. The guides live in the repository, so these links go to G
|
|
|
125
125
|
| [Realtime voice](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/realtime.md) | Browser and server realtime sessions with barge-in, tools, and exports |
|
|
126
126
|
| [Telephony](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/telephony.md) | Calls, webhooks, phone numbers, and phone agents on realtime sessions |
|
|
127
127
|
| [Testing](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/testing.md) | Recording and replaying provider traffic, and conformance suites |
|
|
128
|
+
| [Studio](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/studio.md) | A local UI for traces, threads, approvals, experiments, prompts, costs, and health, as a separate package |
|
|
128
129
|
| [Agent server](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/server.md) | Self-hosted HTTP server for assistants: threads, durable runs, resumable streams, cron |
|
|
129
130
|
| [Workflows](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/workflows.md) | Ready-made chains and domain workflows |
|
|
130
131
|
| [Command line](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/cli.md) | The `nexus` command |
|
|
@@ -66,6 +66,11 @@ export declare class OperationStoreCheckpointer implements GraphCheckpointer {
|
|
|
66
66
|
history(threadId: string, limit?: number): Promise<GraphCheckpoint[]>;
|
|
67
67
|
/** Deletes a thread's checkpoints and its head. */
|
|
68
68
|
delete(threadId: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Every thread with a checkpoint, read from the head records. Empty when the store cannot list,
|
|
71
|
+
* since finding heads any other way would mean scanning every key.
|
|
72
|
+
*/
|
|
73
|
+
threadIds(): Promise<string[]>;
|
|
69
74
|
private readHead;
|
|
70
75
|
/**
|
|
71
76
|
* Writes one record, creating it when absent and otherwise updating in place.
|
|
@@ -86,6 +86,15 @@ export class OperationStoreCheckpointer {
|
|
|
86
86
|
await this.store.delete?.(stepId(threadId, step));
|
|
87
87
|
await this.store.delete?.(threadId);
|
|
88
88
|
}
|
|
89
|
+
async threadIds() {
|
|
90
|
+
if (!this.store.list)
|
|
91
|
+
return [];
|
|
92
|
+
const records = await this.store.list();
|
|
93
|
+
return records
|
|
94
|
+
.filter((record) => record.result !== undefined && isHead(record.result))
|
|
95
|
+
.map((record) => record.id)
|
|
96
|
+
.sort();
|
|
97
|
+
}
|
|
89
98
|
async readHead(threadId) {
|
|
90
99
|
const record = await this.store.read(threadId);
|
|
91
100
|
return record?.result && isHead(record.result) ? record.result : undefined;
|
package/dist/mcp/client.d.ts
CHANGED
|
@@ -62,13 +62,18 @@ export declare class McpClient {
|
|
|
62
62
|
private readonly pending;
|
|
63
63
|
private nextId;
|
|
64
64
|
private initialized;
|
|
65
|
+
private connecting;
|
|
65
66
|
private serverInfo;
|
|
66
67
|
constructor(transport: McpTransport, options?: McpClientOptions);
|
|
67
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Performs the handshake and returns the server's name and version. Called automatically by the
|
|
70
|
+
* first request that needs it; requests made together share one handshake.
|
|
71
|
+
*/
|
|
68
72
|
connect(): Promise<{
|
|
69
73
|
name?: string;
|
|
70
74
|
version?: string;
|
|
71
75
|
}>;
|
|
76
|
+
private handshake;
|
|
72
77
|
/** Lists the server's tools. */
|
|
73
78
|
listTools(): Promise<McpToolDescriptor[]>;
|
|
74
79
|
/**
|
package/dist/mcp/client.js
CHANGED
|
@@ -5,6 +5,7 @@ export class McpClient {
|
|
|
5
5
|
pending = new Map();
|
|
6
6
|
nextId = 1;
|
|
7
7
|
initialized = false;
|
|
8
|
+
connecting;
|
|
8
9
|
serverInfo = {};
|
|
9
10
|
constructor(transport, options = {}) {
|
|
10
11
|
this.transport = transport;
|
|
@@ -15,6 +16,12 @@ export class McpClient {
|
|
|
15
16
|
async connect() {
|
|
16
17
|
if (this.initialized)
|
|
17
18
|
return this.serverInfo;
|
|
19
|
+
this.connecting ??= this.handshake().finally(() => {
|
|
20
|
+
this.connecting = undefined;
|
|
21
|
+
});
|
|
22
|
+
return this.connecting;
|
|
23
|
+
}
|
|
24
|
+
async handshake() {
|
|
18
25
|
await this.transport.start?.();
|
|
19
26
|
const result = (await this.request('initialize', {
|
|
20
27
|
protocolVersion: MCP_PROTOCOL_VERSION,
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isNotification, isRequest, JSON_RPC_ERRORS, LineDecoder, MCP_PROTOCOL_VERSION, textContent, } from './protocol.js';
|
|
1
|
+
import { isNotification, isRequest, JSON_RPC_ERRORS, LineDecoder, MCP_PROTOCOL_VERSION, McpError, textContent, } from './protocol.js';
|
|
2
2
|
export class McpServer {
|
|
3
3
|
options;
|
|
4
4
|
tools = new Map();
|
|
@@ -35,7 +35,7 @@ export class McpServer {
|
|
|
35
35
|
jsonrpc: '2.0',
|
|
36
36
|
id: message.id,
|
|
37
37
|
error: {
|
|
38
|
-
code: JSON_RPC_ERRORS.internal,
|
|
38
|
+
code: error instanceof McpError ? error.code : JSON_RPC_ERRORS.internal,
|
|
39
39
|
message: error instanceof Error ? error.message : String(error),
|
|
40
40
|
},
|
|
41
41
|
});
|
|
@@ -89,7 +89,7 @@ export class McpServer {
|
|
|
89
89
|
const uri = String(params.uri ?? '');
|
|
90
90
|
const resource = this.options.resources?.find((item) => item.uri === uri);
|
|
91
91
|
if (!resource)
|
|
92
|
-
throw new
|
|
92
|
+
throw new McpError(`Unknown resource "${uri}"`, JSON_RPC_ERRORS.invalidParams);
|
|
93
93
|
const value = await resource.read();
|
|
94
94
|
return {
|
|
95
95
|
contents: [
|
|
@@ -102,7 +102,7 @@ export class McpServer {
|
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
default:
|
|
105
|
-
throw new
|
|
105
|
+
throw new McpError(`Unknown method "${request.method}"`, JSON_RPC_ERRORS.methodNotFound);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
}
|
package/dist/security/index.js
CHANGED
|
@@ -63,9 +63,9 @@ export class SecurityPipeline {
|
|
|
63
63
|
findings.push(...piiFindings);
|
|
64
64
|
guardrailsApplied.push('pii-detection');
|
|
65
65
|
const action = piiConfig.action || (level === 'strict' || level === 'paranoid' ? 'mask' : 'flag');
|
|
66
|
-
if (piiFindings.length > 0 && action === 'mask') {
|
|
67
|
-
safeRequest = this.piiDetector.mask(safeRequest, piiConfig);
|
|
68
|
-
guardrailsApplied.push('pii-masking');
|
|
66
|
+
if (piiFindings.length > 0 && (action === 'mask' || action === 'remove')) {
|
|
67
|
+
safeRequest = this.piiDetector.mask(safeRequest, { ...piiConfig, action });
|
|
68
|
+
guardrailsApplied.push(action === 'remove' ? 'pii-removal' : 'pii-masking');
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
const blockingFindings = this.getBlockingFindings(findings, level, normalized);
|
|
@@ -103,8 +103,13 @@ export class InputGuard {
|
|
|
103
103
|
});
|
|
104
104
|
return output;
|
|
105
105
|
};
|
|
106
|
-
if (action !== 'mask')
|
|
106
|
+
if (action !== 'mask') {
|
|
107
|
+
request.messages.forEach((message, index) => {
|
|
108
|
+
for (const text of this.extractText(message))
|
|
109
|
+
maskText(text, `messages.${index}.content`);
|
|
110
|
+
});
|
|
107
111
|
return request;
|
|
112
|
+
}
|
|
108
113
|
return {
|
|
109
114
|
...request,
|
|
110
115
|
messages: request.messages.map((message, index) => ({
|
|
@@ -4,7 +4,7 @@ import type { PIIConfig, SecurityFinding } from '../types/security.js';
|
|
|
4
4
|
export declare class PIIDetector {
|
|
5
5
|
/** Finds personal data in a request's messages. */
|
|
6
6
|
detect(request: CompletionRequest, config?: PIIConfig): SecurityFinding[];
|
|
7
|
-
/** Returns the request with personal data masked
|
|
7
|
+
/** Returns the request with personal data masked, or removed when the action is `remove`. */
|
|
8
8
|
mask(request: CompletionRequest, config?: PIIConfig): CompletionRequest;
|
|
9
9
|
private maskText;
|
|
10
10
|
private extractText;
|
|
@@ -35,22 +35,28 @@ export class PIIDetector {
|
|
|
35
35
|
mask(request, config = {}) {
|
|
36
36
|
const enabledTypes = config.detect || ['email', 'phone', 'credit-card', 'ip-address', 'aws-key', 'private-key'];
|
|
37
37
|
const maskChar = config.maskChar || '█';
|
|
38
|
+
const remove = config.action === 'remove';
|
|
38
39
|
return {
|
|
39
40
|
...request,
|
|
40
41
|
messages: request.messages.map((message) => ({
|
|
41
42
|
...message,
|
|
42
43
|
content: typeof message.content === 'string'
|
|
43
|
-
? this.maskText(message.content, enabledTypes, maskChar, config.preserveFormat !== false)
|
|
44
|
+
? this.maskText(message.content, enabledTypes, maskChar, config.preserveFormat !== false, remove)
|
|
44
45
|
: message.content.map((part) => part.type === 'text'
|
|
45
|
-
? {
|
|
46
|
+
? {
|
|
47
|
+
...part,
|
|
48
|
+
text: this.maskText(part.text, enabledTypes, maskChar, config.preserveFormat !== false, remove),
|
|
49
|
+
}
|
|
46
50
|
: part),
|
|
47
51
|
})),
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
|
-
maskText(text, types, maskChar, preserveFormat) {
|
|
54
|
+
maskText(text, types, maskChar, preserveFormat, remove = false) {
|
|
51
55
|
let masked = text;
|
|
52
56
|
for (const type of types) {
|
|
53
57
|
masked = masked.replace(PII_PATTERNS[type], (value) => {
|
|
58
|
+
if (remove)
|
|
59
|
+
return '';
|
|
54
60
|
if (!preserveFormat)
|
|
55
61
|
return maskChar.repeat(Math.min(value.length, 12));
|
|
56
62
|
return value.replace(/[A-Za-z0-9]/g, maskChar);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { CompletionRequest } from '../types/messages.js';
|
|
2
|
+
/** Options for `hardenPrompt()`. */
|
|
2
3
|
export interface PromptHardeningOptions {
|
|
4
|
+
/** What user text is wrapped in. Defaults to `"""`. */
|
|
3
5
|
delimiter?: string;
|
|
6
|
+
/** The system message placed first, telling the model to treat delimited text as data. */
|
|
4
7
|
systemInstruction?: string;
|
|
5
8
|
}
|
|
6
9
|
/**
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { CompletionRequest } from '../types/messages.js';
|
|
2
2
|
import type { SecurityFinding } from '../types/security.js';
|
|
3
3
|
import { type EmbeddingProvider } from '../hallucination/retrieval.js';
|
|
4
|
+
/** Options for `SemanticInjectionClassifier`. */
|
|
4
5
|
export interface SemanticInjectionClassifierOptions {
|
|
6
|
+
/** `false` turns `detect()` off. */
|
|
5
7
|
enabled?: boolean;
|
|
8
|
+
/** Similarity at which a prompt is flagged, from 0 to 1. Defaults to 0.78; above 0.88 is critical. */
|
|
6
9
|
threshold?: number;
|
|
10
|
+
/** Embeds text for `detect()`. Defaults to hashed term vectors; `detectSync()` always uses those. */
|
|
7
11
|
embed?: EmbeddingProvider;
|
|
12
|
+
/** Example attacks to compare against, instead of the built-in ones. */
|
|
8
13
|
examples?: string[];
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
@@ -16,6 +21,7 @@ export declare class SemanticInjectionClassifier {
|
|
|
16
21
|
private examples;
|
|
17
22
|
private embed;
|
|
18
23
|
private exampleEmbeddings?;
|
|
24
|
+
private hashedExamples?;
|
|
19
25
|
constructor(options?: SemanticInjectionClassifierOptions);
|
|
20
26
|
/** Finds injection attempts, using the configured embedding function. */
|
|
21
27
|
detect(request: CompletionRequest): Promise<SecurityFinding[]>;
|
|
@@ -12,6 +12,7 @@ export class SemanticInjectionClassifier {
|
|
|
12
12
|
examples;
|
|
13
13
|
embed;
|
|
14
14
|
exampleEmbeddings;
|
|
15
|
+
hashedExamples;
|
|
15
16
|
constructor(options = {}) {
|
|
16
17
|
this.options = options;
|
|
17
18
|
this.examples = options.examples || DEFAULT_ATTACK_EXAMPLES;
|
|
@@ -43,7 +44,8 @@ export class SemanticInjectionClassifier {
|
|
|
43
44
|
detectSync(request) {
|
|
44
45
|
const findings = [];
|
|
45
46
|
const threshold = this.options.threshold ?? 0.78;
|
|
46
|
-
|
|
47
|
+
this.hashedExamples ??= createHashEmbeddings(this.examples);
|
|
48
|
+
const exampleEmbeddings = this.hashedExamples;
|
|
47
49
|
for (const [index, message] of request.messages.entries()) {
|
|
48
50
|
for (const text of extractText(message)) {
|
|
49
51
|
const [embedding] = createHashEmbeddings([text]);
|
|
@@ -105,6 +105,8 @@ export function traceModelClient(client, tracer, options = {}) {
|
|
|
105
105
|
}
|
|
106
106
|
: {}),
|
|
107
107
|
...(response.meta?.cost?.amount === undefined ? {} : { cost: response.meta.cost.amount }),
|
|
108
|
+
...(response.meta?.modelUsed ? { model: response.meta.modelUsed } : {}),
|
|
109
|
+
...(response.meta?.providerUsed ? { provider: response.meta.providerUsed } : {}),
|
|
108
110
|
metadata: {
|
|
109
111
|
provider: response.meta?.providerUsed,
|
|
110
112
|
model: response.meta?.modelUsed,
|
package/dist/tracing/tracer.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export interface FinishRunOptions {
|
|
|
49
49
|
cost?: number;
|
|
50
50
|
/** Metadata merged into the run's. */
|
|
51
51
|
metadata?: Record<string, unknown>;
|
|
52
|
+
/** The model that actually ran, replacing the one the run started with, such as `auto` after routing. */
|
|
53
|
+
model?: string;
|
|
54
|
+
/** The provider that actually ran. */
|
|
55
|
+
provider?: string;
|
|
52
56
|
}
|
|
53
57
|
/** A run in progress. Finishing it writes it to the store. */
|
|
54
58
|
export interface RunHandle {
|
package/dist/tracing/tracer.js
CHANGED
|
@@ -78,6 +78,8 @@ export class Tracer {
|
|
|
78
78
|
...(options.error ? { error: describeError(options.error) } : {}),
|
|
79
79
|
...(options.usage ? { usage: options.usage } : {}),
|
|
80
80
|
...(options.cost === undefined ? {} : { cost: options.cost }),
|
|
81
|
+
...(options.model ? { model: options.model } : {}),
|
|
82
|
+
...(options.provider ? { provider: options.provider } : {}),
|
|
81
83
|
metadata: { ...run.metadata, ...options.metadata },
|
|
82
84
|
};
|
|
83
85
|
await this.close(finished, sampled);
|
package/dist/types/graph.d.ts
CHANGED
|
@@ -360,6 +360,8 @@ export interface GraphCheckpointer {
|
|
|
360
360
|
history(threadId: string, limit?: number): Promise<GraphCheckpoint[]> | GraphCheckpoint[];
|
|
361
361
|
/** Deletes every checkpoint of a thread. */
|
|
362
362
|
delete?(threadId: string): Promise<void> | void;
|
|
363
|
+
/** Every thread with a checkpoint, for tools that browse threads, such as the studio. */
|
|
364
|
+
threadIds?(): Promise<string[]> | string[];
|
|
363
365
|
}
|
|
364
366
|
/** Options for one run of a compiled graph. */
|
|
365
367
|
export interface GraphRunOptions {
|
package/dist/types/security.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/** How much protection a client applies, from none to maximal. */
|
|
2
2
|
export type SecurityLevel = 'off' | 'basic' | 'standard' | 'strict' | 'paranoid';
|
|
3
|
+
/**
|
|
4
|
+
* A preset tuned for a kind of deployment. `developer` is the `standard` level; `startup` is `strict`
|
|
5
|
+
* with output redaction; `enterprise` adds blocking injection detection and PII masking; `healthcare`
|
|
6
|
+
* and `finance` are `paranoid`, blocking or masking PII respectively. Your own settings are merged
|
|
7
|
+
* over the preset.
|
|
8
|
+
*/
|
|
3
9
|
export type SecurityPreset = 'developer' | 'startup' | 'enterprise' | 'healthcare' | 'finance';
|
|
4
10
|
/**
|
|
5
11
|
* What a guardrail does with a match: let it through, block the request, mask the match, or record
|
|
@@ -12,16 +18,27 @@ export type PIIType = 'email' | 'phone' | 'credit-card' | 'ip-address' | 'aws-ke
|
|
|
12
18
|
export interface InjectionDetectionConfig {
|
|
13
19
|
/** Turns detection on. */
|
|
14
20
|
enabled?: boolean;
|
|
15
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Ignored: the patterns match or they do not.
|
|
23
|
+
*
|
|
24
|
+
* @deprecated Has never been read. It will be removed in 2.0.
|
|
25
|
+
*/
|
|
16
26
|
sensitivity?: number;
|
|
17
27
|
/** What a detection does: block the request, record it, or neutralize the text. */
|
|
18
28
|
onDetection?: 'block' | 'flag' | 'transform';
|
|
19
29
|
/** Extra patterns to treat as injection. */
|
|
20
30
|
customPatterns?: RegExp[];
|
|
21
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Similarity to example attacks, which catches rephrasings a pattern misses. In the pipeline it
|
|
33
|
+
* compares hashed term vectors, so it needs no provider; for a real embedding model, use
|
|
34
|
+
* `SemanticInjectionClassifier` with `embed` directly.
|
|
35
|
+
*/
|
|
22
36
|
semantic?: {
|
|
37
|
+
/** Turns the semantic check on. Off by default. */
|
|
23
38
|
enabled?: boolean;
|
|
39
|
+
/** Similarity at which a prompt is flagged, from 0 to 1. Defaults to 0.78. */
|
|
24
40
|
threshold?: number;
|
|
41
|
+
/** Example attacks to compare against, instead of the built-in ones. */
|
|
25
42
|
examples?: string[];
|
|
26
43
|
};
|
|
27
44
|
}
|
|
@@ -31,42 +48,82 @@ export interface PIIConfig {
|
|
|
31
48
|
enabled?: boolean;
|
|
32
49
|
/** Kinds of data to detect. */
|
|
33
50
|
detect?: PIIType[];
|
|
34
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* What a match does: mask it, remove it, block the request, or record it. Defaults to `mask` at the
|
|
53
|
+
* `strict` and `paranoid` levels and `flag` below them; `paranoid` also blocks.
|
|
54
|
+
*/
|
|
35
55
|
action?: 'mask' | 'remove' | 'block' | 'flag';
|
|
36
|
-
/** Character used for masking. */
|
|
56
|
+
/** Character used for masking. Defaults to `█`. */
|
|
37
57
|
maskChar?: string;
|
|
38
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Keeps the data's shape when masking: letters and digits are replaced one for one and punctuation
|
|
60
|
+
* stays. On by default; `false` replaces each match with a run of the mask character instead.
|
|
61
|
+
*/
|
|
39
62
|
preserveFormat?: boolean;
|
|
40
63
|
}
|
|
64
|
+
/** Detection of credentials in input: private keys, cloud and payment keys, tokens, and secret assignments. */
|
|
41
65
|
export interface SecretDetectionConfig {
|
|
66
|
+
/** Turns detection on. On by default at every level but `off`. */
|
|
42
67
|
enabled?: boolean;
|
|
68
|
+
/** What a match does: block the request, mask the secret, or record it. Defaults to `block`. */
|
|
43
69
|
action?: 'block' | 'mask' | 'flag';
|
|
44
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Detection of risky URLs in input: loopback and cloud-metadata addresses, onion services, and URLs
|
|
73
|
+
* carrying secret-like query parameters.
|
|
74
|
+
*/
|
|
45
75
|
export interface UrlRiskConfig {
|
|
76
|
+
/** Turns detection on. On by default at every level but `off`. */
|
|
46
77
|
enabled?: boolean;
|
|
78
|
+
/** What a match does: block the request, or record it. Defaults to `flag`. */
|
|
47
79
|
action?: 'block' | 'flag';
|
|
48
80
|
}
|
|
81
|
+
/** Which tools a request may offer the model. */
|
|
49
82
|
export interface ToolPolicyConfig {
|
|
83
|
+
/** The only tool names a request may carry; any other tool blocks it. Unset or empty allows every tool. */
|
|
50
84
|
allowedNames?: string[];
|
|
85
|
+
/**
|
|
86
|
+
* Ignored: approval before a tool runs belongs to the agent loop.
|
|
87
|
+
*
|
|
88
|
+
* @deprecated Has never been read. It will be removed in 2.0.
|
|
89
|
+
*/
|
|
51
90
|
requiresApproval?: string[];
|
|
52
91
|
}
|
|
92
|
+
/** Terms a response must not contain. */
|
|
53
93
|
export interface ModerationConfig {
|
|
94
|
+
/** Turns moderation on. Off by default. */
|
|
54
95
|
enabled?: boolean;
|
|
96
|
+
/** Terms matched literally, ignoring case. */
|
|
55
97
|
forbiddenTerms?: string[];
|
|
98
|
+
/** What a match does: block the response, record it, or redact the term. Defaults to recording it. */
|
|
56
99
|
onViolation?: 'block' | 'flag' | 'redact';
|
|
57
100
|
}
|
|
101
|
+
/** Data-loss prevention on output. */
|
|
58
102
|
export interface DLPConfig {
|
|
103
|
+
/** Turns the checks on. On by default. */
|
|
59
104
|
enabled?: boolean;
|
|
105
|
+
/** Terms that must not leave, such as project code names. A match is recorded as a high finding. */
|
|
60
106
|
proprietaryTerms?: string[];
|
|
107
|
+
/** Redacts database connection strings. On by default. */
|
|
61
108
|
connectionStrings?: boolean;
|
|
62
109
|
}
|
|
110
|
+
/** Topics a response must stay out of, matched as text, ignoring case. */
|
|
63
111
|
export interface TopicGuardrailConfig {
|
|
112
|
+
/** The topics. */
|
|
64
113
|
forbiddenTopics?: string[];
|
|
114
|
+
/** What a match does: block the response, or record it. Defaults to recording it. */
|
|
65
115
|
onViolation?: 'block' | 'flag';
|
|
66
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* A cheap grounding check on output: the share of the response's longer words that also appear in
|
|
119
|
+
* the context. A low share is recorded as a medium finding; it never blocks.
|
|
120
|
+
*/
|
|
67
121
|
export interface GroundingConfig {
|
|
122
|
+
/** Turns the check on. Off by default. */
|
|
68
123
|
enabled?: boolean;
|
|
124
|
+
/** The context the response should draw from. */
|
|
69
125
|
context?: string[];
|
|
126
|
+
/** Smallest acceptable share, from 0 to 1. Defaults to 0.1. */
|
|
70
127
|
minOverlapRatio?: number;
|
|
71
128
|
}
|
|
72
129
|
/** Guardrails for input and output, from a preset, a level, or detailed settings. */
|
|
@@ -77,20 +134,32 @@ export interface SecurityConfig {
|
|
|
77
134
|
level?: SecurityLevel;
|
|
78
135
|
/** Input guardrails: length, injection, PII, secrets, URLs, and tool policy. */
|
|
79
136
|
input?: {
|
|
137
|
+
/** Longest the request's text may be, in characters; longer blocks it. */
|
|
80
138
|
maxContentLength?: number;
|
|
139
|
+
/** Prompt-injection detection, from the `standard` level up. */
|
|
81
140
|
injectionDetection?: InjectionDetectionConfig;
|
|
141
|
+
/** Personal data detection, from the `standard` level up. */
|
|
82
142
|
pii?: PIIConfig;
|
|
143
|
+
/** Credential detection. */
|
|
83
144
|
secrets?: SecretDetectionConfig;
|
|
145
|
+
/** Risky URL detection. */
|
|
84
146
|
urls?: UrlRiskConfig;
|
|
147
|
+
/** Which tools a request may carry. */
|
|
85
148
|
tools?: ToolPolicyConfig;
|
|
86
149
|
};
|
|
87
150
|
/** Output guardrails: length, PII redaction, moderation, DLP, topics, and grounding. */
|
|
88
151
|
output?: {
|
|
152
|
+
/** Longest the response may be, in characters; longer is cut, and the cut recorded. */
|
|
89
153
|
maxContentLength?: number;
|
|
154
|
+
/** Redacts secrets and personal data from the response and its tool-call arguments. On by default. */
|
|
90
155
|
piiRedaction?: boolean;
|
|
156
|
+
/** Forbidden terms. */
|
|
91
157
|
moderation?: ModerationConfig;
|
|
158
|
+
/** Connection strings and proprietary terms. */
|
|
92
159
|
dlp?: DLPConfig;
|
|
160
|
+
/** Forbidden topics. */
|
|
93
161
|
topics?: TopicGuardrailConfig;
|
|
162
|
+
/** Word overlap with a context. */
|
|
94
163
|
grounding?: GroundingConfig;
|
|
95
164
|
};
|
|
96
165
|
}
|
|
@@ -32,7 +32,7 @@ export interface SummarizeVerifyFormatOptions {
|
|
|
32
32
|
model: string;
|
|
33
33
|
/** The content to summarize. */
|
|
34
34
|
input: string;
|
|
35
|
-
/** Sources to check the summary against. Without them,
|
|
35
|
+
/** Sources to check the summary against. Without them, the verify step is skipped. */
|
|
36
36
|
verifyContext?: string[];
|
|
37
37
|
/** Format of the final answer, such as a JSON schema. */
|
|
38
38
|
responseFormat?: ResponseFormatConfig;
|
|
@@ -86,8 +86,8 @@ export interface CompareOptions {
|
|
|
86
86
|
criteria?: string[];
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
|
-
* Summarizes content, verifies the summary against sources when given, then formats it:
|
|
90
|
-
* completions, each step returned.
|
|
89
|
+
* Summarizes content, verifies the summary against sources when given, then formats it: two
|
|
90
|
+
* completions, or three to four with verification, each step returned.
|
|
91
91
|
*/
|
|
92
92
|
export declare function summarizeVerifyFormat(client: WorkflowClient, options: SummarizeVerifyFormatOptions): Promise<WorkflowResult>;
|
|
93
93
|
/**
|
package/dist/workflow/chains.js
CHANGED
|
@@ -13,17 +13,28 @@ export async function summarizeVerifyFormat(client, options) {
|
|
|
13
13
|
],
|
|
14
14
|
}, { chainOfThought: 'private' }));
|
|
15
15
|
steps.push({ step: 'summarize', response: summary });
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
let verified = summary;
|
|
17
|
+
if (options.verifyContext?.length) {
|
|
18
|
+
verified = await completeVerified(client, {
|
|
18
19
|
model: options.model,
|
|
19
|
-
messages: [
|
|
20
|
-
|
|
20
|
+
messages: [
|
|
21
|
+
{
|
|
22
|
+
role: 'user',
|
|
23
|
+
content: [
|
|
24
|
+
'Restate this summary, keeping only what the sources support.',
|
|
25
|
+
'Sources:',
|
|
26
|
+
...options.verifyContext.map((item, index) => `[source-${index + 1}] ${item}`),
|
|
27
|
+
'Summary:',
|
|
28
|
+
summary.content,
|
|
29
|
+
].join('\n'),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
21
32
|
}, {
|
|
22
33
|
context: options.verifyContext,
|
|
23
34
|
repair: true,
|
|
24
|
-
})
|
|
25
|
-
:
|
|
26
|
-
|
|
35
|
+
});
|
|
36
|
+
steps.push({ step: 'verify', response: verified });
|
|
37
|
+
}
|
|
27
38
|
const formatted = await client.complete({
|
|
28
39
|
model: options.model,
|
|
29
40
|
messages: [
|
|
@@ -66,6 +66,11 @@ export declare class OperationStoreCheckpointer implements GraphCheckpointer {
|
|
|
66
66
|
history(threadId: string, limit?: number): Promise<GraphCheckpoint[]>;
|
|
67
67
|
/** Deletes a thread's checkpoints and its head. */
|
|
68
68
|
delete(threadId: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Every thread with a checkpoint, read from the head records. Empty when the store cannot list,
|
|
71
|
+
* since finding heads any other way would mean scanning every key.
|
|
72
|
+
*/
|
|
73
|
+
threadIds(): Promise<string[]>;
|
|
69
74
|
private readHead;
|
|
70
75
|
/**
|
|
71
76
|
* Writes one record, creating it when absent and otherwise updating in place.
|
|
@@ -90,6 +90,15 @@ class OperationStoreCheckpointer {
|
|
|
90
90
|
await this.store.delete?.(stepId(threadId, step));
|
|
91
91
|
await this.store.delete?.(threadId);
|
|
92
92
|
}
|
|
93
|
+
async threadIds() {
|
|
94
|
+
if (!this.store.list)
|
|
95
|
+
return [];
|
|
96
|
+
const records = await this.store.list();
|
|
97
|
+
return records
|
|
98
|
+
.filter((record) => record.result !== undefined && isHead(record.result))
|
|
99
|
+
.map((record) => record.id)
|
|
100
|
+
.sort();
|
|
101
|
+
}
|
|
93
102
|
async readHead(threadId) {
|
|
94
103
|
const record = await this.store.read(threadId);
|
|
95
104
|
return record?.result && isHead(record.result) ? record.result : undefined;
|
package/dist-cjs/mcp/client.d.ts
CHANGED
|
@@ -62,13 +62,18 @@ export declare class McpClient {
|
|
|
62
62
|
private readonly pending;
|
|
63
63
|
private nextId;
|
|
64
64
|
private initialized;
|
|
65
|
+
private connecting;
|
|
65
66
|
private serverInfo;
|
|
66
67
|
constructor(transport: McpTransport, options?: McpClientOptions);
|
|
67
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Performs the handshake and returns the server's name and version. Called automatically by the
|
|
70
|
+
* first request that needs it; requests made together share one handshake.
|
|
71
|
+
*/
|
|
68
72
|
connect(): Promise<{
|
|
69
73
|
name?: string;
|
|
70
74
|
version?: string;
|
|
71
75
|
}>;
|
|
76
|
+
private handshake;
|
|
72
77
|
/** Lists the server's tools. */
|
|
73
78
|
listTools(): Promise<McpToolDescriptor[]>;
|
|
74
79
|
/**
|
package/dist-cjs/mcp/client.js
CHANGED
|
@@ -43,6 +43,7 @@ class McpClient {
|
|
|
43
43
|
pending = new Map();
|
|
44
44
|
nextId = 1;
|
|
45
45
|
initialized = false;
|
|
46
|
+
connecting;
|
|
46
47
|
serverInfo = {};
|
|
47
48
|
constructor(transport, options = {}) {
|
|
48
49
|
this.transport = transport;
|
|
@@ -53,6 +54,12 @@ class McpClient {
|
|
|
53
54
|
async connect() {
|
|
54
55
|
if (this.initialized)
|
|
55
56
|
return this.serverInfo;
|
|
57
|
+
this.connecting ??= this.handshake().finally(() => {
|
|
58
|
+
this.connecting = undefined;
|
|
59
|
+
});
|
|
60
|
+
return this.connecting;
|
|
61
|
+
}
|
|
62
|
+
async handshake() {
|
|
56
63
|
await this.transport.start?.();
|
|
57
64
|
const result = (await this.request('initialize', {
|
|
58
65
|
protocolVersion: protocol_js_1.MCP_PROTOCOL_VERSION,
|
package/dist-cjs/mcp/server.js
CHANGED
|
@@ -39,7 +39,7 @@ class McpServer {
|
|
|
39
39
|
jsonrpc: '2.0',
|
|
40
40
|
id: message.id,
|
|
41
41
|
error: {
|
|
42
|
-
code: protocol_js_1.JSON_RPC_ERRORS.internal,
|
|
42
|
+
code: error instanceof protocol_js_1.McpError ? error.code : protocol_js_1.JSON_RPC_ERRORS.internal,
|
|
43
43
|
message: error instanceof Error ? error.message : String(error),
|
|
44
44
|
},
|
|
45
45
|
});
|
|
@@ -93,7 +93,7 @@ class McpServer {
|
|
|
93
93
|
const uri = String(params.uri ?? '');
|
|
94
94
|
const resource = this.options.resources?.find((item) => item.uri === uri);
|
|
95
95
|
if (!resource)
|
|
96
|
-
throw new
|
|
96
|
+
throw new protocol_js_1.McpError(`Unknown resource "${uri}"`, protocol_js_1.JSON_RPC_ERRORS.invalidParams);
|
|
97
97
|
const value = await resource.read();
|
|
98
98
|
return {
|
|
99
99
|
contents: [
|
|
@@ -106,7 +106,7 @@ class McpServer {
|
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
108
|
default:
|
|
109
|
-
throw new
|
|
109
|
+
throw new protocol_js_1.McpError(`Unknown method "${request.method}"`, protocol_js_1.JSON_RPC_ERRORS.methodNotFound);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -67,9 +67,9 @@ class SecurityPipeline {
|
|
|
67
67
|
findings.push(...piiFindings);
|
|
68
68
|
guardrailsApplied.push('pii-detection');
|
|
69
69
|
const action = piiConfig.action || (level === 'strict' || level === 'paranoid' ? 'mask' : 'flag');
|
|
70
|
-
if (piiFindings.length > 0 && action === 'mask') {
|
|
71
|
-
safeRequest = this.piiDetector.mask(safeRequest, piiConfig);
|
|
72
|
-
guardrailsApplied.push('pii-masking');
|
|
70
|
+
if (piiFindings.length > 0 && (action === 'mask' || action === 'remove')) {
|
|
71
|
+
safeRequest = this.piiDetector.mask(safeRequest, { ...piiConfig, action });
|
|
72
|
+
guardrailsApplied.push(action === 'remove' ? 'pii-removal' : 'pii-masking');
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
const blockingFindings = this.getBlockingFindings(findings, level, normalized);
|
|
@@ -106,8 +106,13 @@ class InputGuard {
|
|
|
106
106
|
});
|
|
107
107
|
return output;
|
|
108
108
|
};
|
|
109
|
-
if (action !== 'mask')
|
|
109
|
+
if (action !== 'mask') {
|
|
110
|
+
request.messages.forEach((message, index) => {
|
|
111
|
+
for (const text of this.extractText(message))
|
|
112
|
+
maskText(text, `messages.${index}.content`);
|
|
113
|
+
});
|
|
110
114
|
return request;
|
|
115
|
+
}
|
|
111
116
|
return {
|
|
112
117
|
...request,
|
|
113
118
|
messages: request.messages.map((message, index) => ({
|
|
@@ -4,7 +4,7 @@ import type { PIIConfig, SecurityFinding } from '../types/security.js';
|
|
|
4
4
|
export declare class PIIDetector {
|
|
5
5
|
/** Finds personal data in a request's messages. */
|
|
6
6
|
detect(request: CompletionRequest, config?: PIIConfig): SecurityFinding[];
|
|
7
|
-
/** Returns the request with personal data masked
|
|
7
|
+
/** Returns the request with personal data masked, or removed when the action is `remove`. */
|
|
8
8
|
mask(request: CompletionRequest, config?: PIIConfig): CompletionRequest;
|
|
9
9
|
private maskText;
|
|
10
10
|
private extractText;
|
|
@@ -38,22 +38,28 @@ class PIIDetector {
|
|
|
38
38
|
mask(request, config = {}) {
|
|
39
39
|
const enabledTypes = config.detect || ['email', 'phone', 'credit-card', 'ip-address', 'aws-key', 'private-key'];
|
|
40
40
|
const maskChar = config.maskChar || '█';
|
|
41
|
+
const remove = config.action === 'remove';
|
|
41
42
|
return {
|
|
42
43
|
...request,
|
|
43
44
|
messages: request.messages.map((message) => ({
|
|
44
45
|
...message,
|
|
45
46
|
content: typeof message.content === 'string'
|
|
46
|
-
? this.maskText(message.content, enabledTypes, maskChar, config.preserveFormat !== false)
|
|
47
|
+
? this.maskText(message.content, enabledTypes, maskChar, config.preserveFormat !== false, remove)
|
|
47
48
|
: message.content.map((part) => part.type === 'text'
|
|
48
|
-
? {
|
|
49
|
+
? {
|
|
50
|
+
...part,
|
|
51
|
+
text: this.maskText(part.text, enabledTypes, maskChar, config.preserveFormat !== false, remove),
|
|
52
|
+
}
|
|
49
53
|
: part),
|
|
50
54
|
})),
|
|
51
55
|
};
|
|
52
56
|
}
|
|
53
|
-
maskText(text, types, maskChar, preserveFormat) {
|
|
57
|
+
maskText(text, types, maskChar, preserveFormat, remove = false) {
|
|
54
58
|
let masked = text;
|
|
55
59
|
for (const type of types) {
|
|
56
60
|
masked = masked.replace(PII_PATTERNS[type], (value) => {
|
|
61
|
+
if (remove)
|
|
62
|
+
return '';
|
|
57
63
|
if (!preserveFormat)
|
|
58
64
|
return maskChar.repeat(Math.min(value.length, 12));
|
|
59
65
|
return value.replace(/[A-Za-z0-9]/g, maskChar);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { CompletionRequest } from '../types/messages.js';
|
|
2
|
+
/** Options for `hardenPrompt()`. */
|
|
2
3
|
export interface PromptHardeningOptions {
|
|
4
|
+
/** What user text is wrapped in. Defaults to `"""`. */
|
|
3
5
|
delimiter?: string;
|
|
6
|
+
/** The system message placed first, telling the model to treat delimited text as data. */
|
|
4
7
|
systemInstruction?: string;
|
|
5
8
|
}
|
|
6
9
|
/**
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import type { CompletionRequest } from '../types/messages.js';
|
|
2
2
|
import type { SecurityFinding } from '../types/security.js';
|
|
3
3
|
import { type EmbeddingProvider } from '../hallucination/retrieval.js';
|
|
4
|
+
/** Options for `SemanticInjectionClassifier`. */
|
|
4
5
|
export interface SemanticInjectionClassifierOptions {
|
|
6
|
+
/** `false` turns `detect()` off. */
|
|
5
7
|
enabled?: boolean;
|
|
8
|
+
/** Similarity at which a prompt is flagged, from 0 to 1. Defaults to 0.78; above 0.88 is critical. */
|
|
6
9
|
threshold?: number;
|
|
10
|
+
/** Embeds text for `detect()`. Defaults to hashed term vectors; `detectSync()` always uses those. */
|
|
7
11
|
embed?: EmbeddingProvider;
|
|
12
|
+
/** Example attacks to compare against, instead of the built-in ones. */
|
|
8
13
|
examples?: string[];
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
@@ -16,6 +21,7 @@ export declare class SemanticInjectionClassifier {
|
|
|
16
21
|
private examples;
|
|
17
22
|
private embed;
|
|
18
23
|
private exampleEmbeddings?;
|
|
24
|
+
private hashedExamples?;
|
|
19
25
|
constructor(options?: SemanticInjectionClassifierOptions);
|
|
20
26
|
/** Finds injection attempts, using the configured embedding function. */
|
|
21
27
|
detect(request: CompletionRequest): Promise<SecurityFinding[]>;
|
|
@@ -15,6 +15,7 @@ class SemanticInjectionClassifier {
|
|
|
15
15
|
examples;
|
|
16
16
|
embed;
|
|
17
17
|
exampleEmbeddings;
|
|
18
|
+
hashedExamples;
|
|
18
19
|
constructor(options = {}) {
|
|
19
20
|
this.options = options;
|
|
20
21
|
this.examples = options.examples || DEFAULT_ATTACK_EXAMPLES;
|
|
@@ -46,7 +47,8 @@ class SemanticInjectionClassifier {
|
|
|
46
47
|
detectSync(request) {
|
|
47
48
|
const findings = [];
|
|
48
49
|
const threshold = this.options.threshold ?? 0.78;
|
|
49
|
-
|
|
50
|
+
this.hashedExamples ??= (0, retrieval_js_1.createHashEmbeddings)(this.examples);
|
|
51
|
+
const exampleEmbeddings = this.hashedExamples;
|
|
50
52
|
for (const [index, message] of request.messages.entries()) {
|
|
51
53
|
for (const text of extractText(message)) {
|
|
52
54
|
const [embedding] = (0, retrieval_js_1.createHashEmbeddings)([text]);
|
|
@@ -109,6 +109,8 @@ function traceModelClient(client, tracer, options = {}) {
|
|
|
109
109
|
}
|
|
110
110
|
: {}),
|
|
111
111
|
...(response.meta?.cost?.amount === undefined ? {} : { cost: response.meta.cost.amount }),
|
|
112
|
+
...(response.meta?.modelUsed ? { model: response.meta.modelUsed } : {}),
|
|
113
|
+
...(response.meta?.providerUsed ? { provider: response.meta.providerUsed } : {}),
|
|
112
114
|
metadata: {
|
|
113
115
|
provider: response.meta?.providerUsed,
|
|
114
116
|
model: response.meta?.modelUsed,
|
|
@@ -49,6 +49,10 @@ export interface FinishRunOptions {
|
|
|
49
49
|
cost?: number;
|
|
50
50
|
/** Metadata merged into the run's. */
|
|
51
51
|
metadata?: Record<string, unknown>;
|
|
52
|
+
/** The model that actually ran, replacing the one the run started with, such as `auto` after routing. */
|
|
53
|
+
model?: string;
|
|
54
|
+
/** The provider that actually ran. */
|
|
55
|
+
provider?: string;
|
|
52
56
|
}
|
|
53
57
|
/** A run in progress. Finishing it writes it to the store. */
|
|
54
58
|
export interface RunHandle {
|
|
@@ -82,6 +82,8 @@ class Tracer {
|
|
|
82
82
|
...(options.error ? { error: describeError(options.error) } : {}),
|
|
83
83
|
...(options.usage ? { usage: options.usage } : {}),
|
|
84
84
|
...(options.cost === undefined ? {} : { cost: options.cost }),
|
|
85
|
+
...(options.model ? { model: options.model } : {}),
|
|
86
|
+
...(options.provider ? { provider: options.provider } : {}),
|
|
85
87
|
metadata: { ...run.metadata, ...options.metadata },
|
|
86
88
|
};
|
|
87
89
|
await this.close(finished, sampled);
|
|
@@ -360,6 +360,8 @@ export interface GraphCheckpointer {
|
|
|
360
360
|
history(threadId: string, limit?: number): Promise<GraphCheckpoint[]> | GraphCheckpoint[];
|
|
361
361
|
/** Deletes every checkpoint of a thread. */
|
|
362
362
|
delete?(threadId: string): Promise<void> | void;
|
|
363
|
+
/** Every thread with a checkpoint, for tools that browse threads, such as the studio. */
|
|
364
|
+
threadIds?(): Promise<string[]> | string[];
|
|
363
365
|
}
|
|
364
366
|
/** Options for one run of a compiled graph. */
|
|
365
367
|
export interface GraphRunOptions {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/** How much protection a client applies, from none to maximal. */
|
|
2
2
|
export type SecurityLevel = 'off' | 'basic' | 'standard' | 'strict' | 'paranoid';
|
|
3
|
+
/**
|
|
4
|
+
* A preset tuned for a kind of deployment. `developer` is the `standard` level; `startup` is `strict`
|
|
5
|
+
* with output redaction; `enterprise` adds blocking injection detection and PII masking; `healthcare`
|
|
6
|
+
* and `finance` are `paranoid`, blocking or masking PII respectively. Your own settings are merged
|
|
7
|
+
* over the preset.
|
|
8
|
+
*/
|
|
3
9
|
export type SecurityPreset = 'developer' | 'startup' | 'enterprise' | 'healthcare' | 'finance';
|
|
4
10
|
/**
|
|
5
11
|
* What a guardrail does with a match: let it through, block the request, mask the match, or record
|
|
@@ -12,16 +18,27 @@ export type PIIType = 'email' | 'phone' | 'credit-card' | 'ip-address' | 'aws-ke
|
|
|
12
18
|
export interface InjectionDetectionConfig {
|
|
13
19
|
/** Turns detection on. */
|
|
14
20
|
enabled?: boolean;
|
|
15
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Ignored: the patterns match or they do not.
|
|
23
|
+
*
|
|
24
|
+
* @deprecated Has never been read. It will be removed in 2.0.
|
|
25
|
+
*/
|
|
16
26
|
sensitivity?: number;
|
|
17
27
|
/** What a detection does: block the request, record it, or neutralize the text. */
|
|
18
28
|
onDetection?: 'block' | 'flag' | 'transform';
|
|
19
29
|
/** Extra patterns to treat as injection. */
|
|
20
30
|
customPatterns?: RegExp[];
|
|
21
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Similarity to example attacks, which catches rephrasings a pattern misses. In the pipeline it
|
|
33
|
+
* compares hashed term vectors, so it needs no provider; for a real embedding model, use
|
|
34
|
+
* `SemanticInjectionClassifier` with `embed` directly.
|
|
35
|
+
*/
|
|
22
36
|
semantic?: {
|
|
37
|
+
/** Turns the semantic check on. Off by default. */
|
|
23
38
|
enabled?: boolean;
|
|
39
|
+
/** Similarity at which a prompt is flagged, from 0 to 1. Defaults to 0.78. */
|
|
24
40
|
threshold?: number;
|
|
41
|
+
/** Example attacks to compare against, instead of the built-in ones. */
|
|
25
42
|
examples?: string[];
|
|
26
43
|
};
|
|
27
44
|
}
|
|
@@ -31,42 +48,82 @@ export interface PIIConfig {
|
|
|
31
48
|
enabled?: boolean;
|
|
32
49
|
/** Kinds of data to detect. */
|
|
33
50
|
detect?: PIIType[];
|
|
34
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* What a match does: mask it, remove it, block the request, or record it. Defaults to `mask` at the
|
|
53
|
+
* `strict` and `paranoid` levels and `flag` below them; `paranoid` also blocks.
|
|
54
|
+
*/
|
|
35
55
|
action?: 'mask' | 'remove' | 'block' | 'flag';
|
|
36
|
-
/** Character used for masking. */
|
|
56
|
+
/** Character used for masking. Defaults to `█`. */
|
|
37
57
|
maskChar?: string;
|
|
38
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Keeps the data's shape when masking: letters and digits are replaced one for one and punctuation
|
|
60
|
+
* stays. On by default; `false` replaces each match with a run of the mask character instead.
|
|
61
|
+
*/
|
|
39
62
|
preserveFormat?: boolean;
|
|
40
63
|
}
|
|
64
|
+
/** Detection of credentials in input: private keys, cloud and payment keys, tokens, and secret assignments. */
|
|
41
65
|
export interface SecretDetectionConfig {
|
|
66
|
+
/** Turns detection on. On by default at every level but `off`. */
|
|
42
67
|
enabled?: boolean;
|
|
68
|
+
/** What a match does: block the request, mask the secret, or record it. Defaults to `block`. */
|
|
43
69
|
action?: 'block' | 'mask' | 'flag';
|
|
44
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Detection of risky URLs in input: loopback and cloud-metadata addresses, onion services, and URLs
|
|
73
|
+
* carrying secret-like query parameters.
|
|
74
|
+
*/
|
|
45
75
|
export interface UrlRiskConfig {
|
|
76
|
+
/** Turns detection on. On by default at every level but `off`. */
|
|
46
77
|
enabled?: boolean;
|
|
78
|
+
/** What a match does: block the request, or record it. Defaults to `flag`. */
|
|
47
79
|
action?: 'block' | 'flag';
|
|
48
80
|
}
|
|
81
|
+
/** Which tools a request may offer the model. */
|
|
49
82
|
export interface ToolPolicyConfig {
|
|
83
|
+
/** The only tool names a request may carry; any other tool blocks it. Unset or empty allows every tool. */
|
|
50
84
|
allowedNames?: string[];
|
|
85
|
+
/**
|
|
86
|
+
* Ignored: approval before a tool runs belongs to the agent loop.
|
|
87
|
+
*
|
|
88
|
+
* @deprecated Has never been read. It will be removed in 2.0.
|
|
89
|
+
*/
|
|
51
90
|
requiresApproval?: string[];
|
|
52
91
|
}
|
|
92
|
+
/** Terms a response must not contain. */
|
|
53
93
|
export interface ModerationConfig {
|
|
94
|
+
/** Turns moderation on. Off by default. */
|
|
54
95
|
enabled?: boolean;
|
|
96
|
+
/** Terms matched literally, ignoring case. */
|
|
55
97
|
forbiddenTerms?: string[];
|
|
98
|
+
/** What a match does: block the response, record it, or redact the term. Defaults to recording it. */
|
|
56
99
|
onViolation?: 'block' | 'flag' | 'redact';
|
|
57
100
|
}
|
|
101
|
+
/** Data-loss prevention on output. */
|
|
58
102
|
export interface DLPConfig {
|
|
103
|
+
/** Turns the checks on. On by default. */
|
|
59
104
|
enabled?: boolean;
|
|
105
|
+
/** Terms that must not leave, such as project code names. A match is recorded as a high finding. */
|
|
60
106
|
proprietaryTerms?: string[];
|
|
107
|
+
/** Redacts database connection strings. On by default. */
|
|
61
108
|
connectionStrings?: boolean;
|
|
62
109
|
}
|
|
110
|
+
/** Topics a response must stay out of, matched as text, ignoring case. */
|
|
63
111
|
export interface TopicGuardrailConfig {
|
|
112
|
+
/** The topics. */
|
|
64
113
|
forbiddenTopics?: string[];
|
|
114
|
+
/** What a match does: block the response, or record it. Defaults to recording it. */
|
|
65
115
|
onViolation?: 'block' | 'flag';
|
|
66
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* A cheap grounding check on output: the share of the response's longer words that also appear in
|
|
119
|
+
* the context. A low share is recorded as a medium finding; it never blocks.
|
|
120
|
+
*/
|
|
67
121
|
export interface GroundingConfig {
|
|
122
|
+
/** Turns the check on. Off by default. */
|
|
68
123
|
enabled?: boolean;
|
|
124
|
+
/** The context the response should draw from. */
|
|
69
125
|
context?: string[];
|
|
126
|
+
/** Smallest acceptable share, from 0 to 1. Defaults to 0.1. */
|
|
70
127
|
minOverlapRatio?: number;
|
|
71
128
|
}
|
|
72
129
|
/** Guardrails for input and output, from a preset, a level, or detailed settings. */
|
|
@@ -77,20 +134,32 @@ export interface SecurityConfig {
|
|
|
77
134
|
level?: SecurityLevel;
|
|
78
135
|
/** Input guardrails: length, injection, PII, secrets, URLs, and tool policy. */
|
|
79
136
|
input?: {
|
|
137
|
+
/** Longest the request's text may be, in characters; longer blocks it. */
|
|
80
138
|
maxContentLength?: number;
|
|
139
|
+
/** Prompt-injection detection, from the `standard` level up. */
|
|
81
140
|
injectionDetection?: InjectionDetectionConfig;
|
|
141
|
+
/** Personal data detection, from the `standard` level up. */
|
|
82
142
|
pii?: PIIConfig;
|
|
143
|
+
/** Credential detection. */
|
|
83
144
|
secrets?: SecretDetectionConfig;
|
|
145
|
+
/** Risky URL detection. */
|
|
84
146
|
urls?: UrlRiskConfig;
|
|
147
|
+
/** Which tools a request may carry. */
|
|
85
148
|
tools?: ToolPolicyConfig;
|
|
86
149
|
};
|
|
87
150
|
/** Output guardrails: length, PII redaction, moderation, DLP, topics, and grounding. */
|
|
88
151
|
output?: {
|
|
152
|
+
/** Longest the response may be, in characters; longer is cut, and the cut recorded. */
|
|
89
153
|
maxContentLength?: number;
|
|
154
|
+
/** Redacts secrets and personal data from the response and its tool-call arguments. On by default. */
|
|
90
155
|
piiRedaction?: boolean;
|
|
156
|
+
/** Forbidden terms. */
|
|
91
157
|
moderation?: ModerationConfig;
|
|
158
|
+
/** Connection strings and proprietary terms. */
|
|
92
159
|
dlp?: DLPConfig;
|
|
160
|
+
/** Forbidden topics. */
|
|
93
161
|
topics?: TopicGuardrailConfig;
|
|
162
|
+
/** Word overlap with a context. */
|
|
94
163
|
grounding?: GroundingConfig;
|
|
95
164
|
};
|
|
96
165
|
}
|
|
@@ -32,7 +32,7 @@ export interface SummarizeVerifyFormatOptions {
|
|
|
32
32
|
model: string;
|
|
33
33
|
/** The content to summarize. */
|
|
34
34
|
input: string;
|
|
35
|
-
/** Sources to check the summary against. Without them,
|
|
35
|
+
/** Sources to check the summary against. Without them, the verify step is skipped. */
|
|
36
36
|
verifyContext?: string[];
|
|
37
37
|
/** Format of the final answer, such as a JSON schema. */
|
|
38
38
|
responseFormat?: ResponseFormatConfig;
|
|
@@ -86,8 +86,8 @@ export interface CompareOptions {
|
|
|
86
86
|
criteria?: string[];
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
|
-
* Summarizes content, verifies the summary against sources when given, then formats it:
|
|
90
|
-
* completions, each step returned.
|
|
89
|
+
* Summarizes content, verifies the summary against sources when given, then formats it: two
|
|
90
|
+
* completions, or three to four with verification, each step returned.
|
|
91
91
|
*/
|
|
92
92
|
export declare function summarizeVerifyFormat(client: WorkflowClient, options: SummarizeVerifyFormatOptions): Promise<WorkflowResult>;
|
|
93
93
|
/**
|
|
@@ -20,17 +20,28 @@ async function summarizeVerifyFormat(client, options) {
|
|
|
20
20
|
],
|
|
21
21
|
}, { chainOfThought: 'private' }));
|
|
22
22
|
steps.push({ step: 'summarize', response: summary });
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
let verified = summary;
|
|
24
|
+
if (options.verifyContext?.length) {
|
|
25
|
+
verified = await (0, verification_js_1.completeVerified)(client, {
|
|
25
26
|
model: options.model,
|
|
26
|
-
messages: [
|
|
27
|
-
|
|
27
|
+
messages: [
|
|
28
|
+
{
|
|
29
|
+
role: 'user',
|
|
30
|
+
content: [
|
|
31
|
+
'Restate this summary, keeping only what the sources support.',
|
|
32
|
+
'Sources:',
|
|
33
|
+
...options.verifyContext.map((item, index) => `[source-${index + 1}] ${item}`),
|
|
34
|
+
'Summary:',
|
|
35
|
+
summary.content,
|
|
36
|
+
].join('\n'),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
28
39
|
}, {
|
|
29
40
|
context: options.verifyContext,
|
|
30
41
|
repair: true,
|
|
31
|
-
})
|
|
32
|
-
:
|
|
33
|
-
|
|
42
|
+
});
|
|
43
|
+
steps.push({ step: 'verify', response: verified });
|
|
44
|
+
}
|
|
34
45
|
const formatted = await client.complete({
|
|
35
46
|
model: options.model,
|
|
36
47
|
messages: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-ai-pro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Typed AI framework for TypeScript: provider routing and failover, parallel agent graphs with checkpoints, durable operations, guardrails, and cost control",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist-cjs/index.js",
|
|
@@ -1017,7 +1017,12 @@
|
|
|
1017
1017
|
"example:agent": "tsx examples/agent.ts",
|
|
1018
1018
|
"example:realtime": "tsx examples/realtime-scheduling.ts",
|
|
1019
1019
|
"size:check": "node scripts/check-subpath-sizes.mjs",
|
|
1020
|
-
"docs:check": "node scripts/check-doc-coverage.mjs",
|
|
1020
|
+
"docs:check": "node scripts/check-doc-coverage.mjs && node scripts/check-doc-coverage.mjs --package studio",
|
|
1021
|
+
"studio:build": "node studio/scripts/build.mjs",
|
|
1022
|
+
"studio:typecheck": "tsc -p studio/tsconfig.json",
|
|
1023
|
+
"studio:demo": "node studio/scripts/run.mjs studio/example/start.ts",
|
|
1024
|
+
"test:studio": "node studio/scripts/run.mjs test",
|
|
1025
|
+
"test:studio-install": "node studio/scripts/install-smoke.mjs",
|
|
1021
1026
|
"docs:guides": "node scripts/check-guide-coverage.mjs",
|
|
1022
1027
|
"docs:update": "node scripts/check-guide-coverage.mjs --update",
|
|
1023
1028
|
"size:update": "node scripts/check-subpath-sizes.mjs --update",
|
|
@@ -1030,7 +1035,7 @@
|
|
|
1030
1035
|
"typecheck:tests": "tsc -p tsconfig.tests.json --noEmit",
|
|
1031
1036
|
"typecheck:examples": "tsc -p tsconfig.examples.json --noEmit",
|
|
1032
1037
|
"typecheck:examples:browser": "tsc -p tsconfig.examples.browser.json --noEmit",
|
|
1033
|
-
"test": "npm run build && npm run test:unit && npm run test:conformance:mock && npm run test:conformance:images && npm run size:check && npm run docs:check && npm run docs:guides && npm run test:smoke && npm run test:api && npm run test:types",
|
|
1038
|
+
"test": "npm run build && npm run test:unit && npm run test:conformance:mock && npm run test:conformance:images && npm run size:check && npm run docs:check && npm run docs:guides && npm run test:studio && npm run test:smoke && npm run test:api && npm run test:types",
|
|
1034
1039
|
"test:unit": "node --import tsx --test tests/unit.ts tests/*.test.ts",
|
|
1035
1040
|
"test:coverage": "node --import tsx --test --experimental-test-coverage --test-coverage-include=src/**/*.ts --test-coverage-lines=82 --test-coverage-branches=67 --test-coverage-functions=73 tests/unit.ts tests/*.test.ts",
|
|
1036
1041
|
"test:smoke": "node tests/package-smoke.mjs",
|
|
@@ -1041,8 +1046,8 @@
|
|
|
1041
1046
|
"test:conformance:real": "tsx tests/conformance.real.ts",
|
|
1042
1047
|
"bench:graph": "node scripts/bench-graph.mjs",
|
|
1043
1048
|
"pack:dry-run": "npm pack --dry-run --cache .tmp-nexus-ai-tests/npm-cache",
|
|
1044
|
-
"check": "npm run format:check && npm run registry:check && npm run lint && npm test && npm run typecheck:tests && npm run typecheck:examples && npm run typecheck:examples:browser && npm run test:coverage",
|
|
1045
|
-
"check:release": "npm run check && npm run bench:graph && npm run pack:dry-run && npm run test:clean-install",
|
|
1049
|
+
"check": "npm run format:check && npm run registry:check && npm run lint && npm test && npm run typecheck:tests && npm run studio:typecheck && npm run typecheck:examples && npm run typecheck:examples:browser && npm run test:coverage",
|
|
1050
|
+
"check:release": "npm run check && npm run bench:graph && npm run pack:dry-run && npm run test:clean-install && npm run test:studio-install",
|
|
1046
1051
|
"prepack": "npm run build",
|
|
1047
1052
|
"prepublishOnly": "npm run check:release",
|
|
1048
1053
|
"build:esm": "tsc --removeComments --declaration false && tsc --emitDeclarationOnly",
|