@dudousxd/nestjs-agent 1.28.0 → 1.30.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/genui/index.ts","../../src/genui/agent-genui.module.ts","../../src/functional-tool.ts"],"sourcesContent":["/**\n * `@dudousxd/nestjs-agent/genui` — generative UI for NestJS: {@link AgentGenuiModule} registers the\n * tools that push catalog components, `GENUI_CATALOG` / `@InjectGenuiCatalog()` expose the catalog,\n * and {@link GenuiCatalogResolver} serves per-request (per-tenant) catalogs. The catalog itself —\n * `defineComponent`, `defineCatalog`, validation, text fallbacks — is re-exported from\n * `@dudousxd/nestjs-agent-core/genui`, the isomorphic entry a browser imports too.\n */\nexport {\n AgentGenuiModule,\n type AgentGenuiModuleAsyncOptions,\n type AgentGenuiModuleOptions,\n type AgentGenuiOptions,\n GENUI_CATALOG,\n GENUI_OPTIONS,\n GenuiCatalogResolver,\n InjectGenuiCatalog,\n} from './agent-genui.module.js';\nexport * from '@dudousxd/nestjs-agent-core/genui';\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_TOOL_BRAND","Symbol","for","brand","tool","spec","handler","provideAgentTools","factory","inject","provide","Symbol","useFactory","deps","map","brand","GENUI_CATALOG","Symbol","for","GENUI_OPTIONS","InjectGenuiCatalog","Inject","GenuiCatalogResolver","resolverProviders","resolver","undefined","provide","useClass","useValue","toolOptions","options","catalog","_catalog","sandbox","_sandbox","rest","resolveCatalog","scope","resolve","withSandbox","base","defineCatalog","extend","defineSandbox","coreProviders","useFactory","inject","provideAgentTools","perRequest","genuiTools","token","optional","EXPORTS","AgentGenuiModule","forRoot","module","global","providers","exports","forRootAsync","imports"]}
1
+ {"version":3,"sources":["../../src/genui/index.ts","../../src/genui/agent-genui.module.ts","../../src/functional-tool.ts"],"sourcesContent":["/**\n * `@dudousxd/nestjs-agent/genui` — generative UI for NestJS: {@link AgentGenuiModule} registers the\n * tools that push catalog components, `GENUI_CATALOG` / `@InjectGenuiCatalog()` expose the catalog,\n * and {@link GenuiCatalogResolver} serves per-request (per-tenant) catalogs. The catalog itself —\n * `defineComponent`, `defineCatalog`, validation, text fallbacks — is re-exported from\n * `@dudousxd/nestjs-agent-core/genui`, the isomorphic entry a browser imports too.\n */\nexport {\n AgentGenuiModule,\n type AgentGenuiModuleAsyncOptions,\n type AgentGenuiModuleOptions,\n type AgentGenuiOptions,\n GENUI_CATALOG,\n GENUI_OPTIONS,\n GenuiCatalogResolver,\n InjectGenuiCatalog,\n} from './agent-genui.module.js';\nexport * from '@dudousxd/nestjs-agent-core/genui';\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 /**\n * Tree mode: components that ALSO get a flat tool whose input IS their props —\n * `componentTools: ['Sandbox']` adds `ui__sandbox`, so a model writing a big component on its own\n * has no `{ type, props }` envelope to leave out.\n */\n componentTools?: readonly string[];\n /** Tool-name prefix for `componentTools`. Default `ui__` (`Sandbox` → `ui__sandbox`). */\n componentToolPrefix?: string;\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_TOOL_BRAND","Symbol","for","brand","tool","spec","handler","provideAgentTools","factory","inject","provide","Symbol","useFactory","deps","map","brand","GENUI_CATALOG","Symbol","for","GENUI_OPTIONS","InjectGenuiCatalog","Inject","GenuiCatalogResolver","resolverProviders","resolver","undefined","provide","useClass","useValue","toolOptions","options","catalog","_catalog","sandbox","_sandbox","rest","resolveCatalog","scope","resolve","withSandbox","base","defineCatalog","extend","defineSandbox","coreProviders","useFactory","inject","provideAgentTools","perRequest","genuiTools","token","optional","EXPORTS","AgentGenuiModule","forRoot","module","global","providers","exports","forRootAsync","imports"]}
@@ -50,6 +50,14 @@ interface AgentGenuiOptions {
50
50
  /** Text prepended to the tree tool's description. */
51
51
  treeInstructions?: string;
52
52
  treeLimits?: TreeLimits;
53
+ /**
54
+ * Tree mode: components that ALSO get a flat tool whose input IS their props —
55
+ * `componentTools: ['Sandbox']` adds `ui__sandbox`, so a model writing a big component on its own
56
+ * has no `{ type, props }` envelope to leave out.
57
+ */
58
+ componentTools?: readonly string[];
59
+ /** Tool-name prefix for `componentTools`. Default `ui__` (`Sandbox` → `ui__sandbox`). */
60
+ componentToolPrefix?: string;
53
61
  /** Tool-name prefix in `per-component` mode. Default `ui__show_`. */
54
62
  namePrefix?: string;
55
63
  /**
@@ -50,6 +50,14 @@ interface AgentGenuiOptions {
50
50
  /** Text prepended to the tree tool's description. */
51
51
  treeInstructions?: string;
52
52
  treeLimits?: TreeLimits;
53
+ /**
54
+ * Tree mode: components that ALSO get a flat tool whose input IS their props —
55
+ * `componentTools: ['Sandbox']` adds `ui__sandbox`, so a model writing a big component on its own
56
+ * has no `{ type, props }` envelope to leave out.
57
+ */
58
+ componentTools?: readonly string[];
59
+ /** Tool-name prefix for `componentTools`. Default `ui__` (`Sandbox` → `ui__sandbox`). */
60
+ componentToolPrefix?: string;
53
61
  /** Tool-name prefix in `per-component` mode. Default `ui__show_`. */
54
62
  namePrefix?: string;
55
63
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/genui/agent-genui.module.ts","../../src/functional-tool.ts","../../src/genui/index.ts"],"sourcesContent":["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","/**\n * `@dudousxd/nestjs-agent/genui` — generative UI for NestJS: {@link AgentGenuiModule} registers the\n * tools that push catalog components, `GENUI_CATALOG` / `@InjectGenuiCatalog()` expose the catalog,\n * and {@link GenuiCatalogResolver} serves per-request (per-tenant) catalogs. The catalog itself —\n * `defineComponent`, `defineCatalog`, validation, text fallbacks — is re-exported from\n * `@dudousxd/nestjs-agent-core/genui`, the isomorphic entry a browser imports too.\n */\nexport {\n AgentGenuiModule,\n type AgentGenuiModuleAsyncOptions,\n type AgentGenuiModuleOptions,\n type AgentGenuiOptions,\n GENUI_CATALOG,\n GENUI_OPTIONS,\n GenuiCatalogResolver,\n InjectGenuiCatalog,\n} from './agent-genui.module.js';\nexport * from '@dudousxd/nestjs-agent-core/genui';\n"],"mappings":"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","names":["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","InjectGenuiCatalog","Inject","GenuiCatalogResolver","resolverProviders","resolver","undefined","provide","useClass","useValue","toolOptions","options","catalog","_catalog","sandbox","_sandbox","rest","resolveCatalog","scope","resolve","withSandbox","base","defineCatalog","extend","defineSandbox","coreProviders","useFactory","inject","provideAgentTools","perRequest","genuiTools","token","optional","EXPORTS","AgentGenuiModule","forRoot","module","global","providers","exports","forRootAsync","imports"]}
1
+ {"version":3,"sources":["../../src/genui/agent-genui.module.ts","../../src/functional-tool.ts","../../src/genui/index.ts"],"sourcesContent":["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 /**\n * Tree mode: components that ALSO get a flat tool whose input IS their props —\n * `componentTools: ['Sandbox']` adds `ui__sandbox`, so a model writing a big component on its own\n * has no `{ type, props }` envelope to leave out.\n */\n componentTools?: readonly string[];\n /** Tool-name prefix for `componentTools`. Default `ui__` (`Sandbox` → `ui__sandbox`). */\n componentToolPrefix?: string;\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","/**\n * `@dudousxd/nestjs-agent/genui` — generative UI for NestJS: {@link AgentGenuiModule} registers the\n * tools that push catalog components, `GENUI_CATALOG` / `@InjectGenuiCatalog()` expose the catalog,\n * and {@link GenuiCatalogResolver} serves per-request (per-tenant) catalogs. The catalog itself —\n * `defineComponent`, `defineCatalog`, validation, text fallbacks — is re-exported from\n * `@dudousxd/nestjs-agent-core/genui`, the isomorphic entry a browser imports too.\n */\nexport {\n AgentGenuiModule,\n type AgentGenuiModuleAsyncOptions,\n type AgentGenuiModuleOptions,\n type AgentGenuiOptions,\n GENUI_CATALOG,\n GENUI_OPTIONS,\n GenuiCatalogResolver,\n InjectGenuiCatalog,\n} from './agent-genui.module.js';\nexport * from '@dudousxd/nestjs-agent-core/genui';\n"],"mappings":"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","names":["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","InjectGenuiCatalog","Inject","GenuiCatalogResolver","resolverProviders","resolver","undefined","provide","useClass","useValue","toolOptions","options","catalog","_catalog","sandbox","_sandbox","rest","resolveCatalog","scope","resolve","withSandbox","base","defineCatalog","extend","defineSandbox","coreProviders","useFactory","inject","provideAgentTools","perRequest","genuiTools","token","optional","EXPORTS","AgentGenuiModule","forRoot","module","global","providers","exports","forRootAsync","imports"]}
@@ -1,4 +1,4 @@
1
- import { A2uiStreamOptions } from '@dudousxd/nestjs-agent-core/a2ui';
1
+ import { A2uiStreamOptions, a2uiThreadReplay } from '@dudousxd/nestjs-agent-core/a2ui';
2
2
  import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver } from '@dudousxd/nestjs-agent-core';
