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.
Files changed (135) hide show
  1. package/API_STABILITY.md +39 -0
  2. package/CHANGELOG.md +142 -1
  3. package/README.md +10 -5
  4. package/dist/agent/capabilities.d.ts +39 -0
  5. package/dist/agent/capabilities.js +36 -0
  6. package/dist/agent/create-agent.d.ts +61 -11
  7. package/dist/agent/create-agent.js +136 -22
  8. package/dist/agent/index.d.ts +3 -2
  9. package/dist/agent/index.js +1 -1
  10. package/dist/agent/middleware/approval.d.ts +26 -0
  11. package/dist/agent/middleware/approval.js +23 -0
  12. package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  13. package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
  14. package/dist/agent/middleware/context-editor.d.ts +52 -0
  15. package/dist/agent/middleware/context-editor.js +66 -0
  16. package/dist/agent/middleware/filesystem.d.ts +44 -0
  17. package/dist/agent/middleware/filesystem.js +83 -0
  18. package/dist/agent/middleware/index.d.ts +19 -0
  19. package/dist/agent/middleware/index.js +9 -0
  20. package/dist/agent/middleware/limits.d.ts +42 -0
  21. package/dist/agent/middleware/limits.js +52 -0
  22. package/dist/agent/middleware/models.d.ts +48 -0
  23. package/dist/agent/middleware/models.js +44 -0
  24. package/dist/agent/middleware/pii.d.ts +67 -0
  25. package/dist/agent/middleware/pii.js +173 -0
  26. package/dist/agent/middleware/retry.d.ts +66 -0
  27. package/dist/agent/middleware/retry.js +59 -0
  28. package/dist/agent/middleware/shared.d.ts +41 -0
  29. package/dist/agent/middleware/shared.js +73 -0
  30. package/dist/agent/middleware/tool-selector.d.ts +64 -0
  31. package/dist/agent/middleware/tool-selector.js +106 -0
  32. package/dist/agent/permissions.d.ts +113 -0
  33. package/dist/agent/permissions.js +180 -0
  34. package/dist/agent/sandbox.d.ts +151 -0
  35. package/dist/agent/sandbox.js +257 -0
  36. package/dist/agent/tool.d.ts +8 -3
  37. package/dist/agent/tool.js +14 -3
  38. package/dist/deep-agent/index.d.ts +109 -0
  39. package/dist/deep-agent/index.js +216 -0
  40. package/dist/graph/functional.d.ts +9 -0
  41. package/dist/graph/functional.js +20 -3
  42. package/dist/graph/graph.js +25 -4
  43. package/dist/graph/lint.d.ts +4 -1
  44. package/dist/graph/lint.js +35 -0
  45. package/dist/postgres/fulltext.d.ts +67 -0
  46. package/dist/postgres/fulltext.js +91 -0
  47. package/dist/rag/elasticsearch.d.ts +71 -0
  48. package/dist/rag/elasticsearch.js +130 -0
  49. package/dist/rag/pipeline.d.ts +140 -0
  50. package/dist/rag/pipeline.js +187 -0
  51. package/dist/rag/rerankers.d.ts +92 -0
  52. package/dist/rag/rerankers.js +97 -0
  53. package/dist/rag/retrievers.d.ts +12 -1
  54. package/dist/server/assistant.d.ts +5 -1
  55. package/dist/server/assistant.js +4 -2
  56. package/dist/server/auth.d.ts +250 -0
  57. package/dist/server/auth.js +392 -0
  58. package/dist/store/memory.d.ts +10 -0
  59. package/dist/store/memory.js +10 -0
  60. package/dist/types/graph.d.ts +38 -0
  61. package/dist/types/messages.d.ts +36 -2
  62. package/dist/types/principal.d.ts +21 -0
  63. package/dist/types/principal.js +1 -0
  64. package/dist/types/server.d.ts +2 -9
  65. package/dist/utils/tenant.d.ts +11 -0
  66. package/dist/utils/tenant.js +14 -0
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/dist-cjs/agent/capabilities.d.ts +39 -0
  70. package/dist-cjs/agent/capabilities.js +41 -0
  71. package/dist-cjs/agent/create-agent.d.ts +61 -11
  72. package/dist-cjs/agent/create-agent.js +136 -22
  73. package/dist-cjs/agent/index.d.ts +3 -2
  74. package/dist-cjs/agent/index.js +4 -4
  75. package/dist-cjs/agent/middleware/approval.d.ts +26 -0
  76. package/dist-cjs/agent/middleware/approval.js +26 -0
  77. package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  78. package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
  79. package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
  80. package/dist-cjs/agent/middleware/context-editor.js +70 -0
  81. package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
  82. package/dist-cjs/agent/middleware/filesystem.js +86 -0
  83. package/dist-cjs/agent/middleware/index.d.ts +19 -0
  84. package/dist-cjs/agent/middleware/index.js +28 -0
  85. package/dist-cjs/agent/middleware/limits.d.ts +42 -0
  86. package/dist-cjs/agent/middleware/limits.js +57 -0
  87. package/dist-cjs/agent/middleware/models.d.ts +48 -0
  88. package/dist-cjs/agent/middleware/models.js +48 -0
  89. package/dist-cjs/agent/middleware/pii.d.ts +67 -0
  90. package/dist-cjs/agent/middleware/pii.js +178 -0
  91. package/dist-cjs/agent/middleware/retry.d.ts +66 -0
  92. package/dist-cjs/agent/middleware/retry.js +63 -0
  93. package/dist-cjs/agent/middleware/shared.d.ts +41 -0
  94. package/dist-cjs/agent/middleware/shared.js +83 -0
  95. package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
  96. package/dist-cjs/agent/middleware/tool-selector.js +109 -0
  97. package/dist-cjs/agent/permissions.d.ts +113 -0
  98. package/dist-cjs/agent/permissions.js +189 -0
  99. package/dist-cjs/agent/sandbox.d.ts +151 -0
  100. package/dist-cjs/agent/sandbox.js +297 -0
  101. package/dist-cjs/agent/tool.d.ts +8 -3
  102. package/dist-cjs/agent/tool.js +14 -3
  103. package/dist-cjs/deep-agent/index.d.ts +109 -0
  104. package/dist-cjs/deep-agent/index.js +219 -0
  105. package/dist-cjs/graph/functional.d.ts +9 -0
  106. package/dist-cjs/graph/functional.js +20 -3
  107. package/dist-cjs/graph/graph.js +25 -4
  108. package/dist-cjs/graph/lint.d.ts +4 -1
  109. package/dist-cjs/graph/lint.js +35 -0
  110. package/dist-cjs/postgres/fulltext.d.ts +67 -0
  111. package/dist-cjs/postgres/fulltext.js +129 -0
  112. package/dist-cjs/rag/elasticsearch.d.ts +71 -0
  113. package/dist-cjs/rag/elasticsearch.js +135 -0
  114. package/dist-cjs/rag/pipeline.d.ts +140 -0
  115. package/dist-cjs/rag/pipeline.js +191 -0
  116. package/dist-cjs/rag/rerankers.d.ts +92 -0
  117. package/dist-cjs/rag/rerankers.js +107 -0
  118. package/dist-cjs/rag/retrievers.d.ts +12 -1
  119. package/dist-cjs/server/assistant.d.ts +5 -1
  120. package/dist-cjs/server/assistant.js +4 -2
  121. package/dist-cjs/server/auth.d.ts +250 -0
  122. package/dist-cjs/server/auth.js +402 -0
  123. package/dist-cjs/store/memory.d.ts +10 -0
  124. package/dist-cjs/store/memory.js +10 -0
  125. package/dist-cjs/types/graph.d.ts +38 -0
  126. package/dist-cjs/types/messages.d.ts +36 -2
  127. package/dist-cjs/types/principal.d.ts +21 -0
  128. package/dist-cjs/types/principal.js +2 -0
  129. package/dist-cjs/types/server.d.ts +2 -9
  130. package/dist-cjs/utils/tenant.d.ts +11 -0
  131. package/dist-cjs/utils/tenant.js +15 -0
  132. package/dist-cjs/version.d.ts +1 -1
  133. package/dist-cjs/version.js +1 -1
  134. package/llms.txt +4 -3
  135. package/package.json +118 -1
