nexus-ai-pro 2.2.0 → 2.3.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 +39 -0
- package/CHANGELOG.md +142 -1
- package/README.md +10 -5
- package/dist/agent/capabilities.d.ts +39 -0
- package/dist/agent/capabilities.js +36 -0
- package/dist/agent/create-agent.d.ts +61 -11
- package/dist/agent/create-agent.js +136 -22
- package/dist/agent/index.d.ts +3 -2
- package/dist/agent/index.js +1 -1
- package/dist/agent/middleware/approval.d.ts +26 -0
- package/dist/agent/middleware/approval.js +23 -0
- package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist/agent/middleware/context-editor.d.ts +52 -0
- package/dist/agent/middleware/context-editor.js +66 -0
- package/dist/agent/middleware/filesystem.d.ts +44 -0
- package/dist/agent/middleware/filesystem.js +83 -0
- package/dist/agent/middleware/index.d.ts +19 -0
- package/dist/agent/middleware/index.js +9 -0
- package/dist/agent/middleware/limits.d.ts +42 -0
- package/dist/agent/middleware/limits.js +52 -0
- package/dist/agent/middleware/models.d.ts +48 -0
- package/dist/agent/middleware/models.js +44 -0
- package/dist/agent/middleware/pii.d.ts +67 -0
- package/dist/agent/middleware/pii.js +173 -0
- package/dist/agent/middleware/retry.d.ts +66 -0
- package/dist/agent/middleware/retry.js +59 -0
- package/dist/agent/middleware/shared.d.ts +41 -0
- package/dist/agent/middleware/shared.js +73 -0
- package/dist/agent/middleware/tool-selector.d.ts +64 -0
- package/dist/agent/middleware/tool-selector.js +106 -0
- package/dist/agent/permissions.d.ts +113 -0
- package/dist/agent/permissions.js +180 -0
- package/dist/agent/sandbox.d.ts +151 -0
- package/dist/agent/sandbox.js +257 -0
- package/dist/agent/tool.d.ts +8 -3
- package/dist/agent/tool.js +14 -3
- package/dist/deep-agent/index.d.ts +109 -0
- package/dist/deep-agent/index.js +216 -0
- package/dist/graph/functional.d.ts +9 -0
- package/dist/graph/functional.js +20 -3
- package/dist/graph/graph.js +25 -4
- package/dist/graph/lint.d.ts +4 -1
- package/dist/graph/lint.js +35 -0
- package/dist/postgres/fulltext.d.ts +67 -0
- package/dist/postgres/fulltext.js +91 -0
- package/dist/rag/elasticsearch.d.ts +71 -0
- package/dist/rag/elasticsearch.js +130 -0
- package/dist/rag/pipeline.d.ts +140 -0
- package/dist/rag/pipeline.js +187 -0
- package/dist/rag/rerankers.d.ts +92 -0
- package/dist/rag/rerankers.js +97 -0
- package/dist/rag/retrievers.d.ts +12 -1
- package/dist/server/assistant.d.ts +5 -1
- package/dist/server/assistant.js +4 -2
- package/dist/server/auth.d.ts +250 -0
- package/dist/server/auth.js +392 -0
- package/dist/store/memory.d.ts +10 -0
- package/dist/store/memory.js +10 -0
- package/dist/types/graph.d.ts +38 -0
- package/dist/types/messages.d.ts +36 -2
- package/dist/types/principal.d.ts +21 -0
- package/dist/types/principal.js +1 -0
- package/dist/types/server.d.ts +2 -9
- package/dist/utils/tenant.d.ts +11 -0
- package/dist/utils/tenant.js +14 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/capabilities.d.ts +39 -0
- package/dist-cjs/agent/capabilities.js +41 -0
- package/dist-cjs/agent/create-agent.d.ts +61 -11
- package/dist-cjs/agent/create-agent.js +136 -22
- package/dist-cjs/agent/index.d.ts +3 -2
- package/dist-cjs/agent/index.js +4 -4
- package/dist-cjs/agent/middleware/approval.d.ts +26 -0
- package/dist-cjs/agent/middleware/approval.js +26 -0
- package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
- package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
- package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
- package/dist-cjs/agent/middleware/context-editor.js +70 -0
- package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
- package/dist-cjs/agent/middleware/filesystem.js +86 -0
- package/dist-cjs/agent/middleware/index.d.ts +19 -0
- package/dist-cjs/agent/middleware/index.js +28 -0
- package/dist-cjs/agent/middleware/limits.d.ts +42 -0
- package/dist-cjs/agent/middleware/limits.js +57 -0
- package/dist-cjs/agent/middleware/models.d.ts +48 -0
- package/dist-cjs/agent/middleware/models.js +48 -0
- package/dist-cjs/agent/middleware/pii.d.ts +67 -0
- package/dist-cjs/agent/middleware/pii.js +178 -0
- package/dist-cjs/agent/middleware/retry.d.ts +66 -0
- package/dist-cjs/agent/middleware/retry.js +63 -0
- package/dist-cjs/agent/middleware/shared.d.ts +41 -0
- package/dist-cjs/agent/middleware/shared.js +83 -0
- package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
- package/dist-cjs/agent/middleware/tool-selector.js +109 -0
- package/dist-cjs/agent/permissions.d.ts +113 -0
- package/dist-cjs/agent/permissions.js +189 -0
- package/dist-cjs/agent/sandbox.d.ts +151 -0
- package/dist-cjs/agent/sandbox.js +297 -0
- package/dist-cjs/agent/tool.d.ts +8 -3
- package/dist-cjs/agent/tool.js +14 -3
- package/dist-cjs/deep-agent/index.d.ts +109 -0
- package/dist-cjs/deep-agent/index.js +219 -0
- package/dist-cjs/graph/functional.d.ts +9 -0
- package/dist-cjs/graph/functional.js +20 -3
- package/dist-cjs/graph/graph.js +25 -4
- package/dist-cjs/graph/lint.d.ts +4 -1
- package/dist-cjs/graph/lint.js +35 -0
- package/dist-cjs/postgres/fulltext.d.ts +67 -0
- package/dist-cjs/postgres/fulltext.js +129 -0
- package/dist-cjs/rag/elasticsearch.d.ts +71 -0
- package/dist-cjs/rag/elasticsearch.js +135 -0
- package/dist-cjs/rag/pipeline.d.ts +140 -0
- package/dist-cjs/rag/pipeline.js +191 -0
- package/dist-cjs/rag/rerankers.d.ts +92 -0
- package/dist-cjs/rag/rerankers.js +107 -0
- package/dist-cjs/rag/retrievers.d.ts +12 -1
- package/dist-cjs/server/assistant.d.ts +5 -1
- package/dist-cjs/server/assistant.js +4 -2
- package/dist-cjs/server/auth.d.ts +250 -0
- package/dist-cjs/server/auth.js +402 -0
- package/dist-cjs/store/memory.d.ts +10 -0
- package/dist-cjs/store/memory.js +10 -0
- package/dist-cjs/types/graph.d.ts +38 -0
- package/dist-cjs/types/messages.d.ts +36 -2
- package/dist-cjs/types/principal.d.ts +21 -0
- package/dist-cjs/types/principal.js +2 -0
- package/dist-cjs/types/server.d.ts +2 -9
- package/dist-cjs/utils/tenant.d.ts +11 -0
- package/dist-cjs/utils/tenant.js +15 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +4 -3
- package/package.json +118 -1
package/dist/agent/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { AgentLoop, type AgentModelClient as AgentLoopModelClient } from './loop.js';
|
|
2
2
|
export { isToolOutput, tool, ToolExecutor, toolMessageContent, toolOutput } from './tool.js';
|
|
3
|
-
export { limitToolCalls, redactMessages, type RedactOptions, summarizeHistory, type SummarizeOptions, } from './middleware.js';
|
|
4
|
-
export { agentAsTool, agentInput, type AgentApproval, type AgentApprovalPolicy, type AgentChannels, type AgentGraph, type AgentMiddleware, type AgentModelClient, type AgentState, type AgentStopReason, type AgentToolCall, type AgentToolResult, createAgent, type CreateAgentOptions, } from './create-agent.js';
|
|
3
|
+
export { limitToolCalls, redactMessages, type RedactOptions, summarizeHistory, type SummarizeOptions, } from './middleware/basic.js';
|
|
4
|
+
export { agentAsTool, agentInput, type AgentApproval, type AgentApprovalPolicy, type AgentChannels, type AgentGraph, type AgentMiddleware, type AgentMiddlewareContext, type AgentModelCallContext, type AgentModelClient, type AgentState, type AgentStopReason, type AgentToolCall, type AgentToolResult, createAgent, type CreateAgentOptions, } from './create-agent.js';
|
|
5
5
|
export type { AgentConfig, AgentResult, AgentStep, ToolExecutionResult } from '../types/agent.js';
|
|
6
|
+
export type { ToolContext } from '../types/messages.js';
|
package/dist/agent/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { AgentLoop } from './loop.js';
|
|
2
2
|
export { isToolOutput, tool, ToolExecutor, toolMessageContent, toolOutput } from './tool.js';
|
|
3
|
-
export { limitToolCalls, redactMessages, summarizeHistory, } from './middleware.js';
|
|
3
|
+
export { limitToolCalls, redactMessages, summarizeHistory, } from './middleware/basic.js';
|
|
4
4
|
export { agentAsTool, agentInput, createAgent, } from './create-agent.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentApproval, AgentMiddleware, AgentMiddlewareContext, AgentToolCall } from '../create-agent.js';
|
|
2
|
+
/** Options for `humanApproval()`. */
|
|
3
|
+
export interface HumanApprovalOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Which calls need a person's approval. Defaults to every call whose tool declares nothing, or
|
|
6
|
+
* declares a capability that does anything but read.
|
|
7
|
+
*/
|
|
8
|
+
when?: (call: AgentToolCall, context: AgentMiddlewareContext) => boolean;
|
|
9
|
+
/** The question a person sees. Defaults to naming the tool and its arguments. */
|
|
10
|
+
reason?: (call: AgentToolCall) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Asks out of band instead of suspending the run: posts to a chat and waits for the answer, or
|
|
13
|
+
* applies a rule of your own. Without it, the run suspends through `interrupt()` until `resume()`
|
|
14
|
+
* answers, as `interruptOn` does.
|
|
15
|
+
*/
|
|
16
|
+
approve?: (call: AgentToolCall, context: AgentMiddlewareContext) => AgentApproval | Promise<AgentApproval>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Asks a person before a tool call runs, decided per call rather than per tool name.
|
|
20
|
+
*
|
|
21
|
+
* `interruptOn` approves every call to a named tool; this approves the calls a rule picks: a refund
|
|
22
|
+
* over a limit, a write outside a draft folder, anything a tool does that is not a read. An
|
|
23
|
+
* approver may refuse, or correct the arguments, which the permission policy then decides again.
|
|
24
|
+
* Put it before `toolRetry()`, so a retried call is not asked about twice.
|
|
25
|
+
*/
|
|
26
|
+
export declare function humanApproval(options?: HumanApprovalOptions): AgentMiddleware;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isSensitiveCapability } from '../capabilities.js';
|
|
2
|
+
export function humanApproval(options = {}) {
|
|
3
|
+
const when = options.when ??
|
|
4
|
+
((call) => call.capabilities === undefined || call.capabilities.some(isSensitiveCapability));
|
|
5
|
+
const reason = options.reason ?? ((call) => `Run tool "${call.name}" with ${JSON.stringify(call.args)}?`);
|
|
6
|
+
return {
|
|
7
|
+
name: 'human-approval',
|
|
8
|
+
async wrapToolCall(call, next, context) {
|
|
9
|
+
if (!when(call, context))
|
|
10
|
+
return next();
|
|
11
|
+
const answer = options.approve
|
|
12
|
+
? await options.approve(call, context)
|
|
13
|
+
: context.interrupt({ reason: reason(call), payload: call });
|
|
14
|
+
if (answer === false || (typeof answer === 'object' && !answer.approved)) {
|
|
15
|
+
const why = typeof answer === 'object' && !answer.approved ? answer.reason : undefined;
|
|
16
|
+
return { ok: false, error: why ?? 'A person refused this tool call' };
|
|
17
|
+
}
|
|
18
|
+
return typeof answer === 'object' && answer.approved && answer.args
|
|
19
|
+
? next({ ...call, args: answer.args })
|
|
20
|
+
: next();
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Message } from '
|
|
2
|
-
import type { AgentMiddleware } from '
|
|
1
|
+
import type { Message } from '../../types/messages.js';
|
|
2
|
+
import type { AgentMiddleware } from '../create-agent.js';
|
|
3
3
|
/**
|
|
4
4
|
* Middleware that ships with the agent.
|
|
5
5
|
*
|
|
@@ -40,9 +40,10 @@ export interface RedactOptions {
|
|
|
40
40
|
*/
|
|
41
41
|
export declare function redactMessages(options: RedactOptions): AgentMiddleware;
|
|
42
42
|
/**
|
|
43
|
-
* Caps how often a tool may be called in one run.
|
|
43
|
+
* Caps how often a tool may be called in one run: one turn, from a user message to the answer.
|
|
44
44
|
*
|
|
45
45
|
* A model that loops on the same call otherwise burns the iteration budget, and with an expensive
|
|
46
|
-
* tool it burns money too.
|
|
46
|
+
* tool it burns money too. Calls are counted from the transcript, so the count survives a restart
|
|
47
|
+
* and a resumed approval, and two threads never share one.
|
|
47
48
|
*/
|
|
48
49
|
export declare function limitToolCalls(limits: Record<string, number>): AgentMiddleware;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { callPosition } from './shared.js';
|
|
1
2
|
export function summarizeHistory(options) {
|
|
2
3
|
const keepLast = options.keepLast ?? 6;
|
|
3
4
|
const triggerAfter = options.triggerAfter ?? 20;
|
|
@@ -40,18 +41,15 @@ export function redactMessages(options) {
|
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
export function limitToolCalls(limits) {
|
|
43
|
-
const counts = new Map();
|
|
44
44
|
return {
|
|
45
45
|
name: 'limit-tool-calls',
|
|
46
|
-
async wrapToolCall(call, next) {
|
|
46
|
+
async wrapToolCall(call, next, context) {
|
|
47
47
|
const limit = limits[call.name];
|
|
48
48
|
if (limit === undefined)
|
|
49
49
|
return next();
|
|
50
|
-
|
|
51
|
-
if (used >= limit) {
|
|
50
|
+
if (callPosition(context.state.messages, call) > limit) {
|
|
52
51
|
return { ok: false, error: `Tool "${call.name}" has already run ${limit} times in this run` };
|
|
53
52
|
}
|
|
54
|
-
counts.set(call.name, used + 1);
|
|
55
53
|
return next();
|
|
56
54
|
},
|
|
57
55
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { CompletionRequest } from '../../types/messages.js';
|
|
2
|
+
import type { AgentMiddleware } from '../create-agent.js';
|
|
3
|
+
/** One cleared result, for a custom placeholder. */
|
|
4
|
+
export interface ClearedToolResult {
|
|
5
|
+
/** The tool whose result was cleared. */
|
|
6
|
+
tool: string;
|
|
7
|
+
/** How long the result was, in characters. */
|
|
8
|
+
length: number;
|
|
9
|
+
}
|
|
10
|
+
/** What one edit did, for logs and for measuring what it saves. */
|
|
11
|
+
export interface ContextEdit {
|
|
12
|
+
/** Tool results cleared. */
|
|
13
|
+
cleared: number;
|
|
14
|
+
/** The request's estimated tokens before. */
|
|
15
|
+
tokensBefore: number;
|
|
16
|
+
/** And after. */
|
|
17
|
+
tokensAfter: number;
|
|
18
|
+
}
|
|
19
|
+
/** Options for `contextEditor()`. */
|
|
20
|
+
export interface ContextEditorOptions {
|
|
21
|
+
/** Editing starts once the request's estimated tokens pass this. Defaults to 30,000. */
|
|
22
|
+
triggerTokens?: number;
|
|
23
|
+
/** The most recent tool results, kept whole. Defaults to 3. */
|
|
24
|
+
keepToolResults?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Results are cleared this many at a time, so the edited prefix stays the same for several calls
|
|
27
|
+
* and a provider's prompt cache keeps working between edits. Defaults to 5.
|
|
28
|
+
*/
|
|
29
|
+
batch?: number;
|
|
30
|
+
/** Tools whose results are never cleared. */
|
|
31
|
+
exclude?: readonly string[];
|
|
32
|
+
/** Also empties the arguments of each call whose result is cleared, for tools that take large inputs. */
|
|
33
|
+
clearArguments?: boolean;
|
|
34
|
+
/** What a cleared result says instead. Defaults to a line naming the tool and how to get it back. */
|
|
35
|
+
placeholder?: string | ((cleared: ClearedToolResult) => string);
|
|
36
|
+
/** Estimates a request's tokens. Defaults to about four characters a token. */
|
|
37
|
+
estimateTokens?: (request: CompletionRequest) => number;
|
|
38
|
+
/** Called after each edit. */
|
|
39
|
+
onEdit?: (edit: ContextEdit) => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clears old tool results from what the model is sent, once the request grows past a threshold.
|
|
43
|
+
*
|
|
44
|
+
* Tool output is most of a long agent's context, and the model rarely needs a result from twenty
|
|
45
|
+
* steps ago verbatim. Every result but the most recent few is replaced by a short placeholder, in
|
|
46
|
+
* batches, so the edited prefix stays the same between edits and a provider's prompt cache keeps
|
|
47
|
+
* working. Only the request changes: the transcript in state keeps every result, and checkpoints,
|
|
48
|
+
* events, and a later summary still see them.
|
|
49
|
+
*/
|
|
50
|
+
export declare function contextEditor(options?: ContextEditorOptions): AgentMiddleware;
|
|
51
|
+
/** About four characters a token, over the messages, the tool calls, and the tool schemas. */
|
|
52
|
+
export declare function estimateRequestTokens(request: CompletionRequest): number;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { textOf } from './shared.js';
|
|
2
|
+
export function contextEditor(options = {}) {
|
|
3
|
+
const triggerTokens = options.triggerTokens ?? 30_000;
|
|
4
|
+
const keep = Math.max(0, options.keepToolResults ?? 3);
|
|
5
|
+
const batch = Math.max(1, options.batch ?? 5);
|
|
6
|
+
const exclude = new Set(options.exclude ?? []);
|
|
7
|
+
const estimate = options.estimateTokens ?? estimateRequestTokens;
|
|
8
|
+
const placeholder = (cleared) => typeof options.placeholder === 'function'
|
|
9
|
+
? options.placeholder(cleared)
|
|
10
|
+
: (options.placeholder ??
|
|
11
|
+
`[Cleared to save context: the result of ${cleared.tool}, ${cleared.length} characters. Call the tool again if you need it.]`);
|
|
12
|
+
return {
|
|
13
|
+
name: 'context-editor',
|
|
14
|
+
beforeModel({ request }) {
|
|
15
|
+
const tokensBefore = estimate(request);
|
|
16
|
+
if (tokensBefore <= triggerTokens)
|
|
17
|
+
return;
|
|
18
|
+
const names = new Map();
|
|
19
|
+
for (const message of request.messages) {
|
|
20
|
+
for (const call of message.toolCalls ?? [])
|
|
21
|
+
names.set(call.id, call.function.name);
|
|
22
|
+
}
|
|
23
|
+
const results = request.messages.flatMap((message, index) => (message.role === 'tool' ? [index] : []));
|
|
24
|
+
const eligible = Math.max(0, results.length - keep);
|
|
25
|
+
const count = eligible < batch ? eligible : eligible - (eligible % batch);
|
|
26
|
+
const clearable = new Set(results.slice(0, count).filter((index) => {
|
|
27
|
+
const id = request.messages[index]?.toolCallId;
|
|
28
|
+
return !exclude.has(names.get(id ?? '') ?? '');
|
|
29
|
+
}));
|
|
30
|
+
if (clearable.size === 0)
|
|
31
|
+
return;
|
|
32
|
+
const clearedIds = new Set();
|
|
33
|
+
const messages = request.messages.map((message, index) => {
|
|
34
|
+
if (!clearable.has(index))
|
|
35
|
+
return message;
|
|
36
|
+
const text = textOf(message.content);
|
|
37
|
+
const tool = names.get(message.toolCallId ?? '') ?? 'a tool';
|
|
38
|
+
clearedIds.add(message.toolCallId ?? '');
|
|
39
|
+
return { ...message, content: placeholder({ tool, length: text.length }) };
|
|
40
|
+
});
|
|
41
|
+
const edited = options.clearArguments
|
|
42
|
+
? messages.map((message) => message.toolCalls?.some((call) => clearedIds.has(call.id))
|
|
43
|
+
? {
|
|
44
|
+
...message,
|
|
45
|
+
toolCalls: message.toolCalls.map((call) => clearedIds.has(call.id) ? { ...call, function: { ...call.function, arguments: '{}' } } : call),
|
|
46
|
+
}
|
|
47
|
+
: message)
|
|
48
|
+
: messages;
|
|
49
|
+
const next = { ...request, messages: edited };
|
|
50
|
+
options.onEdit?.({ cleared: clearable.size, tokensBefore, tokensAfter: estimate(next) });
|
|
51
|
+
return next;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function estimateRequestTokens(request) {
|
|
56
|
+
let characters = 0;
|
|
57
|
+
for (const message of request.messages) {
|
|
58
|
+
characters += textOf(message.content).length + 4;
|
|
59
|
+
for (const call of message.toolCalls ?? [])
|
|
60
|
+
characters += call.function.name.length + call.function.arguments.length;
|
|
61
|
+
}
|
|
62
|
+
for (const tool of request.tools ?? []) {
|
|
63
|
+
characters += tool.name.length + tool.description.length + JSON.stringify(tool.parameters ?? {}).length;
|
|
64
|
+
}
|
|
65
|
+
return Math.ceil(characters / 4);
|
|
66
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AgentMiddleware } from '../create-agent.js';
|
|
2
|
+
import type { Sandbox } from '../sandbox.js';
|
|
3
|
+
/** Where `filesystemContext()` reads and writes: a `Sandbox`, or anything with the same file methods. */
|
|
4
|
+
export type FilesystemContextSource = Pick<Sandbox, 'readFile'> & Partial<Pick<Sandbox, 'writeFile' | 'listFiles'>>;
|
|
5
|
+
/** Where large tool results go instead of the transcript. */
|
|
6
|
+
export interface ContextOffloadOptions {
|
|
7
|
+
/** Results longer than this, in characters, are offloaded. Defaults to 20,000. */
|
|
8
|
+
overChars?: number;
|
|
9
|
+
/** The directory they are written to, inside the source. Defaults to `.context`. */
|
|
10
|
+
directory?: string;
|
|
11
|
+
/** How much of a result stays in the transcript as a preview, in characters. Defaults to 1,000. */
|
|
12
|
+
previewChars?: number;
|
|
13
|
+
/** Tools whose results are never offloaded. Defaults to `read_file`, so reading one back cannot loop. */
|
|
14
|
+
exclude?: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
/** Options for `filesystemContext()`. */
|
|
17
|
+
export interface FilesystemContextOptions {
|
|
18
|
+
/** Where files are read from and offloaded to. */
|
|
19
|
+
source: FilesystemContextSource;
|
|
20
|
+
/**
|
|
21
|
+
* Files whose contents the model sees on every call, after the system prompt: project
|
|
22
|
+
* instructions such as `AGENTS.md`, conventions, a glossary. A missing file is skipped.
|
|
23
|
+
*/
|
|
24
|
+
include?: readonly string[];
|
|
25
|
+
/** Lists a directory after the files, so the model knows what exists without a tool call. Needs `listFiles`. */
|
|
26
|
+
listing?: {
|
|
27
|
+
directory?: string;
|
|
28
|
+
maxEntries?: number;
|
|
29
|
+
};
|
|
30
|
+
/** The most characters of included files, together. Defaults to 32,000; the rest is cut. */
|
|
31
|
+
maxChars?: number;
|
|
32
|
+
/** Writes large tool results to a file and leaves a preview and the path. Needs `writeFile`. */
|
|
33
|
+
offload?: ContextOffloadOptions | boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The filesystem as the agent's context: files in, large results out.
|
|
37
|
+
*
|
|
38
|
+
* Instruction files reach the model on every call without a tool call, read once per run and placed
|
|
39
|
+
* right after the system prompt, so the prefix a provider caches stays the same. A tool result too
|
|
40
|
+
* large for the context is written to a file instead, and the model reads a preview and a path it can
|
|
41
|
+
* open with `read_file` when it needs the rest: the transcript stays small however much a tool
|
|
42
|
+
* returns, and nothing is lost.
|
|
43
|
+
*/
|
|
44
|
+
export declare function filesystemContext(options: FilesystemContextOptions): AgentMiddleware;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export function filesystemContext(options) {
|
|
2
|
+
const maxChars = options.maxChars ?? 32_000;
|
|
3
|
+
const offload = options.offload === true
|
|
4
|
+
? {}
|
|
5
|
+
: options.offload === false || options.offload === undefined
|
|
6
|
+
? undefined
|
|
7
|
+
: options.offload;
|
|
8
|
+
if (offload && !options.source.writeFile) {
|
|
9
|
+
throw new TypeError('filesystemContext() offloading needs a source with writeFile()');
|
|
10
|
+
}
|
|
11
|
+
const loaded = new Map();
|
|
12
|
+
const load = async () => {
|
|
13
|
+
const sections = [];
|
|
14
|
+
let budget = maxChars;
|
|
15
|
+
for (const file of options.include ?? []) {
|
|
16
|
+
if (budget <= 0)
|
|
17
|
+
break;
|
|
18
|
+
let text;
|
|
19
|
+
try {
|
|
20
|
+
text = await options.source.readFile(file);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const cut = text.length > budget ? `${text.slice(0, budget)}\n[cut: ${text.length - budget} more characters]` : text;
|
|
26
|
+
budget -= text.length;
|
|
27
|
+
sections.push(`<file path="${file}">\n${cut}\n</file>`);
|
|
28
|
+
}
|
|
29
|
+
if (options.listing && options.source.listFiles) {
|
|
30
|
+
try {
|
|
31
|
+
const entries = await options.source.listFiles(options.listing.directory ?? '.');
|
|
32
|
+
const max = options.listing.maxEntries ?? 200;
|
|
33
|
+
const shown = entries.slice(0, max).join('\n');
|
|
34
|
+
sections.push(`<listing directory="${options.listing.directory ?? '.'}">\n${shown}${entries.length > max ? `\n[${entries.length - max} more]` : ''}\n</listing>`);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return sections.length ? `Files from the workspace:\n\n${sections.join('\n\n')}` : undefined;
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
name: 'filesystem-context',
|
|
43
|
+
async beforeModel({ request, state, threadId }) {
|
|
44
|
+
if (!options.include?.length && !options.listing)
|
|
45
|
+
return;
|
|
46
|
+
const turn = state.messages.filter((message) => message.role === 'user').length;
|
|
47
|
+
const key = `${threadId}:${turn}`;
|
|
48
|
+
let pending = loaded.get(key);
|
|
49
|
+
if (!pending) {
|
|
50
|
+
pending = load();
|
|
51
|
+
loaded.set(key, pending);
|
|
52
|
+
if (loaded.size > 256)
|
|
53
|
+
loaded.delete(loaded.keys().next().value);
|
|
54
|
+
}
|
|
55
|
+
const text = await pending;
|
|
56
|
+
if (!text)
|
|
57
|
+
return;
|
|
58
|
+
const systemCount = request.messages.findIndex((message) => message.role !== 'system');
|
|
59
|
+
const at = systemCount === -1 ? request.messages.length : systemCount;
|
|
60
|
+
const context = { role: 'system', content: text };
|
|
61
|
+
return { ...request, messages: [...request.messages.slice(0, at), context, ...request.messages.slice(at)] };
|
|
62
|
+
},
|
|
63
|
+
async wrapToolCall(call, next) {
|
|
64
|
+
const result = await next();
|
|
65
|
+
if (!offload || !result.ok || (offload.exclude ?? ['read_file']).includes(call.name))
|
|
66
|
+
return result;
|
|
67
|
+
const value = result.result;
|
|
68
|
+
if (value?.type === 'tool_output')
|
|
69
|
+
return result;
|
|
70
|
+
const text = typeof value === 'string' ? value : JSON.stringify(value);
|
|
71
|
+
if (text === undefined || text.length <= (offload.overChars ?? 20_000))
|
|
72
|
+
return result;
|
|
73
|
+
const directory = (offload.directory ?? '.context').replace(/\/+$/, '');
|
|
74
|
+
const file = `${directory}/${`${call.name}-${call.id}`.replace(/[^A-Za-z0-9._-]/g, '_')}.txt`;
|
|
75
|
+
await options.source.writeFile?.(file, text);
|
|
76
|
+
const preview = text.slice(0, offload.previewChars ?? 1_000);
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
result: `${preview}\n\n[This result is ${text.length} characters; the first ${preview.length} are above. The whole result is in ${file}: read it with read_file when you need the rest.]`,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware for `createAgent()`, each its own import: retries, fallbacks, model choice, tool
|
|
3
|
+
* selection, context editing, personal data, approvals, limits, and the filesystem as context.
|
|
4
|
+
*
|
|
5
|
+
* Each is ordinary `AgentMiddleware` built on the same hooks an application's own middleware uses,
|
|
6
|
+
* so any of them can be read, copied, and changed. None adds a dependency, and a bundler keeps only
|
|
7
|
+
* the ones imported.
|
|
8
|
+
*/
|
|
9
|
+
export { limitToolCalls, redactMessages, type RedactOptions, summarizeHistory, type SummarizeOptions, } from './basic.js';
|
|
10
|
+
export { type ClearedToolResult, type ContextEdit, contextEditor, type ContextEditorOptions, estimateRequestTokens, } from './context-editor.js';
|
|
11
|
+
export { type ContextOffloadOptions, filesystemContext, type FilesystemContextOptions, type FilesystemContextSource, } from './filesystem.js';
|
|
12
|
+
export { humanApproval, type HumanApprovalOptions } from './approval.js';
|
|
13
|
+
export { modelCallLimit, ModelCallLimitError, type ModelCallLimitOptions } from './limits.js';
|
|
14
|
+
export { type DynamicModelContext, dynamicModel, modelFallback, type ModelFallbackEvent, type ModelFallbackOptions, type ModelFallbackTarget, } from './models.js';
|
|
15
|
+
export { PiiBlockedError, type PiiFinding, type PiiKind, piiMiddleware, type PiiMiddlewareOptions, type PiiStrategy, type PiiWhere, } from './pii.js';
|
|
16
|
+
export { modelRetry, type ModelRetryOptions, type RetryEvent, toolRetry, type ToolRetryOptions } from './retry.js';
|
|
17
|
+
export type { RetryBackoff } from './shared.js';
|
|
18
|
+
export { type ToolSelection, type ToolSelectionContext, toolSelector, type ToolSelectorOptions, } from './tool-selector.js';
|
|
19
|
+
export type { AgentMiddleware, AgentMiddlewareContext, AgentModelCallContext, AgentToolCall, AgentToolResult, } from '../create-agent.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { limitToolCalls, redactMessages, summarizeHistory, } from './basic.js';
|
|
2
|
+
export { contextEditor, estimateRequestTokens, } from './context-editor.js';
|
|
3
|
+
export { filesystemContext, } from './filesystem.js';
|
|
4
|
+
export { humanApproval } from './approval.js';
|
|
5
|
+
export { modelCallLimit, ModelCallLimitError } from './limits.js';
|
|
6
|
+
export { dynamicModel, modelFallback, } from './models.js';
|
|
7
|
+
export { PiiBlockedError, piiMiddleware, } from './pii.js';
|
|
8
|
+
export { modelRetry, toolRetry } from './retry.js';
|
|
9
|
+
export { toolSelector, } from './tool-selector.js';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AgentMiddleware } from '../create-agent.js';
|
|
2
|
+
/** Options for `modelCallLimit()`. Set `run`, `thread`, or both. */
|
|
3
|
+
export interface ModelCallLimitOptions {
|
|
4
|
+
/** Model calls in one run: from a user message to the answer, a resumed approval included. */
|
|
5
|
+
run?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Model calls over a thread's whole life, every run on it included. Counted in the agent's store
|
|
8
|
+
* when it has one, so every process shares the count and it survives a restart; otherwise in this
|
|
9
|
+
* process. A budget, not a lock: two runs on one thread at the same moment may each pass it once.
|
|
10
|
+
*/
|
|
11
|
+
thread?: number;
|
|
12
|
+
/**
|
|
13
|
+
* What happens at a limit: `end` answers with `message` and stops with `stopReason: 'stopped'`,
|
|
14
|
+
* without calling the model; `error` fails the run with a `ModelCallLimitError`. Defaults to `end`.
|
|
15
|
+
*/
|
|
16
|
+
onLimit?: 'end' | 'error';
|
|
17
|
+
/** The answer a run ends with under `end`. Defaults to a sentence naming the limit. */
|
|
18
|
+
message?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Thrown by `modelCallLimit()` under `onLimit: 'error'`. */
|
|
21
|
+
export declare class ModelCallLimitError extends Error {
|
|
22
|
+
/** Which limit was reached. */
|
|
23
|
+
readonly scope: 'run' | 'thread';
|
|
24
|
+
/** The limit. */
|
|
25
|
+
readonly limit: number;
|
|
26
|
+
/** Always `MODEL_CALL_LIMIT`. */
|
|
27
|
+
readonly code = "MODEL_CALL_LIMIT";
|
|
28
|
+
constructor(
|
|
29
|
+
/** Which limit was reached. */
|
|
30
|
+
scope: 'run' | 'thread',
|
|
31
|
+
/** The limit. */
|
|
32
|
+
limit: number);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Caps the model calls of a run, of a thread, or both.
|
|
36
|
+
*
|
|
37
|
+
* `maxIterations` stops one run that keeps calling tools; this is a budget an application sets per
|
|
38
|
+
* run and per conversation, so a thread a user keeps coming back to cannot spend without bound. The
|
|
39
|
+
* run count is read from the transcript, so it survives a restart. A run that reaches a limit ends
|
|
40
|
+
* with an answer rather than an error, unless you ask for one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function modelCallLimit(options: ModelCallLimitOptions): AgentMiddleware;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { modelCallsInTurn } from './shared.js';
|
|
2
|
+
export class ModelCallLimitError extends Error {
|
|
3
|
+
scope;
|
|
4
|
+
limit;
|
|
5
|
+
code = 'MODEL_CALL_LIMIT';
|
|
6
|
+
constructor(scope, limit) {
|
|
7
|
+
super(`The agent reached its limit of ${limit} model calls per ${scope}`);
|
|
8
|
+
this.scope = scope;
|
|
9
|
+
this.limit = limit;
|
|
10
|
+
this.name = 'ModelCallLimitError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const NAMESPACE = ['nexus', 'agent', 'model-calls'];
|
|
14
|
+
export function modelCallLimit(options) {
|
|
15
|
+
if (options.run === undefined && options.thread === undefined) {
|
|
16
|
+
throw new RangeError('modelCallLimit() needs a run limit, a thread limit, or both');
|
|
17
|
+
}
|
|
18
|
+
const local = new Map();
|
|
19
|
+
return {
|
|
20
|
+
name: 'model-call-limit',
|
|
21
|
+
async wrapModelCall(request, next, context) {
|
|
22
|
+
let reached;
|
|
23
|
+
if (options.run !== undefined && modelCallsInTurn(context.state.messages) >= options.run) {
|
|
24
|
+
reached = ['run', options.run];
|
|
25
|
+
}
|
|
26
|
+
let used = 0;
|
|
27
|
+
if (!reached && options.thread !== undefined) {
|
|
28
|
+
used = context.store
|
|
29
|
+
? ((await context.store.get(NAMESPACE, context.threadId))?.value ?? 0)
|
|
30
|
+
: (local.get(context.threadId) ?? 0);
|
|
31
|
+
if (used >= options.thread)
|
|
32
|
+
reached = ['thread', options.thread];
|
|
33
|
+
}
|
|
34
|
+
if (reached) {
|
|
35
|
+
const [scope, limit] = reached;
|
|
36
|
+
if (options.onLimit === 'error')
|
|
37
|
+
throw new ModelCallLimitError(scope, limit);
|
|
38
|
+
return context.stop(options.message ?? `I stopped here: this ${scope} reached its limit of ${limit} model calls.`);
|
|
39
|
+
}
|
|
40
|
+
if (options.thread !== undefined) {
|
|
41
|
+
if (context.store)
|
|
42
|
+
await context.store.put(NAMESPACE, context.threadId, used + 1);
|
|
43
|
+
else {
|
|
44
|
+
local.set(context.threadId, used + 1);
|
|
45
|
+
if (local.size > 10_000)
|
|
46
|
+
local.delete(local.keys().next().value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return next(request);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { CompletionRequest } from '../../types/messages.js';
|
|
2
|
+
import type { AgentMiddleware, AgentMiddlewareContext, AgentModelClient } from '../create-agent.js';
|
|
3
|
+
/** A model to fall back to: a model name for the agent's own client, or another client. */
|
|
4
|
+
export type ModelFallbackTarget = string | {
|
|
5
|
+
client: AgentModelClient;
|
|
6
|
+
model?: string;
|
|
7
|
+
};
|
|
8
|
+
/** One fallback about to happen. */
|
|
9
|
+
export interface ModelFallbackEvent {
|
|
10
|
+
/** The model that failed. */
|
|
11
|
+
from: string;
|
|
12
|
+
/** The model tried next. */
|
|
13
|
+
to: string;
|
|
14
|
+
/** What the failed call threw. */
|
|
15
|
+
error: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** Options for `modelFallback()`. */
|
|
18
|
+
export interface ModelFallbackOptions {
|
|
19
|
+
/** Whether an error moves on to the next model. Defaults to every error but a cancellation. */
|
|
20
|
+
fallbackOn?: (error: unknown, model: string) => boolean;
|
|
21
|
+
/** Called before each fallback, for logs and metrics. */
|
|
22
|
+
onFallback?: (event: ModelFallbackEvent) => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Tries other models, in order, when the model call fails.
|
|
26
|
+
*
|
|
27
|
+
* The client's router already falls back between providers for one request; this is the fallback
|
|
28
|
+
* it cannot make: to a client of a different kind, or once the client's own routes are exhausted. A
|
|
29
|
+
* target with its own client is called through `complete()`, never streamed, and middleware after
|
|
30
|
+
* this one does not wrap it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function modelFallback(models: readonly ModelFallbackTarget[], options?: ModelFallbackOptions): AgentMiddleware;
|
|
33
|
+
/** What `dynamicModel()` decides from: the step's context and the request about to be sent. */
|
|
34
|
+
export type DynamicModelContext = AgentMiddlewareContext & {
|
|
35
|
+
request: CompletionRequest;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Chooses the model for each call from the run so far.
|
|
39
|
+
*
|
|
40
|
+
* A cheap model for the first steps and a stronger one once the task proves hard, a model with a
|
|
41
|
+
* longer context once the transcript grows, or a tenant's own model. Returning nothing keeps the
|
|
42
|
+
* model the request already names.
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* dynamicModel(({ state }) => (state.iterations >= 4 ? 'gpt-5' : 'gpt-5-mini'))
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function dynamicModel(select: (context: DynamicModelContext) => string | undefined | Promise<string | undefined>): AgentMiddleware;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isCancellation } from './shared.js';
|
|
2
|
+
export function modelFallback(models, options = {}) {
|
|
3
|
+
if (models.length === 0)
|
|
4
|
+
throw new RangeError('modelFallback() needs at least one model to fall back to');
|
|
5
|
+
return {
|
|
6
|
+
name: 'model-fallback',
|
|
7
|
+
async wrapModelCall(request, next, context) {
|
|
8
|
+
try {
|
|
9
|
+
return await next(request);
|
|
10
|
+
}
|
|
11
|
+
catch (first) {
|
|
12
|
+
let error = first;
|
|
13
|
+
let from = request.model;
|
|
14
|
+
for (const target of models) {
|
|
15
|
+
if (isCancellation(error, context.signal) || !(options.fallbackOn?.(error, from) ?? true))
|
|
16
|
+
throw error;
|
|
17
|
+
const model = typeof target === 'string' ? target : (target.model ?? request.model);
|
|
18
|
+
options.onFallback?.({ from, to: model, error });
|
|
19
|
+
try {
|
|
20
|
+
return typeof target === 'string'
|
|
21
|
+
? await next({ ...request, model })
|
|
22
|
+
: await target.client.complete({ ...request, model });
|
|
23
|
+
}
|
|
24
|
+
catch (failure) {
|
|
25
|
+
error = failure;
|
|
26
|
+
from = model;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function dynamicModel(select) {
|
|
35
|
+
return {
|
|
36
|
+
name: 'dynamic-model',
|
|
37
|
+
async beforeModel(context) {
|
|
38
|
+
const model = await select(context);
|
|
39
|
+
if (!model || model === context.request.model)
|
|
40
|
+
return;
|
|
41
|
+
return { ...context.request, model };
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { AgentMiddleware } from '../create-agent.js';
|
|
2
|
+
/** The personal data and secrets `piiMiddleware()` finds on its own. */
|
|
3
|
+
export type PiiKind = 'email' | 'credit-card' | 'ssn' | 'phone' | 'iban' | 'ip-address' | 'secret';
|
|
4
|
+
/**
|
|
5
|
+
* What happens to a match: `redact` replaces it with its kind, `mask` keeps its last four
|
|
6
|
+
* characters, `hash` replaces it with a stable token so two values stay distinguishable, and `block`
|
|
7
|
+
* refuses the whole message.
|
|
8
|
+
*/
|
|
9
|
+
export type PiiStrategy = 'redact' | 'mask' | 'hash' | 'block';
|
|
10
|
+
/** Where a match was found. */
|
|
11
|
+
export type PiiWhere = 'input' | 'output' | 'tool';
|
|
12
|
+
/** One match, as `onDetect` sees it: never the value itself. */
|
|
13
|
+
export interface PiiFinding {
|
|
14
|
+
/** Its kind: a built-in kind or a name from `patterns`. */
|
|
15
|
+
kind: string;
|
|
16
|
+
/** Where it was found. */
|
|
17
|
+
where: PiiWhere;
|
|
18
|
+
/** What was done with it. */
|
|
19
|
+
strategy: PiiStrategy;
|
|
20
|
+
}
|
|
21
|
+
/** Options for `piiMiddleware()`. */
|
|
22
|
+
export interface PiiMiddlewareOptions {
|
|
23
|
+
/** The built-in kinds to find. Defaults to all of them. */
|
|
24
|
+
kinds?: readonly PiiKind[];
|
|
25
|
+
/** Kinds of your own, by name: `{ 'employee-id': /\bEMP-\d{6}\b/ }`. */
|
|
26
|
+
patterns?: Readonly<Record<string, RegExp>>;
|
|
27
|
+
/**
|
|
28
|
+
* What happens to a match, for every kind or per kind: `'mask'`, or
|
|
29
|
+
* `{ email: 'hash', 'credit-card': 'block' }`. Kinds not named are redacted.
|
|
30
|
+
*/
|
|
31
|
+
strategy?: PiiStrategy | Readonly<Record<string, PiiStrategy>>;
|
|
32
|
+
/**
|
|
33
|
+
* Where to look. `input` is every message sent to the model but the system prompt; `output` is
|
|
34
|
+
* the model's answer; `toolResults` cleans a result before it enters the transcript, so it is never
|
|
35
|
+
* checkpointed either. Defaults to input and tool results.
|
|
36
|
+
*/
|
|
37
|
+
apply?: {
|
|
38
|
+
input?: boolean;
|
|
39
|
+
output?: boolean;
|
|
40
|
+
toolResults?: boolean;
|
|
41
|
+
};
|
|
42
|
+
/** Called for each match, for an audit trail. */
|
|
43
|
+
onDetect?: (finding: PiiFinding) => void;
|
|
44
|
+
}
|
|
45
|
+
/** Thrown when a match under the `block` strategy is found in the model's input or output. */
|
|
46
|
+
export declare class PiiBlockedError extends Error {
|
|
47
|
+
/** The kind that was found. */
|
|
48
|
+
readonly kind: string;
|
|
49
|
+
/** Where it was found. */
|
|
50
|
+
readonly where: PiiWhere;
|
|
51
|
+
/** Always `PII_BLOCKED`. */
|
|
52
|
+
readonly code = "PII_BLOCKED";
|
|
53
|
+
constructor(
|
|
54
|
+
/** The kind that was found. */
|
|
55
|
+
kind: string,
|
|
56
|
+
/** Where it was found. */
|
|
57
|
+
where: PiiWhere);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Keeps personal data and secrets away from the model, the transcript, or both.
|
|
61
|
+
*
|
|
62
|
+
* Finds emails, card numbers (checked with Luhn), US social security numbers, phone numbers, IBANs
|
|
63
|
+
* (checked with their checksum), IP addresses, and common API keys and private keys, plus any kinds
|
|
64
|
+
* of your own; and redacts, masks, hashes, or blocks each. The validated kinds keep false positives
|
|
65
|
+
* low: an order number is not a card number unless its checksum says so.
|
|
66
|
+
*/
|
|
67
|
+
export declare function piiMiddleware(options?: PiiMiddlewareOptions): AgentMiddleware;
|