3
3
  import { Request, Response } from 'express';
4
4
  import { UiCapabilities } from '@dudousxd/nestjs-agent-core/genui';
@@ -567,6 +567,8 @@ interface A2uiAdapterOptions extends A2uiStreamOptions {
567
567
  quietMs?: number;
568
568
  /** Largest action accepted, as JSON, in bytes. Default 8192. */
569
569
  maxActionBytes?: number;
570
+ /** Largest `a2uiClientDataModel` accepted, as JSON, in bytes. Default 32768. */
571
+ maxDataModelBytes?: number;
570
572
  }
571
573
  /**
572
574
  * Serves the agent as an A2UI agent. Not mounted directly — {@link a2uiAdapter} mounts a subclass at
@@ -578,7 +580,17 @@ declare class A2uiRunHandler {
578
580
  protected readonly projection: A2uiStreamOptions;
579
581
  protected readonly quietMs: number | undefined;
580
582
  protected readonly maxActionBytes: number | undefined;
581
- constructor(agent: AgentService, actorResolver: ActorResolver, projection: A2uiStreamOptions, quietMs: number | undefined, maxActionBytes: number | undefined);
583
+ protected readonly maxDataModelBytes: number | undefined;
584
+ constructor(agent: AgentService, actorResolver: ActorResolver, projection: A2uiStreamOptions, quietMs: number | undefined, maxActionBytes: number | undefined, maxDataModelBytes?: number | undefined);
585
+ /**
586
+ * `GET <path>/a2ui/threads/:threadId`: a stored thread as `{ threadId, entries }`
587
+ * (`a2uiThreadReplay`) — what a client draws again after a reload. Owner-scoped; a thread nobody
588
+ * has answers `{ entries: [] }`. `?catalog=<id>` names the client's basic catalog id.
589
+ */
590
+ replay(req: Request, threadId: string, catalog: unknown): Promise<{
591
+ threadId: string;
592
+ entries: ReturnType<typeof a2uiThreadReplay>;
593
+ }>;
582
594
  handle(req: Request, res: Response, raw: unknown): Promise<void>;
