@dudousxd/nestjs-agent-core 0.34.0 → 0.35.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.
@@ -0,0 +1,119 @@
1
+ import { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { a as Actor, P as PageContext } from './stream-events-CgWqAI-1.js';
3
+
4
+ /**
5
+ * Per-invocation context handed to a tool handler. Host-supplied bits are optional. Identity lives
6
+ * on {@link AiToolCtx.actor} — read `ctx.actor.id` / `ctx.actor.tenantRef` (single source of truth;
7
+ * no denormalized copies).
8
+ */
9
+ interface AiToolCtx {
10
+ actor: Actor;
11
+ threadId: string;
12
+ runId: string;
13
+ requestId: string;
14
+ /**
15
+ * The id of the tool call this invocation serves. Absent where a tool is invoked outside a turn
16
+ * (the MCP server, a direct `registry.invoke`).
17
+ */
18
+ toolCallId?: string;
19
+ /**
20
+ * `<runId>:<toolCallId>` — the same value for every execution of THIS call, and for no other.
21
+ *
22
+ * A tool's side effect and the checkpoint that records it are two writes. Under the durable
23
+ * runner a worker that dies between them leaves a call the journal does not know ran, and the
24
+ * runtime's recovery runs it again; an in-step transient retry (a deadlock, a lock-wait timeout)
25
+ * re-invokes it too. The library cannot make your write atomic with its journal — so it hands you
26
+ * the key that makes the second attempt recognisable: pass it to whatever you call as its
27
+ * idempotency key (a payment provider's `Idempotency-Key`, a unique column on the row you insert,
28
+ * a workflow's `id`), and a re-execution lands on the first one's result instead of doing it
29
+ * twice. Stable across replays and across pods: the run id is the run's own, and the call id
30
+ * comes out of the journaled model step.
31
+ *
32
+ * Absent where a tool is invoked outside a turn (the MCP server, a direct `registry.invoke`).
33
+ */
34
+ idempotencyKey?: string;
35
+ /** The name of the agent running this turn — provenance a tool can scope on (e.g. capability sets). */
36
+ agentName?: string;
37
+ pageContext?: PageContext;
38
+ /** Optional host handle (e.g. an ORM EntityManager) the app threads through options. */
39
+ host?: unknown;
40
+ /**
41
+ * Push a component into the assistant message: streamed live as a `ui` frame and persisted on
42
+ * the message, so a reload shows it where the live stream did. Resolves to the component's id.
43
+ *
44
+ * `id` defaults to `<toolCallId>:ui:<n>` (the n-th push without an `id` in this invocation), so a retried or
45
+ * re-executed call REPLACES what it pushed before instead of adding a second copy; pass your own
46
+ * `id` to update one component across pushes (streaming rows into a table). `props` must be
47
+ * JSON; it is snapshotted when pushed.
48
+ *
49
+ * Replay-safe under the durable runner: the pushed components ride the tool step's journaled
50
+ * result, so a replay neither streams nor persists them again.
51
+ *
52
+ * Always present. On a surface with no conversation to push into (the MCP server, a direct
53
+ * `registry.invoke` without one) it is a no-op that still resolves to an id, so a tool calls
54
+ * `ctx.emitUi(…)` unconditionally.
55
+ */
56
+ emitUi(component: string, props: Record<string, unknown>, options?: {
57
+ id?: string;
58
+ version?: number;
59
+ }): Promise<{
60
+ id: string;
61
+ }>;
62
+ }
63
+ /** A tool implementation. `I` is the parsed (Zod-validated) input. */
64
+ interface ToolHandler<I = unknown> {
65
+ execute(input: I, ctx: AiToolCtx): Promise<unknown>;
66
+ /**
67
+ * Whether this tool exists in this deployment at all — evaluated per turn, BEFORE the roles
68
+ * policy, so a `false` here means the model is never shown the tool rather than being shown one
69
+ * it will be refused. Omit → always enabled.
70
+ *
71
+ * This is the seam for a feature flag or a licensing tier: the handler is an ordinary provider,
72
+ * so it can read injected config (`this.config.featureX`) that a decorator, evaluated at import
73
+ * time, cannot. Answering "does this capability exist here?"; `roles`/`RolesPolicy` answers the
74
+ * separate question "may THIS actor use it?", and both still run.
75
+ *
76
+ * Prefer this over conditionally registering the provider: registration happens while the
77
+ * `@Module` metadata is built, which in most apps is before configuration is loaded.
78
+ */
79
+ isEnabled?(): boolean | Promise<boolean>;
80
+ /**
81
+ * Whether THIS actor may use the tool, decided per turn. Omit → the role gate alone decides.
82
+ *
83
+ * The three existing gates all answer the question somewhere else: `roles` is static data,
84
+ * `RolesPolicy` is one app-wide rule for every tool, and an agent's `tools` allow-list is fixed
85
+ * when the agent is declared. This one lives on the tool and runs with DI, so it can ask the
86
+ * questions only the tool knows to ask — is this user's org on the plan that includes it, does
87
+ * this actor own the base being queried, is the per-user override in the DB set today.
88
+ *
89
+ * Runs AFTER {@link isEnabled} and the `RolesPolicy`, and all of them must pass. Applied both
90
+ * when the turn's tool list is built (a denied actor is never shown it) and again on invoke.
91
+ */
92
+ canUse?(actor: Actor): boolean | Promise<boolean>;
93
+ /**
94
+ * What the model is told about this tool for THIS turn — a description and/or input schema that
95
+ * depend on who is asking (a per-tenant component catalog, a per-plan list of options). Called
96
+ * when the turn's tool list is built, after every gate has passed; whatever it returns replaces
97
+ * the registered spec's `description` / `inputSchema` in the definition the model sees. Omit, or
98
+ * return `undefined`, to use the registered spec as is.
99
+ *
100
+ * It shapes what the model is SHOWN only: the registry still validates a call against the
101
+ * registered `inputSchema`, so a tool whose accepted input varies per turn registers a permissive
102
+ * schema and validates in `execute`.
103
+ */
104
+ describe?(scope: ToolDescribeScope): ToolDescription | undefined | Promise<ToolDescription | undefined>;
105
+ }
106
+ /** Who a turn's tool list is being built for — what {@link ToolHandler.describe} can vary on. */
107
+ interface ToolDescribeScope {
108
+ actor: Actor;
109
+ /** Absent where the list is built outside a conversation (the MCP server's `tools/list`). */
110
+ threadId?: string;
111
+ agentName?: string;
112
+ }
113
+ /** A per-turn override of a tool's model-facing definition ({@link ToolHandler.describe}). */
114
+ interface ToolDescription {
115
+ description?: string;
116
+ inputSchema?: StandardSchemaV1;
117
+ }
118
+
119
+ export type { AiToolCtx as A, ToolHandler as T, ToolDescribeScope as a, ToolDescription as b };
@@ -0,0 +1,119 @@
1
+ import { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { a as Actor, P as PageContext } from './stream-events-CgWqAI-1.cjs';
3
+
4
+ /**
5
+ * Per-invocation context handed to a tool handler. Host-supplied bits are optional. Identity lives
6
+ * on {@link AiToolCtx.actor} — read `ctx.actor.id` / `ctx.actor.tenantRef` (single source of truth;
7
+ * no denormalized copies).
8
+ */
9
+ interface AiToolCtx {
10
+ actor: Actor;
11
+ threadId: string;
12
+ runId: string;
13
+ requestId: string;
14
+ /**
15
+ * The id of the tool call this invocation serves. Absent where a tool is invoked outside a turn
16
+ * (the MCP server, a direct `registry.invoke`).
17
+ */
18
+ toolCallId?: string;
19
+ /**
20
+ * `<runId>:<toolCallId>` — the same value for every execution of THIS call, and for no other.
21
+ *
22
+ * A tool's side effect and the checkpoint that records it are two writes. Under the durable
23
+ * runner a worker that dies between them leaves a call the journal does not know ran, and the
24
+ * runtime's recovery runs it again; an in-step transient retry (a deadlock, a lock-wait timeout)
25
+ * re-invokes it too. The library cannot make your write atomic with its journal — so it hands you
26
+ * the key that makes the second attempt recognisable: pass it to whatever you call as its
27
+ * idempotency key (a payment provider's `Idempotency-Key`, a unique column on the row you insert,
28
+ * a workflow's `id`), and a re-execution lands on the first one's result instead of doing it
29
+ * twice. Stable across replays and across pods: the run id is the run's own, and the call id
30
+ * comes out of the journaled model step.
31
+ *
32
+ * Absent where a tool is invoked outside a turn (the MCP server, a direct `registry.invoke`).
33
+ */
34
+ idempotencyKey?: string;
35
+ /** The name of the agent running this turn — provenance a tool can scope on (e.g. capability sets). */
36
+ agentName?: string;
37
+ pageContext?: PageContext;
38
+ /** Optional host handle (e.g. an ORM EntityManager) the app threads through options. */
39
+ host?: unknown;
40
+ /**
41
+ * Push a component into the assistant message: streamed live as a `ui` frame and persisted on
42
+ * the message, so a reload shows it where the live stream did. Resolves to the component's id.
43
+ *
44
+ * `id` defaults to `<toolCallId>:ui:<n>` (the n-th push without an `id` in this invocation), so a retried or
45
+ * re-executed call REPLACES what it pushed before instead of adding a second copy; pass your own
46
+ * `id` to update one component across pushes (streaming rows into a table). `props` must be
47
+ * JSON; it is snapshotted when pushed.
48
+ *
49
+ * Replay-safe under the durable runner: the pushed components ride the tool step's journaled
50
+ * result, so a replay neither streams nor persists them again.
51
+ *
52
+ * Always present. On a surface with no conversation to push into (the MCP server, a direct
53
+ * `registry.invoke` without one) it is a no-op that still resolves to an id, so a tool calls
54
+ * `ctx.emitUi(…)` unconditionally.
55
+ */
56
+ emitUi(component: string, props: Record<string, unknown>, options?: {
57
+ id?: string;
58
+ version?: number;
59
+ }): Promise<{
60
+ id: string;
61
+ }>;
62
+ }
63
+ /** A tool implementation. `I` is the parsed (Zod-validated) input. */
64
+ interface ToolHandler<I = unknown> {
65
+ execute(input: I, ctx: AiToolCtx): Promise<unknown>;
66
+ /**
67
+ * Whether this tool exists in this deployment at all — evaluated per turn, BEFORE the roles
68
+ * policy, so a `false` here means the model is never shown the tool rather than being shown one
69
+ * it will be refused. Omit → always enabled.
70
+ *
71
+ * This is the seam for a feature flag or a licensing tier: the handler is an ordinary provider,
72
+ * so it can read injected config (`this.config.featureX`) that a decorator, evaluated at import
73
+ * time, cannot. Answering "does this capability exist here?"; `roles`/`RolesPolicy` answers the
74
+ * separate question "may THIS actor use it?", and both still run.
75
+ *
76
+ * Prefer this over conditionally registering the provider: registration happens while the
77
+ * `@Module` metadata is built, which in most apps is before configuration is loaded.
78
+ */
79
+ isEnabled?(): boolean | Promise<boolean>;
80
+ /**
81
+ * Whether THIS actor may use the tool, decided per turn. Omit → the role gate alone decides.
82
+ *
83
+ * The three existing gates all answer the question somewhere else: `roles` is static data,
84
+ * `RolesPolicy` is one app-wide rule for every tool, and an agent's `tools` allow-list is fixed
85
+ * when the agent is declared. This one lives on the tool and runs with DI, so it can ask the
86
+ * questions only the tool knows to ask — is this user's org on the plan that includes it, does
87
+ * this actor own the base being queried, is the per-user override in the DB set today.
88
+ *
89
+ * Runs AFTER {@link isEnabled} and the `RolesPolicy`, and all of them must pass. Applied both
90
+ * when the turn's tool list is built (a denied actor is never shown it) and again on invoke.
91
+ */
92
+ canUse?(actor: Actor): boolean | Promise<boolean>;
93
+ /**
94
+ * What the model is told about this tool for THIS turn — a description and/or input schema that
95
+ * depend on who is asking (a per-tenant component catalog, a per-plan list of options). Called
96
+ * when the turn's tool list is built, after every gate has passed; whatever it returns replaces
97
+ * the registered spec's `description` / `inputSchema` in the definition the model sees. Omit, or
98
+ * return `undefined`, to use the registered spec as is.
99
+ *
100
+ * It shapes what the model is SHOWN only: the registry still validates a call against the
101
+ * registered `inputSchema`, so a tool whose accepted input varies per turn registers a permissive
102
+ * schema and validates in `execute`.
103
+ */
104
+ describe?(scope: ToolDescribeScope): ToolDescription | undefined | Promise<ToolDescription | undefined>;
105
+ }
106
+ /** Who a turn's tool list is being built for — what {@link ToolHandler.describe} can vary on. */
107
+ interface ToolDescribeScope {
108
+ actor: Actor;
109
+ /** Absent where the list is built outside a conversation (the MCP server's `tools/list`). */
110
+ threadId?: string;
111
+ agentName?: string;
112
+ }
113
+ /** A per-turn override of a tool's model-facing definition ({@link ToolHandler.describe}). */
114
+ interface ToolDescription {
115
+ description?: string;
116
+ inputSchema?: StandardSchemaV1;
117
+ }
118
+
119
+ export type { AiToolCtx as A, ToolHandler as T, ToolDescribeScope as a, ToolDescription as b };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dudousxd/nestjs-agent-core",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/DavideCarvalho/nestjs-agent.git",
@@ -64,6 +64,16 @@
64
64
  "types": "./dist/genui/builtins.d.cts",
65
65
  "default": "./dist/genui/builtins.cjs"
66
66
  }
67
+ },
68
+ "./ag-ui": {
69
+ "import": {
70
+ "types": "./dist/ag-ui/index.d.ts",
71
+ "default": "./dist/ag-ui/index.js"
72
+ },
73
+ "require": {
74
+ "types": "./dist/ag-ui/index.d.cts",
75
+ "default": "./dist/ag-ui/index.cjs"
76
+ }
67
77
  }
68
78
  },
69
79
  "files": [
@@ -74,7 +84,10 @@
74
84
  "@standard-schema/spec": "1.1.0"
75
85
  },
76
86
  "devDependencies": {
87
+ "@ag-ui/client": "1.0.1",
88
+ "@ag-ui/core": "1.0.1",
77
89
  "ajv": "8.20.0",
90
+ "rxjs": "7.8.2",
78
91
  "tsup": "8.5.1",
79
92
  "typescript": "5.9.3",
80
93
  "zod": "3.25.76"