@@ -4,6 +4,8 @@ export class MemoryStore {
4
4
  options;
5
5
  items = new Map();
6
6
  maxItems;
7
+ expiring = false;
8
+ sweptAt = Number.NEGATIVE_INFINITY;
7
9
  now;
8
10
  constructor(options = {}) {
9
11
  this.options = options;
@@ -18,6 +20,8 @@ export class MemoryStore {
18
20
  const timestamp = this.now().toISOString();
19
21
  const existing = this.items.get(id);
20
22
  const vector = await this.embed(value, options.index);
23
+ if (options.ttlMs !== undefined)
24
+ this.expiring = true;
21
25
  this.items.delete(id);
22
26
  this.items.set(id, {
23
27
  namespace: [...namespace],
@@ -99,6 +103,12 @@ export class MemoryStore {
99
103
  return item.expiresAt !== undefined && Date.parse(item.expiresAt) <= this.now().getTime();
100
104
  }
101
105
  sweep() {
106
+ if (!this.expiring)
107
+ return;
108
+ const now = this.now().getTime();
109
+ if (now - this.sweptAt < 1_000 && this.items.size <= this.maxItems)
110
+ return;
111
+ this.sweptAt = now;
102
112
  for (const [id, item] of this.items)
103
113
  if (this.expired(item))
104
114
  this.items.delete(id);
@@ -8,6 +8,7 @@ import type { OperationLifecycleLike } from './lifecycle.js';
8
8
  * is, so a different process can pick it up.
9
9
  */
10
10
  import type { CacheAdapter } from '../cache/adapters.js';
11
+ import type { Principal } from './principal.js';
11
12
  import type { Store } from './store.js';
12
13
  /** Entry sentinel. An edge from `START` names the first node. */
13
14
  export declare const START = "__start__";
@@ -243,6 +244,14 @@ export interface NodeOptions<S extends ChannelSchema = ChannelSchema> {
243
244
  * that are not.
244
245
  */
245
246
  idempotent?: boolean;
247
+ /**
248
+ * What the node does to the world, as capabilities: `payments:charge`, `network:api.stripe.com`,
249
+ * `filesystem:write`. Declared for the linter, which reports a node with effects that also
250
+ * interrupts, since the node runs again from the top when the answer comes.
251
+ */
252
+ effects?: readonly string[];
253
+ /** True when the node may call `context.interrupt()`. Declared for the linter. */
254
+ interrupts?: boolean;
246
255
  /**
247
256
  * Nodes this one may reach through `Send` or a `Command`. Declaring them keeps compile-time
248
257
  * reachability checks and diagrams exact, so a node reached only that way is not reported as
@@ -374,6 +383,8 @@ export interface NodeContext<S extends ChannelSchema> {
374
383
  readonly threadId: string;
375
384
  /** The tenant the run is for, when it has one. */
376
385
  readonly tenantId?: string;
386
+ /** Who the run is for, when it was started with a principal: the caller the server authenticated. */
387
+ readonly principal?: Readonly<Principal>;
377
388
  /** Identifies this task. Equal to `node` unless the task came from a `Send`. */
378
389
  readonly taskId: string;
379
390
  /** Input carried by a `Send`. Undefined for a node reached through an ordinary edge. */
@@ -577,6 +588,14 @@ export interface GraphRunOptions {
577
588
  * `tenantId` is refused before anything runs. A run without one carries on the thread's tenant.
578
589
  */
579
590
  tenantId?: string;
591
+ /**
592
+ * Who the run is for, such as the caller the server authenticated. Nodes read it as
593
+ * `context.principal`, and an agent hands it to every tool and middleware. Its `tenantId` is the
594
+ * run's tenant when `tenantId` is not given, and a run whose two tenants differ is refused. Its
595
+ * `userId` is recorded on every checkpoint as `metadata.userId`; the rest is never stored, so a
596
+ * resumed run carries the principal of whoever resumed it.
597
+ */
598
+ principal?: Principal;
580
599
  /** Overrides the compiled `maxConcurrency` for this run. */
581
600
  maxConcurrency?: number;
582
601
  /** Breakpoints for this run only, replacing the compiled ones. */
@@ -853,6 +872,10 @@ export interface GraphDescription {
853
872
  idempotent?: boolean;
854
873
  /** True when the node reuses results through `cache`. */
855
874
  cache?: boolean;
875
+ /** The capabilities its work uses, when it declares them. */
876
+ effects?: string[];
877
+ /** True when it declares that it may interrupt. */
878
+ interrupts?: boolean;
856
879
  /** The graph this node runs, when it is a compiled graph used through `asNode()`. */
857
880
  subgraph?: GraphDescription;
858
881
  }>;
@@ -876,6 +899,19 @@ export interface GraphDescription {
876
899
  checkpointer?: 'memory' | 'custom' | 'none';
877
900
  /** Tasks a superstep runs at once. */
878
901
  maxConcurrency?: number;
902
+ /** The tools the graph's nodes call, when it was compiled with them, as an agent is. */
903
+ tools?: GraphToolDescription[];
904
+ }
905
+ /** A tool a graph calls, as the linter reads it. */
906
+ export interface GraphToolDescription {
907
+ /** The tool's name. */
908
+ name: string;
909
+ /** Its capabilities, when it declares a fixed list. */
910
+ capabilities?: string[];
911
+ /** True when it computes its capabilities from each call's arguments. */
912
+ dynamic?: boolean;
913
+ /** How its calls are approved: an interrupt for each, a permission policy, or not at all. */
914
+ approval: 'interrupt' | 'policy' | 'none';
879
915
  }
880
916
  /**
881
917
  * Options applied when a graph is compiled: stores, breakpoints, concurrency, retries, and failure
@@ -898,6 +934,8 @@ export interface CompileOptions {
898
934
  maxConcurrency?: number;
899
935
  /** Default retry policy for every node. A node's own policy wins. */
900
936
  retry?: RetryPolicy;
937
+ /** The tools the graph's nodes call, for `describe()` and the linter. `createAgent()` fills it. */
938
+ tools?: GraphToolDescription[];
901
939
  /**
902
940
  * Defaults for every node: a retry policy, timeouts, and an `onError`. A node's own options win,
903
941
  * field by field. `retry` here is the same as `compile({ retry })`, and wins over it.
@@ -1,5 +1,7 @@
1
1
  import type { CacheTtl, ReasoningEffort } from './providers.js';
2
2
  import type { AssetInput } from './images.js';
3
+ import type { Principal } from './principal.js';
4
+ import type { Store } from './store.js';
3
5
  declare global {
4
6
  namespace NodeJS {
5
7
  /**
@@ -128,8 +130,19 @@ export interface ToolDefinition {
128
130
  description: string;
129
131
  /** JSON Schema for the arguments. */
130
132
  parameters: Record<string, unknown>;
131
- /** Runs the tool, for the agent loop and executors that call tools automatically. */
132
- execute?: (args: Record<string, unknown>) => Promise<unknown>;
133
+ /**
134
+ * Runs the tool, for the agent loop and executors that call tools automatically. An agent passes
135
+ * a `ToolContext` too: who the run is for, its thread and tenant, its signal, and its store.
136
+ */
137
+ execute?: (args: Record<string, unknown>, context?: ToolContext) => Promise<unknown>;
138
+ /**
139
+ * What the tool does to the world, as capabilities a permission policy decides on:
140
+ * `filesystem:read` or `filesystem:write` with an optional path, `network` with an optional host,
141
+ * `shell` with an optional command, `code`, or a name of the application's own such as `payments`.
142
+ * A function computes them from each call's arguments, so a policy sees the path, host, or command
143
+ * the call actually uses. Never sent to a model.
144
+ */
145
+ capabilities?: readonly string[] | ((args: Record<string, unknown>) => readonly string[]);
133
146
  /**
134
147
  * Ends a cacheable prefix after this tool. Only honored when the request sets
135
148
  * `cache.mode: 'explicit'` and the provider accepts caller-placed breakpoints.
@@ -145,6 +158,27 @@ export interface ToolDefinition {
145
158
  export type ToolChoice = 'auto' | 'none' | 'required' | {
146
159
  name: string;
147
160
  };
161
+ /**
162
+ * What a tool receives besides its arguments when an agent runs it. Every field is optional: a tool
163
+ * run outside an agent, or by an executor given nothing, sees none of them.
164
+ */
165
+ export interface ToolContext {
166
+ /** The model's id for this call. */
167
+ toolCallId?: string;
168
+ /** The thread the run belongs to. */
169
+ threadId?: string;
170
+ /** The tenant the run is for. */
171
+ tenantId?: string;
172
+ /**
173
+ * Who the run is for: the caller the server authenticated, with their roles and scopes, so a
174
+ * tool can act as that user and nobody else.
175
+ */
176
+ principal?: Readonly<Principal>;
177
+ /** Aborted when the run is cancelled or the step times out. A tool that waits should stop with it. */
178
+ signal?: AbortSignal;
179
+ /** Long-term memory. In a tenant's run, the tenant's view of it. */
180
+ store?: Store;
181
+ }
148
182
  /** What a tool returned, correlated with the call that asked for it. */
149
183
  export interface ToolCallResult {
150
184
  /** The id of the call this answers. */
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Who a request, a run, or a tool call is for.
3
+ *
4
+ * One shape everywhere: the server's authentication hook returns it, a run carries it, graph nodes
5
+ * and workflow steps read it, tools and middleware receive it, and a permission policy can decide on
6
+ * it. Its tenant scopes threads, runs, budgets, and the store.
7
+ */
8
+ export interface Principal {
9
+ /** Isolates threads, runs, and cron jobs. Requests only ever see their own tenant's resources. */
10
+ tenantId?: string;
11
+ /** Who the caller is: a token's subject, a key's owner. Recorded on what they create. */
12
+ userId?: string;
13
+ /** What the caller may do. A route that names a scope refuses a principal without it. */
14
+ scopes?: readonly string[];
15
+ /** The caller's roles, for policies and tools that decide by role rather than by scope. */
16
+ roles?: readonly string[];
17
+ /** How the caller was authenticated: `jwt`, `api-key`, `proxy`, or a name of your own. */
18
+ method?: string;
19
+ /** The verified claims or attributes the principal was built from, for anything the fields above leave out. */
20
+ claims?: Readonly<Record<string, unknown>>;
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -6,15 +6,8 @@
6
6
  * graph thread, so state, interrupts, and history come from the checkpointer; and an assistant is
7
7
  * anything that can stream events for an input, which a compiled graph already does.
8
8
  */
9
- /** Who is making a request, as the server's authentication hook reports them. */
10
- export interface Principal {
11
- /** Isolates threads, runs, and cron jobs. Requests only ever see their own tenant's resources. */
12
- tenantId?: string;
13
- /** Who the caller is, recorded on what they create. */
14
- userId?: string;
15
- /** What the caller may do. A route that names a scope refuses a principal without it. */
16
- scopes?: readonly string[];
17
- }
9
+ import type { Principal } from './principal.js';
10
+ export type { Principal };
18
11
  /** Where a run stands, as the server reports it. */
19
12
  export type RunStatus = 'queued' | 'running' | 'awaiting_input' | 'succeeded' | 'failed' | 'cancelled' | 'expired';
20
13
  /** What happens when a run is requested for a thread that is already running one. */
@@ -1,3 +1,4 @@
1
+ import type { Principal } from '../types/principal.js';
1
2
  /**
2
3
  * The tenant id rule every scope shares. An id is letters, digits, `_`, and `-`, so it can never hold
3
4
  * the `/` a scoped name or key is built with: no tenant's prefix is a prefix of another's.
@@ -12,3 +13,13 @@ export declare class TenantIdError extends RangeError {
12
13
  export declare function assertTenantId(tenantId: string): string;
13
14
  /** @internal The prefix a tenant's names and keys carry in a shared store. */
14
15
  export declare function tenantPrefix(tenantId: string): string;
16
+ /**
17
+ * Run options with the principal's tenant applied: the principal's `tenantId` becomes the run's when
18
+ * it has none, a run whose two tenants differ is refused, and the principal's `userId` is recorded
19
+ * in the run's metadata.
20
+ */
21
+ export declare function withPrincipal<O extends {
22
+ tenantId?: string;
23
+ principal?: Principal;
24
+ metadata?: Record<string, unknown>;
25
+ }>(runOptions: O): O;
@@ -14,3 +14,17 @@ export function assertTenantId(tenantId) {
14
14
  export function tenantPrefix(tenantId) {
15
15
  return `tenant/${assertTenantId(tenantId)}/`;
16
16
  }
17
+ export function withPrincipal(runOptions) {
18
+ const principal = runOptions.principal;
19
+ if (!principal)
20
+ return runOptions;
21
+ const tenantId = principal.tenantId;
22
+ if (tenantId !== undefined && runOptions.tenantId !== undefined && tenantId !== runOptions.tenantId) {
23
+ throw new TypeError("The run's tenantId and its principal's tenant differ");
24
+ }
25
+ return {
26
+ ...runOptions,
27
+ ...(tenantId !== undefined && runOptions.tenantId === undefined ? { tenantId } : {}),
28
+ ...(principal.userId !== undefined ? { metadata: { ...runOptions.metadata, userId: principal.userId } } : {}),
29
+ };
30
+ }
package/dist/version.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * This package's version, recorded where provenance matters, such as on every experiment. A test
3
3
  * keeps it equal to `package.json`.
4
4
  */
5
- export declare const NEXUS_VERSION = "2.2.0";
5
+ export declare const NEXUS_VERSION = "2.3.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const NEXUS_VERSION = '2.2.0';
1
+ export const NEXUS_VERSION = '2.3.0';
@@ -0,0 +1,39 @@
1
+ import type { ToolDefinition } from '../types/messages.js';
2
+ /**
3
+ * Capabilities: what a tool does to the world, written as strings a policy can decide on.
4
+ *
5
+ * | Capability | Means |
6
+ * | --- | --- |
7
+ * | `filesystem:read`, `filesystem:read:<path>` | Reads files, or one path |
8
+ * | `filesystem:write`, `filesystem:write:<path>` | Writes, creates, or deletes files |
9
+ * | `network`, `network:<host or URL>` | Opens a network connection, to a host when given |
10
+ * | `shell`, `shell:<command line>` | Runs a shell command |
11
+ * | `code` | Runs code it was given, as an interpreter does |
12
+ * | `<name>`, `<name>:<detail>` | Anything the application names, such as `payments:refund` |
13
+ *
14
+ * A tool that declares `capabilities: []` does nothing to the world; one that declares none at all
15
+ * is undeclared, and a policy decides on it as such.
16
+ */
17
+ /** One capability, split into its parts. */
18
+ export interface ParsedCapability {
19
+ /** `filesystem`, `network`, `shell`, `code`, or the application's own name. */
20
+ kind: string;
21
+ /** `read` or `write`, for the filesystem. */
22
+ access?: 'read' | 'write';
23
+ /** The path, host, command, or detail, when the capability names one. */
24
+ target?: string;
25
+ }
26
+ /** Splits a capability string into its kind, access, and target. */
27
+ export declare function parseCapability(capability: string): ParsedCapability;
28
+ /**
29
+ * The capabilities one call of a tool declares: its static list, or what its function computes from
30
+ * the call's arguments. `undefined` means undeclared — no list, or a function that threw — which is
31
+ * not the same as `[]`, a tool that declares it does nothing to the world.
32
+ */
33
+ export declare function capabilitiesOf(tool: Pick<ToolDefinition, 'capabilities'> | undefined, args: Record<string, unknown>): string[] | undefined;
34
+ /**
35
+ * Whether a capability is sensitive: anything but reading files. A pure tool declares
36
+ * `capabilities: []`; whatever it does declare, a write, a command, code, the network, or an
37
+ * effect of the application's own such as `payments`, changes the world or leaves the process.
38
+ */
39
+ export declare function isSensitiveCapability(capability: string): boolean;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseCapability = parseCapability;
4
+ exports.capabilitiesOf = capabilitiesOf;
5
+ exports.isSensitiveCapability = isSensitiveCapability;
6
+ function parseCapability(capability) {
7
+ const text = capability.trim();
8
+ const colon = text.indexOf(':');
9
+ const kind = (colon < 0 ? text : text.slice(0, colon)).toLowerCase();
10
+ const rest = colon < 0 ? undefined : text.slice(colon + 1);
11
+ if (kind === 'filesystem') {
12
+ if (!rest)
13
+ return { kind };
14
+ const [access, ...path] = rest.split(':');
15
+ const parsed = { kind };
16
+ if (access === 'read' || access === 'write')
17
+ parsed.access = access;
18
+ const target = parsed.access ? path.join(':') : rest;
19
+ if (target)
20
+ parsed.target = target;
21
+ return parsed;
22
+ }
23
+ return rest === undefined || rest === '' ? { kind } : { kind, target: rest };
24
+ }
25
+ function capabilitiesOf(tool, args) {
26
+ const declared = tool?.capabilities;
27
+ if (!declared)
28
+ return undefined;
29
+ if (typeof declared !== 'function')
30
+ return [...declared];
31
+ try {
32
+ return [...declared(args)];
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
38
+ function isSensitiveCapability(capability) {
39
+ const parsed = parseCapability(capability);
40
+ return !(parsed.kind === 'filesystem' && parsed.access === 'read');
41
+ }
@@ -1,9 +1,11 @@
1
1
  import type { OperationLifecycleLike } from '../types/lifecycle.js';
2
2
  import { type CompiledGraph } from '../graph/graph.js';
3
- import type { GraphCheckpointer, NodeContext } from '../types/graph.js';
3
+ import type { GraphCheckpointer, InterruptRequest, NodeContext } from '../types/graph.js';
4
4
  import type { CompletionRequest, Message, ToolDefinition } from '../types/messages.js';
5
5
  import type { NexusResponse, StreamChunk } from '../types/response.js';
6
+ import type { Principal } from '../types/principal.js';
6
7
  import type { Store } from '../types/store.js';
8
+ import type { PermissionPolicyLike } from './permissions.js';
7
9
  /**
8
10
  * An agent as a graph.
9
11
  *
@@ -28,6 +30,10 @@ export interface AgentToolCall {
28
30
  name: string;
29
31
  /** Arguments, parsed from the model's JSON. */
30
32
  args: Record<string, unknown>;
33
+ /** What the call declares it does, when the tool declares capabilities. */
34
+ capabilities?: readonly string[];
35
+ /** Why the permission policy asked for approval, when it did. */
36
+ permission?: string;
31
37
  }
32
38
  /** What an approver may answer: allow, refuse with a reason, or allow with corrected arguments. */
33
39
  export type AgentApproval = boolean | {
@@ -42,24 +48,58 @@ export interface AgentApprovalPolicy {
42
48
  /** The question an operator sees. Defaults to naming the tool and its arguments. */
43
49
  reason?: (call: AgentToolCall) => string;
44
50
  }
51
+ /** What every middleware hook can read about the step it runs in. */
52
+ export interface AgentMiddlewareContext {
53
+ /** The agent's state at this step: the whole transcript, tool calls and results included. */
54
+ state: AgentState;
55
+ /** Long-term memory, when the agent has a store. */
56
+ store?: Store;
57
+ /** The thread the run belongs to. */
58
+ threadId: string;
59
+ /** The tenant the run is for, when it has one. */
60
+ tenantId?: string;
61
+ /** Who the run is for, when it has a principal: the caller the server authenticated. */
62
+ principal?: Readonly<Principal>;
63
+ /** Aborted when the run is cancelled or the step times out. A hook that waits should stop with it. */
64
+ signal: AbortSignal;
65
+ /**
66
+ * Suspends the run until a person answers, as `interruptOn` does. When the run resumes, the step
67
+ * runs again and this returns the answer instead of suspending.
68
+ */
69
+ interrupt<T = unknown>(request: InterruptRequest): T;
70
+ }
71
+ /** What `wrapModelCall` can read, and how it ends the run without calling the model. */
72
+ export interface AgentModelCallContext extends AgentMiddlewareContext {
73
+ /**
74
+ * Ends the run with this answer and `stopReason: 'stopped'`, without another model call. Return
75
+ * what it returns: `return context.stop('The model call limit was reached.')`.
76
+ */
77
+ stop(answer: string): NexusResponse;
78
+ }
45
79
  /** Hooks around the agent's model calls and tool calls. Each hook is optional. */
46
80
  export interface AgentMiddleware {
47
81
  /** Names the middleware in traces and errors. */
48
82
  name?: string;
49
83
  /** Adjusts the request before it is sent: trim history, add context, swap the model. */
50
- beforeModel?(context: {
84
+ beforeModel?(context: AgentMiddlewareContext & {
51
85
  request: CompletionRequest;
52
- state: AgentState;
53
- store?: Store;
54
86
  }): CompletionRequest | void | Promise<CompletionRequest | void>;
55
87
  /** Inspects or replaces the response: redact, validate, count. */
56
- afterModel?(context: {
88
+ afterModel?(context: AgentMiddlewareContext & {
57
89
  response: NexusResponse;
58
- state: AgentState;
59
- store?: Store;
60
90
  }): NexusResponse | void | Promise<NexusResponse | void>;
61
- /** Wraps a tool call, so a policy can log it, time it, or refuse it. */
62
- wrapToolCall?(call: AgentToolCall, next: () => Promise<AgentToolResult>): Promise<AgentToolResult> | AgentToolResult;
91
+ /**
92
+ * Wraps the model call itself, after every `beforeModel` and before every `afterModel`. `next`
93
+ * sends a request and may be called again, with the same request or a changed one, to retry or to
94
+ * fall back to another model. The first middleware is the outermost.
95
+ */
96
+ wrapModelCall?(request: CompletionRequest, next: (request: CompletionRequest) => Promise<NexusResponse>, context: AgentModelCallContext): Promise<NexusResponse> | NexusResponse;
97
+ /**
98
+ * Wraps a tool call, so a policy can log it, time it, retry it, or refuse it. `next` runs the
99
+ * rest of the chain and may be called again; given a call, it runs that call's arguments instead,
100
+ * and the permission policy decides them again first. The first middleware is the outermost.
101
+ */
102
+ wrapToolCall?(call: AgentToolCall, next: (call?: AgentToolCall) => Promise<AgentToolResult>, context: AgentMiddlewareContext): Promise<AgentToolResult> | AgentToolResult;
63
103
  }
64
104
  /** What a tool call produced. */
65
105
  export interface AgentToolResult {
@@ -88,6 +128,13 @@ export interface CreateAgentOptions {
88
128
  interruptOn?: Record<string, AgentApprovalPolicy | true>;
89
129
  /** Hooks around model and tool calls, applied in order. */
90
130
  middleware?: AgentMiddleware[];
131
+ /**
132
+ * Decides every tool call from the capabilities it declares, before it runs: allow it, deny it,
133
+ * or interrupt for a person's approval, as `interruptOn` does. A denied call reaches the model as a
134
+ * failed tool result naming the reason, and never runs. See `permissionPolicy()` in
135
+ * `nexus-ai-pro/agent/permissions`.
136
+ */
137
+ permissions?: PermissionPolicyLike;
91
138
  /** Long-term memory, available to tools and middleware as `store`. */
92
139
  store?: Store;
93
140
  /**
@@ -136,8 +183,11 @@ export type AgentState = {
136
183
  /** Why the agent stopped. */
137
184
  stopReason?: AgentStopReason;
138
185
  };
139
- /** `completed` when the model answered, `max_iterations` when it ran out of model calls. */
140
- export type AgentStopReason = 'completed' | 'max_iterations';
186
+ /**
187
+ * `completed` when the model answered, `max_iterations` when it ran out of model calls, `stopped`
188
+ * when a middleware ended the run through `context.stop()`.
189
+ */
190
+ export type AgentStopReason = 'completed' | 'max_iterations' | 'stopped';
141
191
  /** An agent: a compiled graph over the agent's channels. */
142
192
  export type AgentGraph = CompiledGraph<AgentChannels>;
143
193
  /** Wraps a question into the state an agent starts from. */