583
595
  /** Open the JSON Lines response. */
584
596
  protected writeHead(res: Response, extra: Record<string, string>): void;
@@ -587,6 +599,7 @@ declare class A2uiRunHandler {
587
599
  threadId?: string;
588
600
  skip?: number;
589
601
  answered?: string[];
602
+ projection?: A2uiStreamOptions;
590
603
  }): Promise<void>;
591
604
  }
592
605
  /**
@@ -600,14 +613,23 @@ declare class A2uiRunHandler {
600
613
  * AgentModule.forRoot({ model, adapters: [a2uiAdapter()] })
601
614
  * ```
602
615
  *
603
- * The body is `{ threadId?, message?, action?, uiCapabilities?, agent? }`:
616
+ * The body is `{ threadId?, message?, action?, uiCapabilities?, a2uiClientCapabilities?,
617
+ * a2uiClientDataModel?, agent? }` (the two A2UI fields also read from `metadata`, A2A-style):
604
618
  * - `message` — the user's text;
605
619
  * - `action` — an A2UI client message (`{ version: 'v0.9', action: { name, surfaceId,
606
620
  * sourceComponentId, timestamp, context } }`, or v0.8's `{ userAction }`): it becomes the turn
607
621
  * (`uiActionText`). `agora.approve` / `agora.reject` with `context.interruptId` decide the
608
622
  * approval a previous stream ended on, and stream the rest of that run (an independent
609
623
  * proposal is decided through the proposal service and answered with its reply);
610
- * - `threadId` — continue that thread (its owner is checked), or start one under that id.
624
+ * - `threadId` — continue that thread (its owner is checked), or start one under that id;
625
+ * - `a2uiClientCapabilities` — the catalogs the client draws (`{ 'v0.9': { supportedCatalogIds } }`,
626
+ * what `MessageProcessor.getRendererCapabilities()` builds): the basic catalog is sent under the
627
+ * id the client lists for it;
628
+ * - `a2uiClientDataModel` — the data models of surfaces created with `sendDataModel`, handed to the
629
+ * prompt builder as `pageContext.a2uiDataModel`.
630
+ *
631
+ * `GET <path>/a2ui/threads/:threadId` answers a stored thread as `{ threadId, entries }`
632
+ * (`a2uiThreadReplay`): what a client draws again after a reload.
611
633
  *
612
634
  * Response headers name the library's ids: `X-Agent-Thread-Id`, `X-Agent-Run-Id`. Authenticated by
613
635
  * the module's `actorResolver`, guarded by its `guards` and owner-scoped exactly as `chat` is.
@@ -1,4 +1,4 @@
1
- import { A2uiStreamOptions } from '@dudousxd/nestjs-agent-core/a2ui';
1
+ import { A2uiStreamOptions, a2uiThreadReplay } from '@dudousxd/nestjs-agent-core/a2ui';
2
2
  import { AgentAttachmentConfig, AttachmentStagingStore, AgentStore, Actor, ListActionProposals, ActionProposalView, ActionProposalMutationView, TextActionProposalVocabulary, AgentRunner, ModelCatalog, QuotaProvider, ActionApprovalMode, MessageAttachment, QuotaReport, ModelCatalogView, AgentHostContext, AttachmentRef, PageContext, ChatQueueState, HumanReply, ThreadSummary, ThreadDetail, UpdateThreadInput, MessageFeedbackValue, MessageFeedback, StagedAttachment, ActorResolver } from '@dudousxd/nestjs-agent-core';
3
3
  import { Request, Response } from 'express';
4
4
  import { UiCapabilities } from '@dudousxd/nestjs-agent-core/genui';
@@ -567,6 +567,8 @@ interface A2uiAdapterOptions extends A2uiStreamOptions {
567
567
  quietMs?: number;
568
568
  /** Largest action accepted, as JSON, in bytes. Default 8192. */
