@dudousxd/nestjs-agent 1.27.1 → 1.28.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,439 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/a2ui/index.ts
5
+ export * from "@dudousxd/nestjs-agent-core/a2ui";
6
+
7
+ // src/a2ui/a2ui.adapter.ts
8
+ import { AGENT_ACTOR_RESOLVER } from "@dudousxd/nestjs-agent-core";
9
+ import { A2UI_APPROVE_ACTION, A2UI_REJECT_ACTION, A2uiProjector, readA2uiAction } from "@dudousxd/nestjs-agent-core/a2ui";
10
+ import { agUiEvents, agUiFramesFromNdjson, decodeInterruptId } from "@dudousxd/nestjs-agent-core/ag-ui";
11
+ import { uiActionText, validateUiCapabilities } from "@dudousxd/nestjs-agent-core/genui";
12
+ import { BadRequestException, Body, Controller, ForbiddenException, HttpCode, Inject as Inject2, Optional, Post, Req, Res } from "@nestjs/common";
13
+
14
+ // src/agent-service.token.ts
15
+ var AGENT_SERVICE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:agent-service");
16
+
17
+ // src/genui/agent-genui.module.ts
18
+ import { defineCatalog, defineSandbox, genuiTools } from "@dudousxd/nestjs-agent-core/genui";
19
+ import { Inject, Module } from "@nestjs/common";
20
+
21
+ // src/functional-tool.ts
22
+ import { createFunctionalTool } from "@dudousxd/nestjs-agent-core";
23
+ var AGENT_TOOL_BRAND = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:functional-tool");
24
+ function brand(tool) {
25
+ return {
26
+ [AGENT_TOOL_BRAND]: true,
27
+ spec: tool.spec,
28
+ handler: tool.handler
29
+ };
30
+ }
31
+ __name(brand, "brand");
32
+ function provideAgentTools(factory, inject) {
33
+ return {
34
+ provide: /* @__PURE__ */ Symbol("nestjs-agent:functional-tool-list"),
35
+ useFactory: /* @__PURE__ */ __name(async (...deps) => (await factory(...deps)).map(brand), "useFactory"),
36
+ inject: inject ?? []
37
+ };
38
+ }
39
+ __name(provideAgentTools, "provideAgentTools");
40
+
41
+ // src/genui/agent-genui.module.ts
42
+ function _ts_decorate(decorators, target, key, desc) {
43
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
44
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
45
+ r = Reflect.decorate(decorators, target, key, desc);
46
+ } else {
47
+ for (var i = decorators.length - 1; i >= 0; i--) {
48
+ if (d = decorators[i]) {
49
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
50
+ }
51
+ }
52
+ }
53
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
54
+ }
55
+ __name(_ts_decorate, "_ts_decorate");
56
+ var GENUI_CATALOG = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:genui-catalog");
57
+ var GENUI_OPTIONS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:genui-options");
58
+ var GenuiCatalogResolver = class {
59
+ static {
60
+ __name(this, "GenuiCatalogResolver");
61
+ }
62
+ };
63
+ function resolverProviders(resolver) {
64
+ if (resolver === void 0) return [];
65
+ return [
66
+ typeof resolver === "function" ? {
67
+ provide: GenuiCatalogResolver,
68
+ useClass: resolver
69
+ } : {
70
+ provide: GenuiCatalogResolver,
71
+ useValue: resolver
72
+ }
73
+ ];
74
+ }
75
+ __name(resolverProviders, "resolverProviders");
76
+ function toolOptions(options, resolver) {
77
+ const { catalog: _catalog, sandbox: _sandbox, ...rest } = options;
78
+ return {
79
+ ...rest,
80
+ ...resolver !== void 0 ? {
81
+ resolveCatalog: /* @__PURE__ */ __name((scope) => resolver.resolve(scope), "resolveCatalog")
82
+ } : {}
83
+ };
84
+ }
85
+ __name(toolOptions, "toolOptions");
86
+ function withSandbox(options) {
87
+ const base = options.catalog ?? defineCatalog([]);
88
+ const { sandbox } = options;
89
+ return sandbox === void 0 || sandbox === false ? base : base.extend([
90
+ defineSandbox(sandbox === true ? {} : sandbox)
91
+ ]);
92
+ }
93
+ __name(withSandbox, "withSandbox");
94
+ function coreProviders(resolver) {
95
+ return [
96
+ {
97
+ provide: GENUI_CATALOG,
98
+ useFactory: /* @__PURE__ */ __name((options) => withSandbox(options), "useFactory"),
99
+ inject: [
100
+ GENUI_OPTIONS
101
+ ]
102
+ },
103
+ ...resolverProviders(resolver),
104
+ // The generated tools, registered by the agent module's discovery like any `provideAgentTool`.
105
+ // Built from the injected catalog, so `overrideProvider(GENUI_CATALOG)` changes them too.
106
+ provideAgentTools((options, catalog, perRequest) => genuiTools(catalog, toolOptions(options, perRequest)), [
107
+ GENUI_OPTIONS,
108
+ GENUI_CATALOG,
109
+ {
110
+ token: GenuiCatalogResolver,
111
+ optional: true
112
+ }
113
+ ])
114
+ ];
115
+ }
116
+ __name(coreProviders, "coreProviders");
117
+ var EXPORTS = [
118
+ GENUI_CATALOG,
119
+ GENUI_OPTIONS
120
+ ];
121
+ var AgentGenuiModule = class _AgentGenuiModule {
122
+ static {
123
+ __name(this, "AgentGenuiModule");
124
+ }
125
+ static forRoot(options = {}) {
126
+ const { resolver, ...rest } = options;
127
+ return {
128
+ module: _AgentGenuiModule,
129
+ global: true,
130
+ providers: [
131
+ {
132
+ provide: GENUI_OPTIONS,
133
+ useValue: rest
134
+ },
135
+ ...coreProviders(resolver)
136
+ ],
137
+ exports: resolver === void 0 ? EXPORTS : [
138
+ ...EXPORTS,
139
+ GenuiCatalogResolver
140
+ ]
141
+ };
142
+ }
143
+ static forRootAsync(options) {
144
+ return {
145
+ module: _AgentGenuiModule,
146
+ global: true,
147
+ imports: options.imports ?? [],
148
+ providers: [
149
+ {
150
+ provide: GENUI_OPTIONS,
151
+ useFactory: options.useFactory,
152
+ inject: options.inject ?? []
153
+ },
154
+ ...coreProviders(options.resolver)
155
+ ],
156
+ exports: options.resolver === void 0 ? EXPORTS : [
157
+ ...EXPORTS,
158
+ GenuiCatalogResolver
159
+ ]
160
+ };
161
+ }
162
+ };
163
+ AgentGenuiModule = _ts_decorate([
164
+ Module({})
165
+ ], AgentGenuiModule);
166
+
167
+ // src/a2ui/a2ui.adapter.ts
168
+ function _ts_decorate2(decorators, target, key, desc) {
169
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
170
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
171
+ r = Reflect.decorate(decorators, target, key, desc);
172
+ } else {
173
+ for (var i = decorators.length - 1; i >= 0; i--) {
174
+ if (d = decorators[i]) {
175
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
176
+ }
177
+ }
178
+ }
179
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
180
+ }
181
+ __name(_ts_decorate2, "_ts_decorate");
182
+ function _ts_metadata(metadataKey, metadataValue) {
183
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
184
+ return Reflect.metadata(metadataKey, metadataValue);
185
+ }
186
+ }
187
+ __name(_ts_metadata, "_ts_metadata");
188
+ function _ts_param(paramIndex, decorator) {
189
+ return function(target, key) {
190
+ decorator(target, key, paramIndex);
191
+ };
192
+ }
193
+ __name(_ts_param, "_ts_param");
194
+ var A2UI_VIA = "a2ui";
195
+ function isRecord(value) {
196
+ return typeof value === "object" && value !== null && !Array.isArray(value);
197
+ }
198
+ __name(isRecord, "isRecord");
199
+ function invalid(code, message) {
200
+ return new BadRequestException({
201
+ statusCode: 400,
202
+ code,
203
+ message
204
+ });
205
+ }
206
+ __name(invalid, "invalid");
207
+ var A2uiRunHandler = class {
208
+ static {
209
+ __name(this, "A2uiRunHandler");
210
+ }
211
+ agent;
212
+ actorResolver;
213
+ projection;
214
+ quietMs;
215
+ maxActionBytes;
216
+ constructor(agent, actorResolver, projection, quietMs, maxActionBytes) {
217
+ this.agent = agent;
218
+ this.actorResolver = actorResolver;
219
+ this.projection = projection;
220
+ this.quietMs = quietMs;
221
+ this.maxActionBytes = maxActionBytes;
222
+ }
223
+ async handle(req, res, raw) {
224
+ const body = isRecord(raw) ? raw : {};
225
+ const agentName = typeof body.agent === "string" && body.agent.length > 0 ? body.agent : void 0;
226
+ const actor = await this.actorResolver.resolve(req);
227
+ let action;
228
+ if (body.action !== void 0) {
229
+ const read = isRecord(body.action) ? readA2uiAction(body.action, {
230
+ ...this.maxActionBytes !== void 0 ? {
231
+ maxBytes: this.maxActionBytes
232
+ } : {}
233
+ }) : "action must be an A2UI action message";
234
+ if (typeof read === "string") throw invalid("invalid_action", read);
235
+ action = read;
236
+ }
237
+ if (action?.name === A2UI_APPROVE_ACTION || action?.name === A2UI_REJECT_ACTION) {
238
+ const interruptId = action.context.interruptId;
239
+ const address = typeof interruptId === "string" ? decodeInterruptId(interruptId) : void 0;
240
+ if (address === void 0 || address === null || address.kind === "elicitation") {
241
+ throw invalid("invalid_action", "an approval action carries the interruptId it decides");
242
+ }
243
+ if (address.kind === "proposal") {
244
+ if (address.proposalId === void 0 || address.threadId === void 0) {
245
+ throw invalid("invalid_action", "the interruptId names no proposal");
246
+ }
247
+ const decided = await this.agent.decideActionProposal(actor, address.threadId, address.proposalId, {
248
+ decision: action.name === A2UI_APPROVE_ACTION ? "approved" : "rejected",
249
+ via: A2UI_VIA
250
+ });
251
+ this.writeHead(res, {
252
+ "X-Agent-Thread-Id": address.threadId
253
+ });
254
+ const projector = new A2uiProjector(this.projection);
255
+ const messageId = `proposal-${address.proposalId}`;
256
+ for (const event of [
257
+ {
258
+ type: "TEXT_MESSAGE_START",
259
+ messageId,
260
+ role: "assistant"
261
+ },
262
+ {
263
+ type: "TEXT_MESSAGE_CONTENT",
264
+ messageId,
265
+ delta: decided.text
266
+ }
267
+ ]) {
268
+ for (const message2 of projector.project(event)) res.write(`${JSON.stringify(message2)}
269
+ `);
270
+ }
271
+ res.end();
272
+ return;
273
+ }
274
+ await this.agent.assertResumable(actor, address.stream);
275
+ await this.agent.checkDecision(actor, address.toolCallId);
276
+ if (action.name === A2UI_APPROVE_ACTION) {
277
+ await this.agent.approve(actor, address.toolCallId, {
278
+ via: A2UI_VIA
279
+ });
280
+ } else {
281
+ await this.agent.reject(actor, address.toolCallId, void 0, {
282
+ via: A2UI_VIA
283
+ });
284
+ }
285
+ await this.stream(res, address.stream, {
286
+ skip: address.position,
287
+ answered: [
288
+ address.toolCallId
289
+ ]
290
+ });
291
+ return;
292
+ }
293
+ const message = typeof body.message === "string" ? body.message : "";
294
+ const text = action !== void 0 ? uiActionText(action) : message;
295
+ if (text.trim().length === 0) {
296
+ throw invalid("no_user_message", "send a message or an action");
297
+ }
298
+ const threadId = typeof body.threadId === "string" && body.threadId.length > 0 ? body.threadId : void 0;
299
+ const owner = threadId !== void 0 ? await this.agent.threadOwner(threadId) : null;
300
+ if (owner !== null && owner !== actor.id) {
301
+ throw new ForbiddenException("thread belongs to another actor");
302
+ }
303
+ let uiCapabilities;
304
+ if (Object.hasOwn(body, "uiCapabilities")) {
305
+ try {
306
+ uiCapabilities = validateUiCapabilities(body.uiCapabilities);
307
+ } catch (error) {
308
+ throw invalid("invalid_input", error.message);
309
+ }
310
+ }
311
+ const started = await this.agent.chat({
312
+ actor,
313
+ message: text,
314
+ ...threadId === void 0 ? {} : owner !== null ? {
315
+ threadId
316
+ } : {
317
+ newThreadId: threadId
318
+ },
319
+ ...agentName !== void 0 ? {
320
+ agentName
321
+ } : {},
322
+ ...uiCapabilities !== void 0 ? {
323
+ uiCapabilities
324
+ } : {}
325
+ });
326
+ await this.stream(res, started.runId, {
327
+ threadId: started.threadId
328
+ });
329
+ }
330
+ /** Open the JSON Lines response. */
331
+ writeHead(res, extra) {
332
+ res.status(200);
333
+ res.setHeader("Content-Type", "application/jsonl; charset=utf-8");
334
+ res.setHeader("Cache-Control", "no-cache, no-transform");
335
+ res.setHeader("Connection", "keep-alive");
336
+ res.setHeader("X-Accel-Buffering", "no");
337
+ for (const [name, value] of Object.entries(extra)) res.setHeader(name, value);
338
+ res.flushHeaders();
339
+ }
340
+ /** Write one run as A2UI JSON Lines, until it ends or stops to ask. */
341
+ async stream(res, runId, options) {
342
+ this.writeHead(res, {
343
+ "X-Agent-Run-Id": runId,
344
+ ...options.threadId !== void 0 ? {
345
+ "X-Agent-Thread-Id": options.threadId
346
+ } : {}
347
+ });
348
+ const projector = new A2uiProjector(this.projection);
349
+ const write = /* @__PURE__ */ __name((messages) => {
350
+ for (const message of messages) res.write(`${JSON.stringify(message)}
351
+ `);
352
+ }, "write");
353
+ try {
354
+ const frames = agUiFramesFromNdjson(this.agent.subscribe(runId));
355
+ for await (const event of agUiEvents(frames, {
356
+ threadId: options.threadId ?? runId,
357
+ runId,
358
+ streamRunId: runId,
359
+ ...options.threadId !== void 0 ? {
360
+ streamThreadId: options.threadId
361
+ } : {},
362
+ ...options.skip !== void 0 ? {
363
+ skip: options.skip
364
+ } : {},
365
+ ...options.answered !== void 0 ? {
366
+ answered: options.answered
367
+ } : {},
368
+ ...this.quietMs !== void 0 ? {
369
+ quietMs: this.quietMs
370
+ } : {}
371
+ })) {
372
+ write(projector.project(event));
373
+ }
374
+ } catch {
375
+ write(projector.project({
376
+ type: "RUN_ERROR",
377
+ message: "The run could not be followed."
378
+ }));
379
+ }
380
+ res.end();
381
+ }
382
+ };
383
+ function a2uiAdapter(options = {}) {
384
+ const path = (options.path ?? "a2ui").replace(/^\/+|\/+$/g, "");
385
+ const { path: _path, quietMs, maxActionBytes, ...projection } = options;
386
+ let A2uiController = class A2uiController extends A2uiRunHandler {
387
+ static {
388
+ __name(this, "A2uiController");
389
+ }
390
+ constructor(agent, actorResolver, catalog) {
391
+ super(agent, actorResolver, projection.catalog === void 0 && catalog !== void 0 ? {
392
+ ...projection,
393
+ catalog
394
+ } : projection, quietMs, maxActionBytes);
395
+ }
396
+ run(req, res, body) {
397
+ return this.handle(req, res, body);
398
+ }
399
+ };
400
+ _ts_decorate2([
401
+ Post(path),
402
+ HttpCode(200),
403
+ _ts_param(0, Req()),
404
+ _ts_param(1, Res()),
405
+ _ts_param(2, Body()),
406
+ _ts_metadata("design:type", Function),
407
+ _ts_metadata("design:paramtypes", [
408
+ typeof Request === "undefined" ? Object : Request,
409
+ typeof Response === "undefined" ? Object : Response,
410
+ Object
411
+ ]),
412
+ _ts_metadata("design:returntype", typeof Promise === "undefined" ? Object : Promise)
413
+ ], A2uiController.prototype, "run", null);
414
+ A2uiController = _ts_decorate2([
415
+ Controller(),
416
+ _ts_param(0, Inject2(AGENT_SERVICE)),
417
+ _ts_param(1, Inject2(AGENT_ACTOR_RESOLVER)),
418
+ _ts_param(2, Optional()),
419
+ _ts_param(2, Inject2(GENUI_CATALOG)),
420
+ _ts_metadata("design:type", Function),
421
+ _ts_metadata("design:paramtypes", [
422
+ typeof AgentService === "undefined" ? Object : AgentService,
423
+ typeof ActorResolver === "undefined" ? Object : ActorResolver,
424
+ typeof Catalog === "undefined" ? Object : Catalog
425
+ ])
426
+ ], A2uiController);
427
+ return {
428
+ name: "a2ui",
429
+ controllers: [
430
+ A2uiController
431
+ ]
432
+ };
433
+ }
434
+ __name(a2uiAdapter, "a2uiAdapter");
435
+ export {
436
+ A2uiRunHandler,
437
+ a2uiAdapter
438
+ };
439
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/a2ui/index.ts","../../src/a2ui/a2ui.adapter.ts","../../src/agent-service.token.ts","../../src/genui/agent-genui.module.ts","../../src/functional-tool.ts"],"sourcesContent":["/**\n * `@dudousxd/nestjs-agent/a2ui` — A2UI (https://a2ui.org, v0.9) for the agent: the `a2uiAdapter()`\n * route (JSON Lines of A2UI messages), and the framework-free pieces it is built from (re-exported\n * from `@dudousxd/nestjs-agent-core/a2ui`) — the `ui` frame → A2UI converter, the builtin →\n * basic-catalog mappings, the catalog export, the inbound action reader. For A2UI over AG-UI, pass\n * `a2ui: true` to `agUiAdapter()`.\n */\n\nexport * from '@dudousxd/nestjs-agent-core/a2ui';\nexport { type A2uiAdapterOptions, A2uiRunHandler, a2uiAdapter } from './a2ui.adapter.js';\n","import { AGENT_ACTOR_RESOLVER, type ActorResolver } from '@dudousxd/nestjs-agent-core';\nimport {\n A2UI_APPROVE_ACTION,\n A2UI_REJECT_ACTION,\n A2uiProjector,\n type A2uiServerMessage,\n type A2uiStreamOptions,\n readA2uiAction,\n} from '@dudousxd/nestjs-agent-core/a2ui';\nimport {\n agUiEvents,\n agUiFramesFromNdjson,\n decodeInterruptId,\n} from '@dudousxd/nestjs-agent-core/ag-ui';\nimport {\n type Catalog,\n type UiAction,\n type UiCapabilities,\n uiActionText,\n validateUiCapabilities,\n} from '@dudousxd/nestjs-agent-core/genui';\nimport {\n BadRequestException,\n Body,\n Controller,\n ForbiddenException,\n HttpCode,\n Inject,\n Optional,\n Post,\n Req,\n Res,\n} from '@nestjs/common';\nimport type { Request, Response } from 'express';\nimport { AGENT_SERVICE } from '../agent-service.token.js';\nimport type { AgentService } from '../agent.service.js';\nimport { GENUI_CATALOG } from '../genui/agent-genui.module.js';\nimport type { AgentProtocolAdapter } from '../protocol-adapter.js';\n\nexport interface A2uiAdapterOptions extends A2uiStreamOptions {\n /** Where the route mounts, under the agent's path. Default `'a2ui'` → `POST <path>/a2ui`. */\n path?: string;\n /** As `agUiAdapter`'s: how long a run waiting on a person may stay silent before the stream ends. */\n quietMs?: number;\n /** Largest action accepted, as JSON, in bytes. Default 8192. */\n maxActionBytes?: number;\n}\n\n/** The surface an A2UI decision is recorded as having come through. */\nconst A2UI_VIA = 'a2ui';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction invalid(code: string, message: string): BadRequestException {\n return new BadRequestException({ statusCode: 400, code, message });\n}\n\n/**\n * Serves the agent as an A2UI agent. Not mounted directly — {@link a2uiAdapter} mounts a subclass at\n * the configured path — but exported so a host can extend it under a route of its own.\n */\nexport class A2uiRunHandler {\n constructor(\n protected readonly agent: AgentService,\n protected readonly actorResolver: ActorResolver,\n protected readonly projection: A2uiStreamOptions,\n protected readonly quietMs: number | undefined,\n protected readonly maxActionBytes: number | undefined,\n ) {}\n\n async handle(req: Request, res: Response, raw: unknown): Promise<void> {\n const body = isRecord(raw) ? raw : {};\n const agentName =\n typeof body.agent === 'string' && body.agent.length > 0 ? body.agent : undefined;\n const actor = await this.actorResolver.resolve(req);\n\n let action: UiAction | undefined;\n if (body.action !== undefined) {\n const read = isRecord(body.action)\n ? readA2uiAction(body.action, {\n ...(this.maxActionBytes !== undefined ? { maxBytes: this.maxActionBytes } : {}),\n })\n : 'action must be an A2UI action message';\n if (typeof read === 'string') throw invalid('invalid_action', read);\n action = read;\n }\n\n // Deciding the approval a stream ended on: settle it, then stream the rest of the run.\n if (action?.name === A2UI_APPROVE_ACTION || action?.name === A2UI_REJECT_ACTION) {\n const interruptId = action.context.interruptId;\n const address = typeof interruptId === 'string' ? decodeInterruptId(interruptId) : undefined;\n if (address === undefined || address === null || address.kind === 'elicitation') {\n throw invalid('invalid_action', 'an approval action carries the interruptId it decides');\n }\n // An independent proposal: decided through the proposal service (its own run is not\n // waiting), as an AG-UI resume decides it, and answered with the configured reply.\n if (address.kind === 'proposal') {\n if (address.proposalId === undefined || address.threadId === undefined) {\n throw invalid('invalid_action', 'the interruptId names no proposal');\n }\n const decided = await this.agent.decideActionProposal(\n actor,\n address.threadId,\n address.proposalId,\n {\n decision: action.name === A2UI_APPROVE_ACTION ? 'approved' : 'rejected',\n via: A2UI_VIA,\n },\n );\n this.writeHead(res, { 'X-Agent-Thread-Id': address.threadId });\n const projector = new A2uiProjector(this.projection);\n const messageId = `proposal-${address.proposalId}`;\n for (const event of [\n { type: 'TEXT_MESSAGE_START' as const, messageId, role: 'assistant' as const },\n { type: 'TEXT_MESSAGE_CONTENT' as const, messageId, delta: decided.text },\n ]) {\n for (const message of projector.project(event)) res.write(`${JSON.stringify(message)}\\n`);\n }\n res.end();\n return;\n }\n // The same checks an AG-UI resume makes: the run is the actor's and still waiting, and the\n // actor may decide this call.\n await this.agent.assertResumable(actor, address.stream);\n await this.agent.checkDecision(actor, address.toolCallId);\n if (action.name === A2UI_APPROVE_ACTION) {\n await this.agent.approve(actor, address.toolCallId, { via: A2UI_VIA });\n } else {\n await this.agent.reject(actor, address.toolCallId, undefined, { via: A2UI_VIA });\n }\n await this.stream(res, address.stream, {\n skip: address.position,\n answered: [address.toolCallId],\n });\n return;\n }\n\n const message = typeof body.message === 'string' ? body.message : '';\n const text = action !== undefined ? uiActionText(action) : message;\n if (text.trim().length === 0) {\n throw invalid('no_user_message', 'send a message or an action');\n }\n const threadId =\n typeof body.threadId === 'string' && body.threadId.length > 0 ? body.threadId : undefined;\n const owner = threadId !== undefined ? await this.agent.threadOwner(threadId) : null;\n if (owner !== null && owner !== actor.id) {\n throw new ForbiddenException('thread belongs to another actor');\n }\n let uiCapabilities: UiCapabilities | undefined;\n if (Object.hasOwn(body, 'uiCapabilities')) {\n try {\n uiCapabilities = validateUiCapabilities(body.uiCapabilities);\n } catch (error) {\n throw invalid('invalid_input', (error as Error).message);\n }\n }\n const started = await this.agent.chat({\n actor,\n message: text,\n ...(threadId === undefined ? {} : owner !== null ? { threadId } : { newThreadId: threadId }),\n ...(agentName !== undefined ? { agentName } : {}),\n ...(uiCapabilities !== undefined ? { uiCapabilities } : {}),\n });\n await this.stream(res, started.runId, { threadId: started.threadId });\n }\n\n /** Open the JSON Lines response. */\n protected writeHead(res: Response, extra: Record<string, string>): void {\n res.status(200);\n res.setHeader('Content-Type', 'application/jsonl; charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache, no-transform');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n for (const [name, value] of Object.entries(extra)) res.setHeader(name, value);\n res.flushHeaders();\n }\n\n /** Write one run as A2UI JSON Lines, until it ends or stops to ask. */\n protected async stream(\n res: Response,\n runId: string,\n options: { threadId?: string; skip?: number; answered?: string[] },\n ): Promise<void> {\n this.writeHead(res, {\n 'X-Agent-Run-Id': runId,\n ...(options.threadId !== undefined ? { 'X-Agent-Thread-Id': options.threadId } : {}),\n });\n const projector = new A2uiProjector(this.projection);\n const write = (messages: A2uiServerMessage[]) => {\n for (const message of messages) res.write(`${JSON.stringify(message)}\\n`);\n };\n try {\n const frames = agUiFramesFromNdjson(this.agent.subscribe(runId));\n for await (const event of agUiEvents(frames, {\n threadId: options.threadId ?? runId,\n runId,\n streamRunId: runId,\n ...(options.threadId !== undefined ? { streamThreadId: options.threadId } : {}),\n ...(options.skip !== undefined ? { skip: options.skip } : {}),\n ...(options.answered !== undefined ? { answered: options.answered } : {}),\n ...(this.quietMs !== undefined ? { quietMs: this.quietMs } : {}),\n })) {\n write(projector.project(event));\n }\n } catch {\n write(projector.project({ type: 'RUN_ERROR', message: 'The run could not be followed.' }));\n }\n res.end();\n }\n}\n\n/**\n * Serve the agent as an A2UI (https://a2ui.org, v0.9) agent: `POST <path>/a2ui` answers with the\n * run as a JSON Lines stream of A2UI server-to-client messages — `createSurface`,\n * `updateComponents`, `updateDataModel`, `deleteSurface` — that any A2UI renderer draws.\n *\n * ```ts\n * import { a2uiAdapter } from '@dudousxd/nestjs-agent/a2ui'\n *\n * AgentModule.forRoot({ model, adapters: [a2uiAdapter()] })\n * ```\n *\n * The body is `{ threadId?, message?, action?, uiCapabilities?, agent? }`:\n * - `message` — the user's text;\n * - `action` — an A2UI client message (`{ version: 'v0.9', action: { name, surfaceId,\n * sourceComponentId, timestamp, context } }`, or v0.8's `{ userAction }`): it becomes the turn\n * (`uiActionText`). `agora.approve` / `agora.reject` with `context.interruptId` decide the\n * approval a previous stream ended on, and stream the rest of that run (an independent\n * proposal is decided through the proposal service and answered with its reply);\n * - `threadId` — continue that thread (its owner is checked), or start one under that id.\n *\n * Response headers name the library's ids: `X-Agent-Thread-Id`, `X-Agent-Run-Id`. Authenticated by\n * the module's `actorResolver`, guarded by its `guards` and owner-scoped exactly as `chat` is.\n * Components nothing maps are drawn as their text, from the `AgentGenuiModule` catalog when there\n * is one (or `catalog` here).\n */\nexport function a2uiAdapter(options: A2uiAdapterOptions = {}): AgentProtocolAdapter {\n const path = (options.path ?? 'a2ui').replace(/^\\/+|\\/+$/g, '');\n const { path: _path, quietMs, maxActionBytes, ...projection } = options;\n\n @Controller()\n class A2uiController extends A2uiRunHandler {\n constructor(\n // Tokens, never classes: this entry is its own bundle (see AGENT_SERVICE).\n @Inject(AGENT_SERVICE) agent: AgentService,\n @Inject(AGENT_ACTOR_RESOLVER) actorResolver: ActorResolver,\n @Optional() @Inject(GENUI_CATALOG) catalog?: Catalog,\n ) {\n super(\n agent,\n actorResolver,\n projection.catalog === undefined && catalog !== undefined\n ? { ...projection, catalog }\n : projection,\n quietMs,\n maxActionBytes,\n );\n }\n\n @Post(path)\n @HttpCode(200)\n run(@Req() req: Request, @Res() res: Response, @Body() body: unknown): Promise<void> {\n return this.handle(req, res, body);\n }\n }\n\n return { name: 'a2ui', controllers: [A2uiController] };\n}\n","/**\n * `AgentService`, as the controllers of a secondary entry (`@dudousxd/nestjs-agent/a2ui`) ask for it.\n *\n * A token rather than the class: every entry is its own bundle with its own copy of every class it\n * imports, so the class used as an injection token there is not the one `AgentModule` provides.\n * `Symbol.for` is the same value in every bundle.\n */\nexport const AGENT_SERVICE = Symbol.for('@dudousxd/nestjs-agent:agent-service');\n","import type { ToolPresentation } from '@dudousxd/nestjs-agent-core';\nimport type {\n Catalog,\n GenuiCatalogScope,\n GenuiStreaming,\n GenuiToolsOptions,\n TreeLimits,\n TreeSchemaMode,\n} from '@dudousxd/nestjs-agent-core/genui';\nimport {\n type DefineSandboxOptions,\n defineCatalog,\n defineSandbox,\n genuiTools,\n} from '@dudousxd/nestjs-agent-core/genui';\nimport {\n type DynamicModule,\n type FactoryProvider,\n Inject,\n Module,\n type ModuleMetadata,\n type Provider,\n type Type,\n} from '@nestjs/common';\nimport { provideAgentTools } from '../functional-tool.js';\n\n/** The app's genui catalog — `@InjectGenuiCatalog()` it anywhere; `overrideProvider` it in tests. */\nexport const GENUI_CATALOG = Symbol.for('@dudousxd/nestjs-agent:genui-catalog');\n\n/** The resolved {@link AgentGenuiOptions} (without the resolver). */\nexport const GENUI_OPTIONS = Symbol.for('@dudousxd/nestjs-agent:genui-options');\n\n/** Inject the app's genui catalog ({@link GENUI_CATALOG}). */\nexport const InjectGenuiCatalog = (): ParameterDecorator & PropertyDecorator =>\n Inject(GENUI_CATALOG);\n\n/**\n * Picks the catalog for ONE request — a tenant's own, versioned components. Consulted when a genui\n * call is validated and when the turn's tool list is described to the model, so components can\n * change without rebuilding tools at boot. Cache in here if resolving costs a round trip: it runs\n * once per turn per genui tool, and once per call.\n *\n * Provide it through `AgentGenuiModule.forRoot({ resolver })` (a class, resolved with DI, or an\n * instance); it is then injectable, and replaceable in a test with\n * `overrideProvider(GenuiCatalogResolver)`.\n */\nexport abstract class GenuiCatalogResolver {\n abstract resolve(scope: GenuiCatalogScope): Catalog | Promise<Catalog>;\n}\n\n/** What {@link AgentGenuiModule} builds tools from. */\nexport interface AgentGenuiOptions {\n /**\n * The catalog. With a {@link GenuiCatalogResolver} it is the boot-time one — it names the\n * per-component tools — and may be empty. Default: an empty catalog.\n */\n catalog?: Catalog;\n /**\n * `tree` (default): one `ui__render` tool taking a composed tree (a single node is pushed as that\n * component). `per-component`: one `ui__show_<snake>` tool per component.\n */\n mode?: 'per-component' | 'tree';\n /** `ui__render`'s input schema: `'strict'` (default, a `$defs` union per component) or `'loose'`. */\n treeSchema?: TreeSchemaMode;\n /**\n * Tree mode: `'complete'` (default) draws the tree once the call has run; `'partial'` streams it\n * as `partial` ui frames while the model writes it. A component's own `streaming` overrides it.\n */\n streaming?: GenuiStreaming;\n /** Least time between two partial tree frames of one call, in ms. Default 100. */\n streamingThrottleMs?: number;\n /** End the model's turn once a genui call succeeds (no narrating follow-up call). */\n terminal?: boolean;\n /** Tool name in `tree` mode. Default `ui__render`. */\n treeToolName?: string;\n /** Text prepended to the tree tool's description. */\n treeInstructions?: string;\n treeLimits?: TreeLimits;\n /** Tool-name prefix in `per-component` mode. Default `ui__show_`. */\n namePrefix?: string;\n /**\n * Also register ONE generic tool taking `{ component, props }`, validated against the request's\n * catalog — how a model reaches a tenant's own components, which no boot-time tool names. `true`\n * names it `ui__show`.\n */\n showTool?: boolean | string;\n /** Text prepended to the show tool's description. */\n showInstructions?: string;\n /** Roles allowed to call the tools. Undefined → the roles policy default. */\n roles?: string[];\n /** Override the generated presentation per component (or per tree / show tool name). */\n presentation?: (component: string) => ToolPresentation | undefined;\n /**\n * Add the sandbox component (`Sandbox`): the model may write HTML, CSS and JS for a one-off\n * interactive answer when no catalog component fits. `true` for the defaults (no network), or\n * the {@link DefineSandboxOptions} (a policy listing origins, extra instructions). Off by default.\n * The browser's catalog should carry the same definition (`defineSandbox(...)` in the shared\n * catalog file) so the renderer enforces the same policy.\n */\n sandbox?: boolean | DefineSandboxOptions;\n}\n\ntype ResolverOption = Type<GenuiCatalogResolver> | GenuiCatalogResolver;\n\nexport interface AgentGenuiModuleOptions extends AgentGenuiOptions {\n /** Per-request catalogs. A class is instantiated with DI (its dependencies must be global or exported to the root). */\n resolver?: ResolverOption;\n}\n\nexport interface AgentGenuiModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {\n inject?: FactoryProvider['inject'];\n useFactory: (...deps: any[]) => AgentGenuiOptions | Promise<AgentGenuiOptions>;\n /** Per-request catalogs. A class resolves its dependencies from `imports`. */\n resolver?: ResolverOption;\n}\n\nfunction resolverProviders(resolver: ResolverOption | undefined): Provider[] {\n if (resolver === undefined) return [];\n return [\n typeof resolver === 'function'\n ? { provide: GenuiCatalogResolver, useClass: resolver }\n : { provide: GenuiCatalogResolver, useValue: resolver },\n ];\n}\n\nfunction toolOptions(\n options: AgentGenuiOptions,\n resolver: GenuiCatalogResolver | undefined,\n): GenuiToolsOptions {\n const { catalog: _catalog, sandbox: _sandbox, ...rest } = options;\n return {\n ...rest,\n ...(resolver !== undefined\n ? { resolveCatalog: (scope: GenuiCatalogScope) => resolver.resolve(scope) }\n : {}),\n };\n}\n\n/** The configured catalog, with the sandbox component added when `sandbox` asks for it. */\nfunction withSandbox(options: AgentGenuiOptions): Catalog {\n const base = options.catalog ?? defineCatalog([]);\n const { sandbox } = options;\n return sandbox === undefined || sandbox === false\n ? base\n : base.extend([defineSandbox(sandbox === true ? {} : sandbox)]);\n}\n\nfunction coreProviders(resolver: ResolverOption | undefined): Provider[] {\n return [\n {\n provide: GENUI_CATALOG,\n useFactory: (options: AgentGenuiOptions) => withSandbox(options),\n inject: [GENUI_OPTIONS],\n },\n ...resolverProviders(resolver),\n // The generated tools, registered by the agent module's discovery like any `provideAgentTool`.\n // Built from the injected catalog, so `overrideProvider(GENUI_CATALOG)` changes them too.\n provideAgentTools(\n (options: AgentGenuiOptions, catalog: Catalog, perRequest?: GenuiCatalogResolver) =>\n genuiTools(catalog, toolOptions(options, perRequest)),\n [GENUI_OPTIONS, GENUI_CATALOG, { token: GenuiCatalogResolver, optional: true }],\n ),\n ];\n}\n\nconst EXPORTS = [GENUI_CATALOG, GENUI_OPTIONS];\n\n/**\n * Generative UI for the agent: registers the tools that let the model push catalog components\n * (one `ui__render` tree tool, or `ui__show_<component>` per component, and/or the generic\n * `ui__show`), and makes the catalog\n * injectable. Global, like `AgentModule`; import it once, next to it.\n *\n * ```ts\n * AgentGenuiModule.forRoot({ catalog, mode: 'tree', terminal: true })\n * ```\n */\n@Module({})\nexport class AgentGenuiModule {\n static forRoot(options: AgentGenuiModuleOptions = {}): DynamicModule {\n const { resolver, ...rest } = options;\n return {\n module: AgentGenuiModule,\n global: true,\n providers: [{ provide: GENUI_OPTIONS, useValue: rest }, ...coreProviders(resolver)],\n exports: resolver === undefined ? EXPORTS : [...EXPORTS, GenuiCatalogResolver],\n };\n }\n\n static forRootAsync(options: AgentGenuiModuleAsyncOptions): DynamicModule {\n return {\n module: AgentGenuiModule,\n global: true,\n imports: options.imports ?? [],\n providers: [\n { provide: GENUI_OPTIONS, useFactory: options.useFactory, inject: options.inject ?? [] },\n ...coreProviders(options.resolver),\n ],\n exports: options.resolver === undefined ? EXPORTS : [...EXPORTS, GenuiCatalogResolver],\n };\n }\n}\n","import {\n type FunctionalToolDefinition,\n type ToolHandler,\n type ToolSpec,\n createFunctionalTool,\n} from '@dudousxd/nestjs-agent-core';\nimport type { FactoryProvider, Provider } from '@nestjs/common';\n\n/**\n * Brand stamped onto a functional-tool provider instance so `AiToolDiscoveryService` picks it up\n * when it walks every provider at boot — the declarative alternative to a manual `registry.register`.\n */\nexport const AGENT_TOOL_BRAND = Symbol.for('@dudousxd/nestjs-agent:functional-tool');\n\n/** A tool expressed as data + handler (e.g. from `createExecuteSqlTool`), not an `@AiTool` class. */\nexport interface FunctionalTool {\n spec: ToolSpec;\n handler: ToolHandler;\n}\n\n/** A {@link FunctionalTool} carrying {@link AGENT_TOOL_BRAND} — what a provider resolves to. */\nexport interface BrandedFunctionalTool extends FunctionalTool {\n readonly [AGENT_TOOL_BRAND]: true;\n}\n\nfunction brand(tool: FunctionalTool): BrandedFunctionalTool {\n return { [AGENT_TOOL_BRAND]: true, spec: tool.spec, handler: tool.handler };\n}\n\n/** Narrows a provider instance to a branded functional tool for boot-time registration. */\nexport function isBrandedFunctionalTool(value: object): value is BrandedFunctionalTool {\n return AGENT_TOOL_BRAND in value && 'spec' in value && 'handler' in value;\n}\n\n/**\n * Registers a functional tool declaratively — drop the returned provider in a module's `providers`\n * and `AiToolDiscoveryService` auto-registers it at boot. Each call uses a fresh, unique provide\n * token so the same tool can be provided many times without collisions.\n *\n * Two forms: a static tool (`useValue`) or a factory with `inject` for DI-resolved dependencies\n * (`useFactory`). Either way the resolved instance is branded with {@link AGENT_TOOL_BRAND}.\n */\nexport function provideAgentTool(tool: FunctionalTool): Provider;\nexport function provideAgentTool(\n factory: (...deps: never[]) => FunctionalTool,\n inject?: FactoryProvider['inject'],\n): Provider;\nexport function provideAgentTool(\n factoryOrTool: FunctionalTool | ((...deps: never[]) => FunctionalTool),\n inject?: FactoryProvider['inject'],\n): Provider {\n const provide = Symbol('nestjs-agent:functional-tool-instance');\n if (typeof factoryOrTool === 'function') {\n return {\n provide,\n useFactory: (...deps: never[]) => brand(factoryOrTool(...deps)),\n inject: inject ?? [],\n };\n }\n return { provide, useValue: brand(factoryOrTool) };\n}\n\n/**\n * Registers a LIST of functional tools from one factory — for a producer whose tool count is only\n * known once its dependencies resolve (a catalog loaded from config, one tool per entry). The\n * factory's result is branded element by element; `AiToolDiscoveryService` registers each.\n */\nexport function provideAgentTools(\n factory: (...deps: never[]) => readonly FunctionalTool[] | Promise<readonly FunctionalTool[]>,\n inject?: FactoryProvider['inject'],\n): Provider {\n return {\n provide: Symbol('nestjs-agent:functional-tool-list'),\n useFactory: async (...deps: never[]) => (await factory(...deps)).map(brand),\n inject: inject ?? [],\n };\n}\n\n/** Author a functional tool with inferred Standard Schema input and domain output. */\nexport const defineTool = createFunctionalTool;\nexport type { FunctionalToolDefinition };\n"],"mappings":"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","names":["AGENT_ACTOR_RESOLVER","A2UI_APPROVE_ACTION","A2UI_REJECT_ACTION","A2uiProjector","readA2uiAction","agUiEvents","agUiFramesFromNdjson","decodeInterruptId","uiActionText","validateUiCapabilities","BadRequestException","Body","Controller","ForbiddenException","HttpCode","Inject","Optional","Post","Req","Res","AGENT_SERVICE","Symbol","for","defineCatalog","defineSandbox","genuiTools","Inject","Module","createFunctionalTool","AGENT_TOOL_BRAND","Symbol","for","brand","tool","spec","handler","provideAgentTools","factory","inject","provide","Symbol","useFactory","deps","map","brand","GENUI_CATALOG","Symbol","for","GENUI_OPTIONS","GenuiCatalogResolver","resolverProviders","resolver","undefined","provide","useClass","useValue","toolOptions","options","catalog","_catalog","sandbox","_sandbox","rest","resolveCatalog","scope","resolve","withSandbox","base","defineCatalog","extend","defineSandbox","coreProviders","GENUI_CATALOG","useFactory","inject","GENUI_OPTIONS","provideAgentTools","perRequest","genuiTools","token","optional","EXPORTS","AgentGenuiModule","forRoot","module","global","providers","exports","forRootAsync","imports","A2UI_VIA","isRecord","value","Array","isArray","invalid","code","message","BadRequestException","statusCode","A2uiRunHandler","agent","actorResolver","projection","quietMs","maxActionBytes","handle","req","res","raw","body","agentName","length","undefined","actor","resolve","action","read","readA2uiAction","maxBytes","name","A2UI_APPROVE_ACTION","A2UI_REJECT_ACTION","interruptId","context","address","decodeInterruptId","kind","proposalId","threadId","decided","decideActionProposal","decision","via","writeHead","projector","A2uiProjector","messageId","event","type","role","delta","text","project","write","JSON","stringify","end","assertResumable","stream","checkDecision","toolCallId","approve","reject","skip","position","answered","uiActionText","trim","owner","threadOwner","id","ForbiddenException","uiCapabilities","Object","hasOwn","validateUiCapabilities","error","started","chat","newThreadId","runId","extra","status","setHeader","entries","flushHeaders","options","messages","frames","agUiFramesFromNdjson","subscribe","agUiEvents","streamRunId","streamThreadId","a2uiAdapter","path","replace","_path","A2uiController","catalog","run","controllers"]}
@@ -181,8 +181,9 @@ declare class LedgerQuotaProvider implements QuotaProvider {
181
181
  * `path` and guarded by its `guards` exactly like the native routes; the runs, the store, the
182
182
  * approvals and the quota are the library's, unchanged.
183
183
  *
184
- * `agUiAdapter()` is the one that ships. Write your own the same way: a controller that injects
185
- * `AgentService` (and `AGENT_ACTOR_RESOLVER`) and translates.
184
+ * `agUiAdapter()` and `a2uiAdapter()` (`/a2ui`) are the ones that ship. Write your own the same
185
+ * way: a controller that injects `AgentService` (and `AGENT_ACTOR_RESOLVER`) and translates — by
186
+ * the `AGENT_SERVICE` token when it ships in an entry of its own.
186
187
  */
187
188
  interface AgentProtocolAdapter {
188
189
  /** For logs and diagnostics. */
@@ -181,8 +181,9 @@ declare class LedgerQuotaProvider implements QuotaProvider {
181
181
  * `path` and guarded by its `guards` exactly like the native routes; the runs, the store, the
182
182
  * approvals and the quota are the library's, unchanged.
183
183
  *
184
- * `agUiAdapter()` is the one that ships. Write your own the same way: a controller that injects
185
- * `AgentService` (and `AGENT_ACTOR_RESOLVER`) and translates.
184
+ * `agUiAdapter()` and `a2uiAdapter()` (`/a2ui`) are the ones that ship. Write your own the same
185
+ * way: a controller that injects `AgentService` (and `AGENT_ACTOR_RESOLVER`) and translates — by
186
+ * the `AGENT_SERVICE` token when it ships in an entry of its own.
186
187
  */
187
188
  interface AgentProtocolAdapter {
188
189
  /** For logs and diagnostics. */
@@ -1290,7 +1290,7 @@ function resolverProviders(resolver) {
1290
1290
  }
1291
1291
  __name(resolverProviders, "resolverProviders");
1292
1292
  function toolOptions(options, resolver) {
1293
- const { catalog: _catalog, ...rest } = options;
1293
+ const { catalog: _catalog, sandbox: _sandbox, ...rest } = options;
1294
1294
  return {
1295
1295
  ...rest,
1296
1296
  ...resolver !== void 0 ? {
@@ -1299,11 +1299,19 @@ function toolOptions(options, resolver) {
1299
1299
  };
1300
1300
  }
1301
1301
  __name(toolOptions, "toolOptions");
1302
+ function withSandbox(options) {
1303
+ const base = options.catalog ?? (0, import_genui.defineCatalog)([]);
1304
+ const { sandbox } = options;
1305
+ return sandbox === void 0 || sandbox === false ? base : base.extend([
1306
+ (0, import_genui.defineSandbox)(sandbox === true ? {} : sandbox)
1307
+ ]);
1308
+ }
1309
+ __name(withSandbox, "withSandbox");
1302
1310
  function coreProviders(resolver) {
1303
1311
  return [
1304
1312
  {
1305
1313
  provide: GENUI_CATALOG,
1306
- useFactory: /* @__PURE__ */ __name((options) => options.catalog ?? (0, import_genui.defineCatalog)([]), "useFactory"),
1314
+ useFactory: /* @__PURE__ */ __name((options) => withSandbox(options), "useFactory"),
1307
1315
  inject: [
1308
1316
  GENUI_OPTIONS
1309
1317
  ]
@@ -1762,6 +1770,9 @@ AgentDepsFactory2 = _ts_decorate6([
1762
1770
  ])
1763
1771
  ], AgentDepsFactory2);
1764
1772
 
1773
+ // src/agent-service.token.ts
1774
+ var AGENT_SERVICE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:agent-service");
1775
+
1765
1776
  // src/agent.service.ts
1766
1777
  var import_nestjs_agent_core16 = require("@dudousxd/nestjs-agent-core");
1767
1778
  var import_common10 = require("@nestjs/common");
@@ -7338,6 +7349,10 @@ function sharedProviders(durable, engine) {
7338
7349
  },
7339
7350
  InlineAgentRunner,
7340
7351
  AgentService,
7352
+ {
7353
+ provide: AGENT_SERVICE,
7354
+ useExisting: AgentService
7355
+ },
7341
7356
  ActionProposalService,
7342
7357
  ActionProposalWorkerService,
7343
7358
  // Bound ALWAYS (durable or inline) — the console's cross-thread approvals inbox routes decisions
@@ -7405,6 +7420,7 @@ function exportsFor(engine) {
7405
7420
  import_nestjs_agent_core40.AGENT_DEPS_FACTORY,
7406
7421
  AgentDepsFactory2,
7407
7422
  AgentService,
7423
+ AGENT_SERVICE,
7408
7424
  ActionProposalService,
7409
7425
  ActionProposalWorkerService,
7410
7426
  ChatQueueService2,