569
569
  maxActionBytes?: number;
570
+ /** Largest `a2uiClientDataModel` accepted, as JSON, in bytes. Default 32768. */
571
+ maxDataModelBytes?: number;
570
572
  }
571
573
  /**
572
574
  * Serves the agent as an A2UI agent. Not mounted directly — {@link a2uiAdapter} mounts a subclass at
@@ -578,7 +580,17 @@ declare class A2uiRunHandler {
578
580
  protected readonly projection: A2uiStreamOptions;
579
581
  protected readonly quietMs: number | undefined;
580
582
  protected readonly maxActionBytes: number | undefined;
581
- constructor(agent: AgentService, actorResolver: ActorResolver, projection: A2uiStreamOptions, quietMs: number | undefined, maxActionBytes: number | undefined);
583
+ protected readonly maxDataModelBytes: number | undefined;
584
+ constructor(agent: AgentService, actorResolver: ActorResolver, projection: A2uiStreamOptions, quietMs: number | undefined, maxActionBytes: number | undefined, maxDataModelBytes?: number | undefined);
585
+ /**
586
+ * `GET <path>/a2ui/threads/:threadId`: a stored thread as `{ threadId, entries }`
587
+ * (`a2uiThreadReplay`) — what a client draws again after a reload. Owner-scoped; a thread nobody
588
+ * has answers `{ entries: [] }`. `?catalog=<id>` names the client's basic catalog id.
589
+ */
590
+ replay(req: Request, threadId: string, catalog: unknown): Promise<{
591
+ threadId: string;
592
+ entries: ReturnType<typeof a2uiThreadReplay>;
593
+ }>;
582
594
  handle(req: Request, res: Response, raw: unknown): Promise<void>;
583
595
  /** Open the JSON Lines response. */
584
596
  protected writeHead(res: Response, extra: Record<string, string>): void;
@@ -587,6 +599,7 @@ declare class A2uiRunHandler {
587
599
  threadId?: string;
588
600
  skip?: number;
589
601
  answered?: string[];
602
+ projection?: A2uiStreamOptions;
590
603
  }): Promise<void>;
591
604
  }
592
605
  /**
@@ -600,14 +613,23 @@ declare class A2uiRunHandler {
600
613
  * AgentModule.forRoot({ model, adapters: [a2uiAdapter()] })
601
614
  * ```
602
615
  *
603
- * The body is `{ threadId?, message?, action?, uiCapabilities?, agent? }`:
616
+ * The body is `{ threadId?, message?, action?, uiCapabilities?, a2uiClientCapabilities?,
617
+ * a2uiClientDataModel?, agent? }` (the two A2UI fields also read from `metadata`, A2A-style):
604
618
  * - `message` — the user's text;
605
619
  * - `action` — an A2UI client message (`{ version: 'v0.9', action: { name, surfaceId,
606
620
  * sourceComponentId, timestamp, context } }`, or v0.8's `{ userAction }`): it becomes the turn
607
621
  * (`uiActionText`). `agora.approve` / `agora.reject` with `context.interruptId` decide the
608
622
  * approval a previous stream ended on, and stream the rest of that run (an independent
609
623
  * proposal is decided through the proposal service and answered with its reply);
610
- * - `threadId` — continue that thread (its owner is checked), or start one under that id.
624
+ * - `threadId` — continue that thread (its owner is checked), or start one under that id;
625
+ * - `a2uiClientCapabilities` — the catalogs the client draws (`{ 'v0.9': { supportedCatalogIds } }`,
626
+ * what `MessageProcessor.getRendererCapabilities()` builds): the basic catalog is sent under the
627
+ * id the client lists for it;
628
+ * - `a2uiClientDataModel` — the data models of surfaces created with `sendDataModel`, handed to the
629
+ * prompt builder as `pageContext.a2uiDataModel`.
630
+ *
631
+ * `GET <path>/a2ui/threads/:threadId` answers a stored thread as `{ threadId, entries }`
632
+ * (`a2uiThreadReplay`): what a client draws again after a reload.
611
633
  *
612
634
  * Response headers name the library's ids: `X-Agent-Thread-Id`, `X-Agent-Run-Id`. Authenticated by
613
635
  * the module's `actorResolver`, guarded by its `guards` and owner-scoped exactly as `chat` is.
package/dist/index.cjs CHANGED
@@ -6607,6 +6607,7 @@ AgentModule = _ts_decorate27([
6607
6607
 
6608
6608
  // src/ag-ui/ag-ui.adapter.ts
6609
6609
  var import_nestjs_agent_core35 = require("@dudousxd/nestjs-agent-core");
6610
+ var import_a2ui = require("@dudousxd/nestjs-agent-core/a2ui");
6610
6611
  var import_ag_ui = require("@dudousxd/nestjs-agent-core/ag-ui");
6611
6612
  var import_genui3 = require("@dudousxd/nestjs-agent-core/genui");
6612
6613
  var import_common29 = require("@nestjs/common");
@@ -6720,6 +6721,7 @@ var AgUiRunHandler = class {
6720
6721
  skip: first.address.position,
6721
6722
  answered: plan.decisions.map((decision) => decision.address.toolCallId),
6722
6723
  ...quiet,
6724
+ ...this.a2uiFor(input),
6723
6725
  preamble: warningEvents(warnings)
6724
6726
  });
6725
6727
  return;
@@ -6808,6 +6810,7 @@ var AgUiRunHandler = class {
6808
6810
  streamRunId: started.runId,
6809
6811
  streamThreadId: started.threadId,
6810
6812
  ...quiet,
6813
+ ...this.a2uiFor(input),
6811
6814
  preamble: warningEvents(warnings)
6812
6815
  });
6813
6816
  }
@@ -6954,6 +6957,13 @@ var AgUiRunHandler = class {
6954
6957
  for (const event of events) res.write((0, import_ag_ui.agUiSse)(event));
6955
6958
  res.end();
6956
6959
  }
6960
+ /** A2UI for this request's client: the basic catalog under the id it advertises. Off when off. */
6961
+ a2uiFor(input) {
6962
+ if (this.a2ui === void 0) return {};
6963
+ return {
6964
+ a2ui: (0, import_a2ui.negotiateA2uiCatalog)(this.a2ui, (0, import_a2ui.readAgUiA2uiCatalogIds)(input), import_a2ui.A2UI_LEGACY_BASIC_CATALOG_ID)
6965
+ };
6966
+ }
6957
6967
  /**
6958
6968
  * Pipe a run to the client as AG-UI 1.0 over SSE: one event per `data:` line, from `RUN_STARTED`
6959
6969
  * to the run's terminal event. It ends when the run stops to ask — the library run stays parked,
@@ -6973,12 +6983,8 @@ var AgUiRunHandler = class {
6973
6983
  };
6974
6984
  try {
6975
6985
  const frames = (0, import_ag_ui.agUiFramesFromNdjson)(this.agent.subscribe(runId));
6976
- const a2ui = this.a2ui !== void 0 ? {
6977
- a2ui: this.a2ui
6978
- } : {};
6979
6986
  for await (const event of (0, import_ag_ui.agUiEvents)(frames, {
6980
6987
  ...options,
6981
- ...a2ui,
6982
6988
  cursor
6983
6989
  })) {
6984
6990
  if (event.type === "RUN_FINISHED" || event.type === "RUN_ERROR") terminal = true;
@@ -7062,7 +7068,7 @@ __name(withCatalog, "withCatalog");
7062
7068
 
7063
7069
  // src/a2ui/a2ui.adapter.ts
7064
7070
  var import_nestjs_agent_core36 = require("@dudousxd/nestjs-agent-core");
7065
- var import_a2ui = require("@dudousxd/nestjs-agent-core/a2ui");
7071
+ var import_a2ui2 = require("@dudousxd/nestjs-agent-core/a2ui");
7066
7072
  var import_ag_ui2 = require("@dudousxd/nestjs-agent-core/ag-ui");
7067
7073
  var import_genui4 = require("@dudousxd/nestjs-agent-core/genui");
7068
7074
  var import_common30 = require("@nestjs/common");
@@ -7105,6 +7111,28 @@ function invalid2(code, message) {
7105
7111
  });
7106
7112
  }
7107
7113
  __name(invalid2, "invalid");
7114
+ function bodyField(body, key) {
7115
+ if (body[key] !== void 0) return body[key];
7116
+ return isRecord(body.metadata) ? body.metadata[key] : void 0;
7117
+ }
7118
+ __name(bodyField, "bodyField");
7119
+ function readDataModel(raw, maxBytes) {
7120
+ if (raw === void 0) return void 0;
7121
+ if (!isRecord(raw) || !isRecord(raw.surfaces)) {
7122
+ return "a2uiClientDataModel carries { version, surfaces }";
7123
+ }
7124
+ let size;
7125
+ try {
7126
+ size = new TextEncoder().encode(JSON.stringify(raw.surfaces)).length;
7127
+ } catch {
7128
+ return "a2uiClientDataModel must be JSON";
7129
+ }
7130
+ if (size > maxBytes) {
7131
+ return `a2uiClientDataModel may be at most ${maxBytes} bytes (this one is ${size})`;
7132
+ }
7133
+ return Object.keys(raw.surfaces).length > 0 ? raw.surfaces : void 0;
7134
+ }
7135
+ __name(readDataModel, "readDataModel");
7108
7136
  var A2uiRunHandler = class {
7109
7137
  static {
7110
7138
  __name(this, "A2uiRunHandler");
@@ -7114,20 +7142,46 @@ var A2uiRunHandler = class {
7114
7142
  projection;
7115
7143
  quietMs;
7116
7144
  maxActionBytes;
7117
- constructor(agent, actorResolver, projection, quietMs, maxActionBytes) {
7145
+ maxDataModelBytes;
7146
+ constructor(agent, actorResolver, projection, quietMs, maxActionBytes, maxDataModelBytes = void 0) {
7118
7147
  this.agent = agent;
7119
7148
  this.actorResolver = actorResolver;
7120
7149
  this.projection = projection;
7121
7150
  this.quietMs = quietMs;
7122
7151
  this.maxActionBytes = maxActionBytes;
7152
+ this.maxDataModelBytes = maxDataModelBytes;
7153
+ }
7154
+ /**
7155
+ * `GET <path>/a2ui/threads/:threadId`: a stored thread as `{ threadId, entries }`
7156
+ * (`a2uiThreadReplay`) — what a client draws again after a reload. Owner-scoped; a thread nobody
7157
+ * has answers `{ entries: [] }`. `?catalog=<id>` names the client's basic catalog id.
7158
+ */
7159
+ async replay(req, threadId, catalog) {
7160
+ const actor = await this.actorResolver.resolve(req);
7161
+ const owner = await this.agent.threadOwner(threadId);
7162
+ if (owner === null) return {
7163
+ threadId,
7164
+ entries: []
7165
+ };
7166
+ const thread = await this.agent.getThread(actor, threadId);
7167
+ const supported = typeof catalog === "string" ? [
7168
+ catalog
7169
+ ] : Array.isArray(catalog) ? catalog.filter((id) => typeof id === "string") : void 0;
7170
+ return {
7171
+ threadId,
7172
+ entries: (0, import_a2ui2.a2uiThreadReplay)(thread?.messages ?? [], (0, import_a2ui2.negotiateA2uiCatalog)(this.projection, supported))
7173
+ };
7123
7174
  }
7124
7175
  async handle(req, res, raw) {
7125
7176
  const body = isRecord(raw) ? raw : {};
7126
7177
  const agentName = typeof body.agent === "string" && body.agent.length > 0 ? body.agent : void 0;
7127
7178
  const actor = await this.actorResolver.resolve(req);
7179
+ const projection = (0, import_a2ui2.negotiateA2uiCatalog)(this.projection, (0, import_a2ui2.readA2uiClientCapabilities)(bodyField(body, "a2uiClientCapabilities")));
7180
+ const dataModel = readDataModel(bodyField(body, "a2uiClientDataModel"), this.maxDataModelBytes ?? 32768);
7181
+ if (typeof dataModel === "string") throw invalid2("invalid_data_model", dataModel);
7128
7182
  let action;
7129
7183
  if (body.action !== void 0) {
7130
- const read = isRecord(body.action) ? (0, import_a2ui.readA2uiAction)(body.action, {
7184
+ const read = isRecord(body.action) ? (0, import_a2ui2.readA2uiAction)(body.action, {
7131
7185
  ...this.maxActionBytes !== void 0 ? {
7132
7186
  maxBytes: this.maxActionBytes
7133
7187
  } : {}
@@ -7135,7 +7189,7 @@ var A2uiRunHandler = class {
7135
7189
  if (typeof read === "string") throw invalid2("invalid_action", read);
7136
7190
  action = read;
7137
7191
  }
7138
- if (action?.name === import_a2ui.A2UI_APPROVE_ACTION || action?.name === import_a2ui.A2UI_REJECT_ACTION) {
7192
+ if (action?.name === import_a2ui2.A2UI_APPROVE_ACTION || action?.name === import_a2ui2.A2UI_REJECT_ACTION) {
7139
7193
  const interruptId = action.context.interruptId;
7140
7194
  const address = typeof interruptId === "string" ? (0, import_ag_ui2.decodeInterruptId)(interruptId) : void 0;
7141
7195
  if (address === void 0 || address === null || address.kind === "elicitation") {
@@ -7146,13 +7200,13 @@ var A2uiRunHandler = class {
7146
7200
  throw invalid2("invalid_action", "the interruptId names no proposal");
7147
7201
  }
7148
7202
  const decided = await this.agent.decideActionProposal(actor, address.threadId, address.proposalId, {
7149
- decision: action.name === import_a2ui.A2UI_APPROVE_ACTION ? "approved" : "rejected",
7203
+ decision: action.name === import_a2ui2.A2UI_APPROVE_ACTION ? "approved" : "rejected",
7150
7204
  via: A2UI_VIA
7151
7205
  });
7152
7206
  this.writeHead(res, {
7153
7207
  "X-Agent-Thread-Id": address.threadId
7154
7208
  });
7155
- const projector = new import_a2ui.A2uiProjector(this.projection);
7209
+ const projector = new import_a2ui2.A2uiProjector(projection);
7156
7210
  const messageId = `proposal-${address.proposalId}`;
7157
7211
  for (const event of [
7158
7212
  {
@@ -7174,7 +7228,7 @@ var A2uiRunHandler = class {
7174
7228
  }
7175
7229
  await this.agent.assertResumable(actor, address.stream);
7176
7230
  await this.agent.checkDecision(actor, address.toolCallId);
7177
- if (action.name === import_a2ui.A2UI_APPROVE_ACTION) {
7231
+ if (action.name === import_a2ui2.A2UI_APPROVE_ACTION) {
7178
7232
  await this.agent.approve(actor, address.toolCallId, {
7179
7233
  via: A2UI_VIA
7180
7234
  });
@@ -7187,7 +7241,8 @@ var A2uiRunHandler = class {
7187
7241
  skip: address.position,
7188
7242
  answered: [
7189
7243
  address.toolCallId
7190
- ]
7244
+ ],
7245
+ projection
7191
7246
  });
7192
7247
  return;
7193
7248
  }
@@ -7222,10 +7277,16 @@ var A2uiRunHandler = class {
7222
7277
  } : {},
7223
7278
  ...uiCapabilities !== void 0 ? {
7224
7279
  uiCapabilities
7280
+ } : {},
7281
+ ...dataModel !== void 0 ? {
7282
+ pageContext: {
7283
+ a2uiDataModel: dataModel
7284
+ }
7225
7285
  } : {}
7226
7286
  });
7227
7287
  await this.stream(res, started.runId, {
7228
- threadId: started.threadId
7288
+ threadId: started.threadId,
7289
+ projection
7229
7290
  });
7230
7291
  }
7231
7292
  /** Open the JSON Lines response. */
@@ -7246,7 +7307,7 @@ var A2uiRunHandler = class {
7246
7307
  "X-Agent-Thread-Id": options.threadId
7247
7308
  } : {}
7248
7309
  });
7249
- const projector = new import_a2ui.A2uiProjector(this.projection);
7310
+ const projector = new import_a2ui2.A2uiProjector(options.projection ?? this.projection);
7250
7311
  const write = /* @__PURE__ */ __name((messages) => {
7251
7312
  for (const message of messages) res.write(`${JSON.stringify(message)}
7252
7313
  `);
@@ -7283,7 +7344,7 @@ var A2uiRunHandler = class {
7283
7344
  };
7284
7345
  function a2uiAdapter(options = {}) {
7285
7346
  const path = (options.path ?? "a2ui").replace(/^\/+|\/+$/g, "");
7286
- const { path: _path, quietMs, maxActionBytes, ...projection } = options;
7347
+ const { path: _path, quietMs, maxActionBytes, maxDataModelBytes, ...projection } = options;
7287
7348
  let A2uiController = class A2uiController extends A2uiRunHandler {
7288
7349
  static {
7289
7350
  __name(this, "A2uiController");
@@ -7292,12 +7353,28 @@ function a2uiAdapter(options = {}) {
7292
7353
  super(agent, actorResolver, projection.catalog === void 0 && catalog !== void 0 ? {
7293
7354
  ...projection,
7294
7355
  catalog
7295
- } : projection, quietMs, maxActionBytes);
7356
+ } : projection, quietMs, maxActionBytes, maxDataModelBytes);
7357
+ }
7358
+ thread(req, threadId, catalog) {
7359
+ return this.replay(req, threadId, catalog);
7296
7360
  }
7297
7361
  run(req, res, body) {
7298
7362
  return this.handle(req, res, body);
7299
7363
  }
7300
7364
  };
7365
+ _ts_decorate29([
7366
+ (0, import_common30.Get)(`${path}/threads/:threadId`),
7367
+ _ts_param27(0, (0, import_common30.Req)()),
7368
+ _ts_param27(1, (0, import_common30.Param)("threadId")),
7369
+ _ts_param27(2, (0, import_common30.Query)("catalog")),
7370
+ _ts_metadata27("design:type", Function),
7371
+ _ts_metadata27("design:paramtypes", [
7372
+ typeof Request === "undefined" ? Object : Request,
7373
+ String,
7374
+ Object
7375
+ ]),
7376
+ _ts_metadata27("design:returntype", typeof ReturnType === "undefined" ? Object : ReturnType)
7377
+ ], A2uiController.prototype, "thread", null);
7301
7378
  _ts_decorate29([
7302
7379
  (0, import_common30.Post)(path),
7303
7380
  (0, import_common30.HttpCode)(200),