@mcp-abap-adt/llm-agent-server-libs 26.0.0 → 28.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/dist/builders/controller-skill-pipeline-builder.d.ts +6 -5
  2. package/dist/builders/controller-skill-pipeline-builder.d.ts.map +1 -1
  3. package/dist/builders/controller-skill-pipeline-builder.js +49 -54
  4. package/dist/builders/controller-skill-pipeline-builder.js.map +1 -1
  5. package/dist/factories/controller-factory.d.ts +13 -10
  6. package/dist/factories/controller-factory.d.ts.map +1 -1
  7. package/dist/factories/controller-factory.js +33 -12
  8. package/dist/factories/controller-factory.js.map +1 -1
  9. package/dist/generated/version.d.ts +1 -1
  10. package/dist/generated/version.js +1 -1
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/pipelines/controller-subagents.d.ts +11 -0
  16. package/dist/pipelines/controller-subagents.d.ts.map +1 -0
  17. package/dist/pipelines/controller-subagents.js +77 -0
  18. package/dist/pipelines/controller-subagents.js.map +1 -0
  19. package/dist/pipelines/controller.d.ts +11 -10
  20. package/dist/pipelines/controller.d.ts.map +1 -1
  21. package/dist/pipelines/controller.js +14 -65
  22. package/dist/pipelines/controller.js.map +1 -1
  23. package/dist/pipelines/coordinator-resolvers.d.ts +6 -11
  24. package/dist/pipelines/coordinator-resolvers.d.ts.map +1 -1
  25. package/dist/pipelines/coordinator-resolvers.js +7 -17
  26. package/dist/pipelines/coordinator-resolvers.js.map +1 -1
  27. package/dist/pipelines/dag.d.ts +9 -9
  28. package/dist/pipelines/dag.d.ts.map +1 -1
  29. package/dist/pipelines/dag.js +11 -22
  30. package/dist/pipelines/dag.js.map +1 -1
  31. package/dist/pipelines/flat.d.ts +4 -7
  32. package/dist/pipelines/flat.d.ts.map +1 -1
  33. package/dist/pipelines/flat.js +3 -8
  34. package/dist/pipelines/flat.js.map +1 -1
  35. package/dist/pipelines/linear.d.ts +8 -10
  36. package/dist/pipelines/linear.d.ts.map +1 -1
  37. package/dist/pipelines/linear.js +18 -11
  38. package/dist/pipelines/linear.js.map +1 -1
  39. package/dist/pipelines/parsers.d.ts +0 -3
  40. package/dist/pipelines/parsers.d.ts.map +1 -1
  41. package/dist/pipelines/parsers.js +4 -19
  42. package/dist/pipelines/parsers.js.map +1 -1
  43. package/dist/pipelines/server-context.d.ts +0 -5
  44. package/dist/pipelines/server-context.d.ts.map +1 -1
  45. package/dist/pipelines/server-context.js.map +1 -1
  46. package/dist/pipelines/stepper.d.ts +5 -4
  47. package/dist/pipelines/stepper.d.ts.map +1 -1
  48. package/dist/pipelines/stepper.js +5 -4
  49. package/dist/pipelines/stepper.js.map +1 -1
  50. package/dist/smart-agent/build-dag-coordinator-deps.d.ts +11 -22
  51. package/dist/smart-agent/build-dag-coordinator-deps.d.ts.map +1 -1
  52. package/dist/smart-agent/build-dag-coordinator-deps.js +21 -78
  53. package/dist/smart-agent/build-dag-coordinator-deps.js.map +1 -1
  54. package/dist/smart-agent/build-stepper-root.d.ts.map +1 -1
  55. package/dist/smart-agent/build-stepper-root.js +0 -1
  56. package/dist/smart-agent/build-stepper-root.js.map +1 -1
  57. package/dist/smart-agent/config-validator.d.ts +8 -1
  58. package/dist/smart-agent/config-validator.d.ts.map +1 -1
  59. package/dist/smart-agent/config-validator.js +200 -62
  60. package/dist/smart-agent/config-validator.js.map +1 -1
  61. package/dist/smart-agent/config.d.ts +7 -7
  62. package/dist/smart-agent/config.d.ts.map +1 -1
  63. package/dist/smart-agent/config.js +24 -11
  64. package/dist/smart-agent/config.js.map +1 -1
  65. package/dist/smart-agent/controller/types.d.ts +22 -15
  66. package/dist/smart-agent/controller/types.d.ts.map +1 -1
  67. package/dist/smart-agent/controller/types.js.map +1 -1
  68. package/dist/smart-agent/http/config-route-handler.d.ts +2 -0
  69. package/dist/smart-agent/http/config-route-handler.d.ts.map +1 -1
  70. package/dist/smart-agent/http/config-route-handler.js +20 -0
  71. package/dist/smart-agent/http/config-route-handler.js.map +1 -1
  72. package/dist/smart-agent/llm/role-llm-resolver.d.ts +23 -11
  73. package/dist/smart-agent/llm/role-llm-resolver.d.ts.map +1 -1
  74. package/dist/smart-agent/llm/role-llm-resolver.js +65 -37
  75. package/dist/smart-agent/llm/role-llm-resolver.js.map +1 -1
  76. package/dist/smart-agent/llm-config-map.d.ts +9 -1
  77. package/dist/smart-agent/llm-config-map.d.ts.map +1 -1
  78. package/dist/smart-agent/llm-config-map.js +27 -5
  79. package/dist/smart-agent/llm-config-map.js.map +1 -1
  80. package/dist/smart-agent/pipeline-settings.d.ts +50 -0
  81. package/dist/smart-agent/pipeline-settings.d.ts.map +1 -0
  82. package/dist/smart-agent/pipeline-settings.js +174 -0
  83. package/dist/smart-agent/pipeline-settings.js.map +1 -0
  84. package/dist/smart-agent/pipeline.d.ts +3 -63
  85. package/dist/smart-agent/pipeline.d.ts.map +1 -1
  86. package/dist/smart-agent/pipeline.js +3 -2
  87. package/dist/smart-agent/pipeline.js.map +1 -1
  88. package/dist/smart-agent/rag-config.d.ts +155 -0
  89. package/dist/smart-agent/rag-config.d.ts.map +1 -0
  90. package/dist/smart-agent/rag-config.js +62 -0
  91. package/dist/smart-agent/rag-config.js.map +1 -0
  92. package/dist/smart-agent/resolve-agent-embedder.d.ts +11 -10
  93. package/dist/smart-agent/resolve-agent-embedder.d.ts.map +1 -1
  94. package/dist/smart-agent/resolve-agent-embedder.js +37 -36
  95. package/dist/smart-agent/resolve-agent-embedder.js.map +1 -1
  96. package/dist/smart-agent/resolve-config-sections.d.ts +1 -1
  97. package/dist/smart-agent/resolve-config-sections.d.ts.map +1 -1
  98. package/dist/smart-agent/resolve-config-sections.js +124 -30
  99. package/dist/smart-agent/resolve-config-sections.js.map +1 -1
  100. package/dist/smart-agent/select-pipeline-plugin.d.ts +8 -0
  101. package/dist/smart-agent/select-pipeline-plugin.d.ts.map +1 -0
  102. package/dist/smart-agent/select-pipeline-plugin.js +26 -0
  103. package/dist/smart-agent/select-pipeline-plugin.js.map +1 -0
  104. package/dist/smart-agent/session-lifecycle/index.d.ts +38 -4
  105. package/dist/smart-agent/session-lifecycle/index.d.ts.map +1 -1
  106. package/dist/smart-agent/session-lifecycle/index.js +11 -1
  107. package/dist/smart-agent/session-lifecycle/index.js.map +1 -1
  108. package/dist/smart-agent/session-lifecycle/session-rag-registry.d.ts +39 -0
  109. package/dist/smart-agent/session-lifecycle/session-rag-registry.d.ts.map +1 -0
  110. package/dist/smart-agent/session-lifecycle/session-rag-registry.js +109 -0
  111. package/dist/smart-agent/session-lifecycle/session-rag-registry.js.map +1 -0
  112. package/dist/smart-agent/skill-plugins-config.d.ts +3 -2
  113. package/dist/smart-agent/skill-plugins-config.d.ts.map +1 -1
  114. package/dist/smart-agent/skill-plugins-config.js +12 -1
  115. package/dist/smart-agent/skill-plugins-config.js.map +1 -1
  116. package/dist/smart-agent/skill-plugins-host-factory.d.ts +9 -0
  117. package/dist/smart-agent/skill-plugins-host-factory.d.ts.map +1 -1
  118. package/dist/smart-agent/skill-plugins-host-factory.js +12 -6
  119. package/dist/smart-agent/skill-plugins-host-factory.js.map +1 -1
  120. package/dist/smart-agent/smart-server.d.ts +90 -78
  121. package/dist/smart-agent/smart-server.d.ts.map +1 -1
  122. package/dist/smart-agent/smart-server.js +201 -152
  123. package/dist/smart-agent/smart-server.js.map +1 -1
  124. package/dist/smart-agent/worker-llm.d.ts +18 -0
  125. package/dist/smart-agent/worker-llm.d.ts.map +1 -0
  126. package/dist/smart-agent/worker-llm.js +59 -0
  127. package/dist/smart-agent/worker-llm.js.map +1 -0
  128. package/dist/smart-agent/workers/worker-registry.d.ts +6 -14
  129. package/dist/smart-agent/workers/worker-registry.d.ts.map +1 -1
  130. package/dist/smart-agent/workers/worker-registry.js +2 -11
  131. package/dist/smart-agent/workers/worker-registry.js.map +1 -1
  132. package/dist/smart-agent/yaml-loader.d.ts +1 -1
  133. package/dist/smart-agent/yaml-loader.d.ts.map +1 -1
  134. package/dist/smart-agent/yaml-loader.js +20 -17
  135. package/dist/smart-agent/yaml-loader.js.map +1 -1
  136. package/package.json +9 -8
  137. package/dist/smart-agent/mcp/mcp-content.d.ts +0 -19
  138. package/dist/smart-agent/mcp/mcp-content.d.ts.map +0 -1
  139. package/dist/smart-agent/mcp/mcp-content.js +0 -31
  140. package/dist/smart-agent/mcp/mcp-content.js.map +0 -1
  141. package/dist/smart-agent/mcp/require-mcp-timeout.d.ts +0 -20
  142. package/dist/smart-agent/mcp/require-mcp-timeout.d.ts.map +0 -1
  143. package/dist/smart-agent/mcp/require-mcp-timeout.js +0 -23
  144. package/dist/smart-agent/mcp/require-mcp-timeout.js.map +0 -1
  145. package/dist/smart-agent/mcp-readiness-monitor.d.ts +0 -38
  146. package/dist/smart-agent/mcp-readiness-monitor.d.ts.map +0 -1
  147. package/dist/smart-agent/mcp-readiness-monitor.js +0 -81
  148. package/dist/smart-agent/mcp-readiness-monitor.js.map +0 -1
  149. package/dist/smart-agent/mcp-readiness-registry.d.ts +0 -37
  150. package/dist/smart-agent/mcp-readiness-registry.d.ts.map +0 -1
  151. package/dist/smart-agent/mcp-readiness-registry.js +0 -55
  152. package/dist/smart-agent/mcp-readiness-registry.js.map +0 -1
  153. package/dist/smart-agent/skill-plugins-config.test.d.ts +0 -2
  154. package/dist/smart-agent/skill-plugins-config.test.d.ts.map +0 -1
  155. package/dist/smart-agent/skill-plugins-config.test.js.map +0 -1
  156. package/dist/smart-agent/skill-plugins-host-factory.test.d.ts +0 -2
  157. package/dist/smart-agent/skill-plugins-host-factory.test.d.ts.map +0 -1
  158. package/dist/smart-agent/skill-plugins-host-factory.test.js.map +0 -1
@@ -6,10 +6,10 @@ import http from 'node:http';
6
6
  import { createRequire } from 'node:module';
7
7
  import { resolve as pathResolve } from 'node:path';
8
8
  import { pathToFileURL } from 'node:url';
9
- import { buildNamespacedTools, defaultToolNamespace, isReadinessReporter, } from '@mcp-abap-adt/llm-agent';
10
- import { ClaudeSkillManager, CodexSkillManager, FileSystemPluginLoader, FileSystemSkillManager, getDefaultPluginDirs, HealthChecker, InMemoryKnowledgeBackend, KnowledgeRag, makeLlm, mergePluginExports, SessionRequestLogger, SmartAgentBuilder, SmartAgentSubAgent, WindowContextStrategy, } from '@mcp-abap-adt/llm-agent-libs';
9
+ import { buildNamespacedTools, defaultToolNamespace, isReadinessReporter, SimpleRagProviderRegistry, } from '@mcp-abap-adt/llm-agent';
10
+ import { ClaudeSkillManager, CodexSkillManager, FileSystemPluginLoader, FileSystemSkillManager, getDefaultPluginDirs, HealthChecker, InMemoryKnowledgeBackend, KnowledgeRag, mergePluginExports, SessionRequestLogger, SmartAgentBuilder, SmartAgentSubAgent, WindowContextStrategy, } from '@mcp-abap-adt/llm-agent-libs';
11
11
  import { DefaultMcpFailureClassifier, MCPClientWrapper, McpClientAdapter, } from '@mcp-abap-adt/llm-agent-mcp';
12
- import { makeRag, prefetchEmbedderFactories, resolveEmbedder, } from '@mcp-abap-adt/llm-agent-rag';
12
+ import { prefetchEmbedderFactories } from '@mcp-abap-adt/llm-agent-rag';
13
13
  import { PACKAGE_VERSION } from '../generated/version.js';
14
14
  import { ConfigReloadWatcher } from './config-reload-watcher.js';
15
15
  import { handleAdapterRequest } from './http/adapter-route-handler.js';
@@ -21,9 +21,11 @@ import { CORS_HEADERS, jsonError, writeNotReady, } from './http/response-helpers
21
21
  import { HttpRouteTable } from './http/route-table.js';
22
22
  import { handleSessionDelete, handleSessionResume, handleSessionsList, } from './http/sessions-route-handler.js';
23
23
  import { handleUsageRoute } from './http/usage-route-handler.js';
24
- import { makeDefaultRoleLlm, RoleLlmResolver, } from './llm/role-llm-resolver.js';
24
+ import { RoleLlmResolver, } from './llm/role-llm-resolver.js';
25
+ import { assertRagConfigShape, embedderSectionFor, toMakeRagInput, } from './rag-config.js';
25
26
  import { resolveAgentEmbedder } from './resolve-agent-embedder.js';
26
27
  import { makeToolsRagHandle } from './tools-rag-handle.js';
28
+ import { assertWorkerLlmConfig, parseWorkerLlm } from './worker-llm.js';
27
29
  export { writeNotReady } from './http/response-helpers.js';
28
30
  // ---------------------------------------------------------------------------
29
31
  // Helpers
@@ -56,9 +58,12 @@ import { createServerPipelineContext, } from '../pipelines/server-context.js';
56
58
  import { StepperPipelinePlugin } from '../pipelines/stepper.js';
57
59
  import { normalizeLlmConfig, resolveLlmConfig, resolveLlmConfigStrict, resolveToolSelectionStrategy, } from './config.js';
58
60
  import { makeKnowledgeBackend } from './knowledge/make-knowledge-backend.js';
61
+ import { llmKeySet } from './llm-config-map.js';
59
62
  import { buildSessionMcpClients, serverOwnsMcpConnection, shouldIsolateMcpPerSession, } from './mcp/build-session-mcp-clients.js';
60
63
  import { buildNamespacedMcpBridge, rebindProvenanceToClients, } from './mcp/namespaced-bridge.js';
61
64
  import { makePgPool, makePgReadPool } from './pg-pool.js';
65
+ import { assertNamedLlmKeys, dagNamedLlmKeys, parseControllerSettings, parseDagSettings, parseLinearSettings, parseStepperSettings, } from './pipeline-settings.js';
66
+ import { selectPipelinePlugin } from './select-pipeline-plugin.js';
62
67
  import { InMemorySessionMetaStore } from './session-meta-store.js';
63
68
  import { buildSkillHostFromConfig, initSkillHost, } from './skill-plugins-host-factory.js';
64
69
  export { generateConfigTemplate, loadYamlConfig, resolveCoordinatorActivation, resolveCoordinatorDispatch, resolveCoordinatorPlanning, resolveEnvVars, resolveSmartServerConfig, resolveToolSelectionStrategy, YAML_TEMPLATE, } from './config.js';
@@ -75,8 +80,8 @@ import { backfillWorkerCacheFromHandle, resolveWorkerLlmSet, WorkerRegistry, } f
75
80
  // Internal callers (SmartServer methods) import the value symbols they call;
76
81
  // re-export the full public surface for the package barrel.
77
82
  // ---------------------------------------------------------------------------
78
- import { buildSessionLifecycle, recordSessionEnd, recordSessionStart, resolveSubAgentRagRegistry, seedSessionKnowledge, } from './session-lifecycle/index.js';
79
- export { buildSessionLifecycle, handleDeleteSession, handleListSessions, handleResumeSession, recordSessionEnd, recordSessionStart, resolveSubAgentRagRegistry, seedSessionKnowledge, } from './session-lifecycle/index.js';
83
+ import { buildSessionLifecycle, buildSessionRagRegistry, recordSessionEnd, recordSessionStart, resolveSubAgentRagRegistry, seedSessionKnowledge, } from './session-lifecycle/index.js';
84
+ export { buildSessionLifecycle, buildSessionRagRegistry, handleDeleteSession, handleListSessions, handleResumeSession, recordSessionEnd, recordSessionStart, resolveSubAgentRagRegistry, seedSessionKnowledge, } from './session-lifecycle/index.js';
80
85
  // ---------------------------------------------------------------------------
81
86
  // MCP bridge for the Stepper path (B-1)
82
87
  // ---------------------------------------------------------------------------
@@ -191,6 +196,25 @@ export function buildMcpBridge(clients, classifier = new DefaultMcpFailureClassi
191
196
  return { text: `Tool not found: ${name}`, isError: true };
192
197
  };
193
198
  }
199
+ /**
200
+ * The seams the library no longer defaults. The type already makes them required;
201
+ * this runs for callers with no types to check, so a plain-JS embedder of
202
+ * SmartServer is told at construction instead of starting without an LLM.
203
+ */
204
+ const REQUIRED_CONSTRUCTION_SEAMS = [
205
+ 'makeLlm',
206
+ 'resolveEmbedder',
207
+ 'makeRag',
208
+ ];
209
+ function assertConstructionSeams(deps) {
210
+ const missing = REQUIRED_CONSTRUCTION_SEAMS.filter((k) => typeof deps?.[k] !== 'function');
211
+ if (missing.length === 0)
212
+ return;
213
+ throw new Error(`${missing.map((k) => `BuildAgentDeps.${k}`).join(', ')} ` +
214
+ `${missing.length > 1 ? 'are' : 'is'} required: the library constructs no ` +
215
+ 'LLM, embedder or store from configuration, because doing so meant carrying ' +
216
+ 'a secret through a framework config. Supply them from your composition root.');
217
+ }
194
218
  export class SmartServer {
195
219
  cfg;
196
220
  noop = () => { };
@@ -238,7 +262,6 @@ export class SmartServer {
238
262
  * `start()` so buildServerCtx can hand the raw role-LLM materials to the
239
263
  * context factory (mirrors the inline DAG/linear resolution). */
240
264
  _llmMap;
241
- _pipelineFallback;
242
265
  _mainTemp;
243
266
  _roleLlm;
244
267
  _requestLogger;
@@ -253,6 +276,21 @@ export class SmartServer {
253
276
  * (the stepper catalog handle), which falls back to catalog order regardless.
254
277
  */
255
278
  _toolsRag;
279
+ /**
280
+ * The providers every build of this server creates through, and the catalogs
281
+ * a session registry is hydrated from. One object, handed to every build:
282
+ * without it each session build replaced the registry's provider registry
283
+ * with a fresh empty one.
284
+ */
285
+ _ragProviderRegistry = new SimpleRagProviderRegistry();
286
+ /**
287
+ * Catalog findings (rejected rows, skipped globals) already logged by one of
288
+ * this server's sessions, so the next session does not repeat them. One per
289
+ * server, never module-global: see SessionRagRegistryInput.reported.
290
+ */
291
+ _reportedCatalogFindings = new Set();
292
+ /** The deployment's registry, whose globals every session registry holds. */
293
+ _globalRagRegistry;
256
294
  /**
257
295
  * MCP clients connected for the Stepper path from the YAML `mcp:` config
258
296
  * block. These are connected ONCE in `start()` (lazily resolved by
@@ -335,12 +373,12 @@ export class SmartServer {
335
373
  */
336
374
  _sessionMetaStore = new InMemorySessionMetaStore();
337
375
  /**
338
- * Pipeline-plugin registry, populated in `start()` after plugins load: the 6
339
- * built-ins (flat/linear/dag/stepper/controller/controller-weak) plus any
340
- * `plugins.pipelinePlugins`, fail-fast on name collision.
341
- * `buildPipelineInstance` selects by `cfg.pipeline.name` (default 'flat').
376
+ * The ONE pipeline plugin this server runs: its factory was selected by
377
+ * `cfg.pipeline.name` (default 'flat') and called once in `_buildInfra`, with
378
+ * `cfg.pipeline.config` parsed by the server (built-ins) or by the plugin author's
379
+ * factory (dynamic). `buildPipelineInstance` only builds it per session.
342
380
  */
343
- _pipelineRegistry;
381
+ _pipelinePlugin;
344
382
  /**
345
383
  * Per-session `IPipelineInstance.close()` hooks, keyed by sessionId. Populated
346
384
  * by `buildPipelineInstance` (via `buildSessionAgent`) and invoked from the
@@ -374,8 +412,13 @@ export class SmartServer {
374
412
  * and `embedder` stay optional (present only when injected).
375
413
  */
376
414
  _deps;
377
- constructor(config, deps = {}) {
415
+ constructor(config, deps) {
416
+ assertConstructionSeams(deps);
378
417
  this.cfg = config;
418
+ assertRagConfigShape(config.rag, 'rag');
419
+ for (const sub of config.subAgentConfigs ?? []) {
420
+ assertRagConfigShape(sub.config.rag, `subagent '${sub.name}' rag`);
421
+ }
379
422
  this._mcpSeamInjected =
380
423
  deps.mcpClients !== undefined ||
381
424
  deps.connectMcp !== undefined ||
@@ -398,8 +441,9 @@ export class SmartServer {
398
441
  this._waitStrategy = deps.waitStrategy;
399
442
  this._toolNamespace = deps.toolNamespace ?? defaultToolNamespace;
400
443
  this._deps = {
401
- makeLlm: deps.makeLlm ?? ((cfg) => this._makeLlmDefault(cfg)),
402
- resolveEmbedder: deps.resolveEmbedder ?? resolveEmbedder,
444
+ makeLlm: deps.makeLlm,
445
+ resolveEmbedder: deps.resolveEmbedder,
446
+ makeRag: deps.makeRag,
403
447
  prefetchEmbedderFactories: deps.prefetchEmbedderFactories ?? prefetchEmbedderFactories,
404
448
  buildSkillHost: deps.buildSkillHost ?? buildSkillHostFromConfig,
405
449
  connectMcp: deps.connectMcp ?? connectMcpClientsFromConfig,
@@ -452,26 +496,24 @@ export class SmartServer {
452
496
  };
453
497
  this._fileLogger = fileLogger;
454
498
  // ---- Composition root: resolve config → interfaces --------------------
455
- // LLM resolution — normalize the flat/map top-level `llm:` block. The legacy
456
- // per-pipeline `pipeline.llm.*` override is gone; role LLMs derive entirely
457
- // from the top-level map (resolveLlmConfig falls back to map.main), so the
458
- // pipelineFallback chain is no longer fed a separate config — it stays
459
- // undefined and the map.main fallback in resolveLlmConfig covers it.
499
+ // LLM resolution — normalize the flat/map top-level `llm:` block.
460
500
  const llmMap = normalizeLlmConfig(this.cfg.llm);
461
- const pipelineFallback = undefined;
462
- const topMain = resolveLlmConfig(llmMap, 'main', pipelineFallback);
501
+ const topMain = resolveLlmConfig(llmMap, 'main');
463
502
  const mainTemp = Number(topMain?.temperature ?? 0.7);
464
503
  const mainLlm = topMain
465
504
  ? await this._deps.makeLlm({ ...topMain, temperature: mainTemp })
466
505
  : (() => {
467
506
  throw new Error('no LLM configured: provide top-level llm.main');
468
507
  })();
508
+ const classifierEntry = resolveLlmConfigStrict(llmMap, 'classifier');
469
509
  const classifierTemp = Number(topMain?.classifierTemperature ?? 0.1);
470
- const classifierLlm = topMain
471
- ? await this._deps.makeLlm({ ...topMain, temperature: classifierTemp })
472
- : (() => {
473
- throw new Error('no LLM configured: provide top-level llm.main');
474
- })();
510
+ const classifierLlm = classifierEntry
511
+ ? await this._deps.makeLlm(classifierEntry)
512
+ : topMain
513
+ ? await this._deps.makeLlm({ ...topMain, temperature: classifierTemp })
514
+ : (() => {
515
+ throw new Error('no LLM configured: provide top-level llm.main');
516
+ })();
475
517
  // A 'helper' role LLM derives from the top-level `llm:` map when present
476
518
  // (built only when an explicit map entry exists).
477
519
  const helperCfg = resolveLlmConfigStrict(llmMap, 'helper');
@@ -485,16 +527,17 @@ export class SmartServer {
485
527
  this._classifierLlm = classifierLlm;
486
528
  this._helperLlm = helperLlm;
487
529
  this._llmMap = llmMap;
488
- this._pipelineFallback = pipelineFallback;
489
530
  this._mainTemp = mainTemp;
490
531
  this._roleLlm = new RoleLlmResolver({
491
532
  getMain: () => this._mainLlm,
492
533
  getHelper: () => this._helperLlm,
493
534
  getClassifier: () => this._classifierLlm,
494
535
  getLlmMap: () => this._llmMap,
495
- getPipelineFallback: () => this._pipelineFallback,
496
- makeLlm: (lc) => this._deps.makeLlm(lc),
536
+ build: (entry) => this._deps.makeLlm(entry),
497
537
  });
538
+ // The programmatic subAgentConfigs path never passed the YAML check, so a
539
+ // worker's named keys are checked here too (§4.6.7): startup fails loudly.
540
+ assertWorkerLlmConfig(this.cfg.subAgentConfigs, llmMap);
498
541
  // ---- Plugin loader -------------------------------------------------------
499
542
  const pluginLoader = this.cfg.pluginLoader ??
500
543
  (() => {
@@ -542,34 +585,53 @@ export class SmartServer {
542
585
  const registered = mergePluginExports(plugins, mod, spec);
543
586
  log({ event: 'plugin_specifier_loaded', spec, registered });
544
587
  }
545
- // ---- Pipeline-plugin registry (sub-goal C) ---------------------------
546
- // The 6 built-ins are STATIC; plugin-supplied pipelines are merged on top.
547
- // Fail-fast on a name collision so a plugin cannot silently shadow a
548
- // built-in (or another plugin). `buildPipelineInstance` selects by
549
- // `cfg.pipeline.name` (default 'flat') at session-build time.
550
- const pipelineRegistry = new Map();
551
- for (const builtin of [
552
- new FlatPipelinePlugin(),
553
- new LinearPipelinePlugin(),
554
- new DagPipelinePlugin(),
555
- new StepperPipelinePlugin(),
556
- new ControllerPipelinePlugin('controller', 'smart-executor'),
557
- new ControllerPipelinePlugin('controller-weak', 'weak-executor'),
558
- ]) {
559
- pipelineRegistry.set(builtin.name, builtin);
560
- }
561
- for (const [name, plugin] of plugins.pipelinePlugins) {
588
+ // ---- Pipeline-plugin registry: factories (§4.6.7) -------------------
589
+ // Built-ins are server code — parse, validate, construct with typed settings.
590
+ // A dynamic instance export is registered as a factory that ignores its
591
+ // section; a dynamic factory is the plugin author's parser. Only the selected
592
+ // entry is ever called, once, below.
593
+ const warn = (m) => this.warn(m);
594
+ const pipelineRegistry = new Map([
595
+ ['flat', () => new FlatPipelinePlugin()],
596
+ ['linear', (s) => new LinearPipelinePlugin(parseLinearSettings(s))],
597
+ [
598
+ 'dag',
599
+ (s) => {
600
+ const settings = parseDagSettings(s, warn);
601
+ assertNamedLlmKeys(dagNamedLlmKeys(settings), this._llmMap, "pipeline 'dag'");
602
+ return new DagPipelinePlugin(settings);
603
+ },
604
+ ],
605
+ ['stepper', (s) => new StepperPipelinePlugin(parseStepperSettings(s))],
606
+ [
607
+ 'controller',
608
+ (s) => new ControllerPipelinePlugin('controller', 'smart-executor', parseControllerSettings(s, llmKeySet(this._llmMap))),
609
+ ],
610
+ [
611
+ 'controller-weak',
612
+ (s) => new ControllerPipelinePlugin('controller-weak', 'weak-executor', parseControllerSettings(s, llmKeySet(this._llmMap))),
613
+ ],
614
+ ]);
615
+ const pipelineSources = new Map([...pipelineRegistry.keys()].map((k) => [k, 'built-in']));
616
+ const registerPipeline = (name, factory) => {
617
+ const source = plugins.pipelinePluginSources.get(name) ?? 'unknown';
562
618
  if (pipelineRegistry.has(name)) {
563
619
  throw new Error(`pipeline plugin name collision: '${name}' is already registered ` +
564
- '(built-in or another plugin)');
620
+ `(built-in or another plugin) — '${source}' against '${pipelineSources.get(name)}'`);
565
621
  }
566
- pipelineRegistry.set(name, plugin);
622
+ pipelineRegistry.set(name, factory);
623
+ pipelineSources.set(name, source);
624
+ };
625
+ for (const [name, plugin] of plugins.pipelinePlugins)
626
+ registerPipeline(name, () => plugin);
627
+ for (const [name, factory] of plugins.pipelinePluginFactories ?? []) {
628
+ registerPipeline(name, factory);
567
629
  }
568
- this._pipelineRegistry = pipelineRegistry;
569
630
  log({
570
631
  event: 'pipeline_registry_loaded',
571
632
  pipelines: [...pipelineRegistry.keys()],
572
633
  });
634
+ this._pipelinePlugin = selectPipelinePlugin(pipelineRegistry, pipelineSources, this.cfg.pipeline?.name ?? 'flat', this.cfg.pipeline?.config ?? {});
573
635
  // Merge plugin embedder factories with config-provided ones
574
636
  const mergedEmbedderFactories = {
575
637
  ...plugins.embedderFactories,
@@ -588,7 +650,7 @@ export class SmartServer {
588
650
  });
589
651
  // Resolve the embedder ONCE so the same instance feeds both makeRag and the
590
652
  // subagent context-builder's toolSource (#137). See resolve-agent-embedder.
591
- const resolvedEmbedder = await resolveAgentEmbedder(this.cfg.rag, this._deps.embedder ?? this.cfg.embedder, mergedEmbedderFactories, this._fileLogger);
653
+ const resolvedEmbedder = await resolveAgentEmbedder(this.cfg.rag, this._deps.embedder ?? this.cfg.embedder, this._deps.resolveEmbedder, mergedEmbedderFactories, this._fileLogger);
592
654
  // Hold the resolved embedder so buildServerCtx can thread it onto every
593
655
  // pipeline context (the controller pipeline needs it for target-state).
594
656
  this._resolvedEmbedder = resolvedEmbedder;
@@ -607,9 +669,10 @@ export class SmartServer {
607
669
  // Prefetch the named embedder factory only when we will actually build a
608
670
  // dedicated one (the agent embedder is already prefetched + wrapped).
609
671
  if (!reuseAgentEmbedder) {
610
- await this._deps.prefetchEmbedderFactories([
611
- skillCfg.embedder?.provider ?? 'ollama',
612
- ]);
672
+ const section = embedderSectionFor(skillCfg.embedder?.provider);
673
+ if (section.factory === undefined) {
674
+ await this._deps.prefetchEmbedderFactories([section.provider]);
675
+ }
613
676
  }
614
677
  // Build → load → validate as one fail-fast unit. If ANY step throws, the
615
678
  // captured pg pools are ended INSIDE initSkillHost (the later closeFns
@@ -620,9 +683,7 @@ export class SmartServer {
620
683
  : () => this._deps.buildSkillHost(skillCfg, {
621
684
  resolveEmbedder: (ec) => reuseAgentEmbedder
622
685
  ? (injectedEmbedder ?? resolvedEmbedder)
623
- : this._deps.resolveEmbedder(ec, {
624
- extraFactories: mergedEmbedderFactories,
625
- }),
686
+ : this._deps.resolveEmbedder(embedderSectionFor(ec.embedder, ec.model), { extraFactories: mergedEmbedderFactories }),
626
687
  // Real pg `Pool` provider for a `postgres` catalog (qdrant
627
688
  // deployment). Lazily imports `pg` and ensures the catalog table
628
689
  // exists on first use; pass the configured table so the DDL targets
@@ -654,12 +715,12 @@ export class SmartServer {
654
715
  let historyRag;
655
716
  const ragCollections = [];
656
717
  if (this.cfg.rag) {
657
- const ragOptions = {
658
- injectedEmbedder: resolvedEmbedder,
659
- extraFactories: mergedEmbedderFactories,
660
- };
661
- toolsRag = await makeRag(this.cfg.rag, ragOptions);
662
- historyRag = await makeRag({ ...this.cfg.rag }, ragOptions);
718
+ // The embedder was resolved through the seam above; the store is built
719
+ // through its own. Two calls, two stores — the history store never shared
720
+ // the tools store's instance.
721
+ const input = toMakeRagInput(this.cfg.rag.store, resolvedEmbedder, 'rag');
722
+ toolsRag = await this._deps.makeRag(input);
723
+ historyRag = await this._deps.makeRag(input);
663
724
  }
664
725
  // Capture the tools store for the flat/smart pipeline's ToolSelectHandler
665
726
  // (and white-box vectorization assertions). See field doc.
@@ -810,6 +871,15 @@ export class SmartServer {
810
871
  const agentHandle = await builder.build();
811
872
  const { agent: smartAgent, chat, streamChat, close: closeAgent, circuitBreakers, ragStores, modelProvider, } = agentHandle;
812
873
  const { ragRegistry: globalRagRegistry, mcpClients: globalMcpClients } = agentHandle;
874
+ this._globalRagRegistry = globalRagRegistry;
875
+ // Two limits of this server, stated rather than fixed (§6.4): it registers
876
+ // no RAG providers, so no session registry has a catalog to hydrate from —
877
+ // each holds only the deployment's globals; and its sessions carry a
878
+ // sessionId and no userId (llm-agent-libs session-registry.ts builds them
879
+ // from { sessionId }), so user collections are neither hydrated nor
880
+ // creatable through it. Not logged: in today's default deployment it would
881
+ // fire at every start, and an expected warning trains readers to skip the
882
+ // real ones — the limits are stated here, in the CHANGELOG and in the docs.
813
883
  // ---- Authoritative namespaced snapshot — yaml-builder path (#244) --------
814
884
  // The startup builder computes `namespacedTools`/`toolProvenance` (+ the
815
885
  // descriptors backing them) ONLY when it owns the connection itself (the
@@ -903,7 +973,13 @@ export class SmartServer {
903
973
  // `this._toolsRag` === the `toolsRag` local captured in start(); reference
904
974
  // the field as the single source of truth for the tools store.
905
975
  toolsRag: this._toolsRag,
906
- ragRegistry: globalRagRegistry,
976
+ // A registry per session, seeded with the deployment's globals and
977
+ // hydrated for its identity (§6.4). Never the shared one: collections are
978
+ // addressed by name, so sharing it would put every session's collections
979
+ // in every session's address space. Hydration finds nothing until a
980
+ // provider is registered, and no user collection while sessions carry no
981
+ // userId — see the limits stated above.
982
+ ragRegistryFactory: (identity) => this._sessionRagRegistry(identity),
907
983
  buildAgent: (parts) => this.buildSessionAgent(parts),
908
984
  logger: fileLogger,
909
985
  // Per-session pipeline teardown: run the IPipelineInstance.close captured
@@ -1129,18 +1205,16 @@ export class SmartServer {
1129
1205
  * always undefined here.
1130
1206
  */
1131
1207
  async buildSubAgent(name, subCfg, parentLogger, embedderFactories, injected) {
1132
- // Normalize subagent llm: either flat { provider, apiKey, ... } or a map
1133
- // { main: {...}, planner: {...} }. normalizeLlmConfig wraps flat shape as
1134
- // { main: flat } so downstream code always reads from .main.
1135
- const subLlmMap = normalizeLlmConfig(subCfg.llm);
1136
- const subLlmMain = subLlmMap?.main;
1137
- if (!subLlmMain?.apiKey &&
1138
- subLlmMain?.provider !== 'sap-ai-sdk' &&
1139
- subLlmMain?.provider !== 'ollama') {
1140
- throw new Error(`subagent '${name}': LLM API key is required`);
1141
- }
1142
- // The subagent's helper role derives from its own top-level `llm:` map.
1143
- const subHelperCfg = resolveLlmConfigStrict(subLlmMap, 'helper');
1208
+ // A worker's three LLM slots come from the SAME resolver as the pipeline's
1209
+ // (§4.6.7): a named key resolves strictly, an omitted role as that name
1210
+ // does for the pipeline. Instances are held by the resolver, so nothing is
1211
+ // built per session and a PUT /v1/config swap reaches workers too.
1212
+ const keys = parseWorkerLlm(name, subCfg.llm);
1213
+ const [mainLlm, classifierLlm, helperLlm] = await Promise.all([
1214
+ this.resolveWorkerRoleLlm(keys.main, 'main'),
1215
+ this.resolveWorkerRoleLlm(keys.classifier, 'classifier'),
1216
+ this.resolveWorkerRoleLlm(keys.helper, 'helper'),
1217
+ ]);
1144
1218
  // LLM/embedder clients: when the per-session re-wire injected them, use
1145
1219
  // those cached instances by reference (NEVER reconstruct). Otherwise (the
1146
1220
  // primary build()), build-once via the cache so the global agent build
@@ -1148,58 +1222,24 @@ export class SmartServer {
1148
1222
  // instances.
1149
1223
  // Note: a per-worker embedder slot is carried in WorkerLlmSet and the
1150
1224
  // injected record for forward-compat with Task A8/A10 per-session wiring.
1151
- // Today's buildSubAgent does not separately resolve an embedder here —
1152
- // embedders are carried by the worker's own store via makeRag's
1153
- // `injectedEmbedder` — so we ignore the embedder field below.
1154
- // Resolve (build-once or load from cache) the worker's own LLMs +
1225
+ // The worker's embedder is resolved through `BuildAgentDeps.resolveEmbedder`
1226
+ // inside the factories below (`_workerRagInput`), not separately here.
1227
+ // Resolve (build-once or load from cache) the worker's own
1155
1228
  // toolsRag/historyRag/mcpClients. The cache is keyed by worker name; the
1156
1229
  // primary build() populates it (no `injected` arg), and per-session
1157
1230
  // re-wires (`injected` set) read from it via the same call below — the
1158
1231
  // cache hit short-circuits all factories. This keeps the worker's
1159
1232
  // declared RAG/MCP intact across per-session re-wires (review HIGH #1).
1160
- const subFlatLlm = subLlmMain;
1161
- const mainTemp = Number(subFlatLlm?.temperature ?? 0.7);
1162
- const classifierTemp = Number(subFlatLlm?.classifierTemperature ?? 0.1);
1163
1233
  const cached = await resolveWorkerLlmSet({
1164
1234
  name,
1165
1235
  cache: this._workers.cache,
1166
- // Preserve the existing makeLlm derivation exactly.
1167
- makeMain: () => makeLlm({
1168
- // ?? 'deepseek' is a TS type-narrowing net only; the config
1169
- // validator rejects a missing flat-schema provider before
1170
- // this runs.
1171
- provider: subFlatLlm?.provider ?? 'deepseek',
1172
- apiKey: subFlatLlm?.apiKey ?? '',
1173
- baseURL: subFlatLlm?.url,
1174
- model: subFlatLlm?.model,
1175
- }, mainTemp),
1176
- makeClassifier: () => makeLlm({
1177
- provider: subFlatLlm?.provider ?? 'deepseek',
1178
- apiKey: subFlatLlm?.apiKey ?? '',
1179
- baseURL: subFlatLlm?.url,
1180
- model: subFlatLlm?.model,
1181
- }, classifierTemp),
1182
- makeHelper: subHelperCfg
1183
- ? ((h) => () => makeLlm({
1184
- provider: h.provider ?? 'deepseek',
1185
- apiKey: h.apiKey,
1186
- baseURL: h.url,
1187
- model: h.model,
1188
- }, Number(h.temperature ?? 0.1)))(subHelperCfg)
1189
- : undefined,
1190
1236
  // Worker-OWN tools RAG (from subCfg.rag, if declared). Built once;
1191
1237
  // re-wired per-session by reference — never re-vectorized.
1192
1238
  makeToolsRag: subCfg.rag
1193
- ? () => makeRag(subCfg.rag, {
1194
- injectedEmbedder: subCfg.embedder,
1195
- extraFactories: embedderFactories,
1196
- })
1239
+ ? async () => this._deps.makeRag(await this._workerRagInput(name, subCfg.rag, subCfg.embedder, embedderFactories))
1197
1240
  : undefined,
1198
1241
  makeHistoryRag: subCfg.rag
1199
- ? () => makeRag({ ...subCfg.rag }, {
1200
- injectedEmbedder: subCfg.embedder,
1201
- extraFactories: embedderFactories,
1202
- })
1242
+ ? async () => this._deps.makeRag(await this._workerRagInput(name, subCfg.rag, subCfg.embedder, embedderFactories))
1203
1243
  : undefined,
1204
1244
  // Worker-OWN MCP clients. DI list (subCfg.mcpClients) wins; otherwise
1205
1245
  // SmartAgentBuilder's own MCP-connect path handles `subCfg.mcp` — we
@@ -1209,9 +1249,6 @@ export class SmartServer {
1209
1249
  ? async () => subCfg.mcpClients
1210
1250
  : undefined,
1211
1251
  });
1212
- const mainLlm = cached.mainLlm;
1213
- const classifierLlm = cached.classifierLlm;
1214
- const helperLlm = cached.helperLlm;
1215
1252
  let subBuilder = new SmartAgentBuilder({
1216
1253
  mcp: subCfg.mcp,
1217
1254
  agent: subCfg.agent,
@@ -1222,9 +1259,7 @@ export class SmartServer {
1222
1259
  .withClassifierLlm(classifierLlm)
1223
1260
  .withLogger(parentLogger)
1224
1261
  .withMode(subCfg.mode ?? 'smart');
1225
- if (helperLlm) {
1226
- subBuilder = subBuilder.withHelperLlm(helperLlm);
1227
- }
1262
+ subBuilder = subBuilder.withHelperLlm(helperLlm);
1228
1263
  // SHARE the parent RAG registry + session logger when injected (per-session
1229
1264
  // worker re-wire). The per-call scope filter isolates by ctx.sessionId.
1230
1265
  const sharedReg = resolveSubAgentRagRegistry({
@@ -1280,24 +1315,31 @@ export class SmartServer {
1280
1315
  }
1281
1316
  // -- Pipeline-context dep sources (promoted from the inline coordinator-gate
1282
1317
  // closures; consumed by buildServerCtx, which later tasks call) ----------
1283
- /** Build an LLM from a SmartServerLlmConfig (mirrors stepperMakeLlm/DAG).
1284
- * Routes through the BuildAgentDeps seam so an injected `makeLlm` overrides
1285
- * the real builder. */
1286
- _makeLlm(lc) {
1287
- return this._deps.makeLlm(lc);
1288
- }
1289
- /** The real `makeLlm`-backed construction (the seam's default). */
1290
- _makeLlmDefault(lc) {
1291
- return makeDefaultRoleLlm(lc, this._mainTemp);
1318
+ /** A worker's own store input: its embedder through the seam, then paired. */
1319
+ async _workerRagInput(name, rag, diEmbedder, extraFactories) {
1320
+ const embedder = await resolveAgentEmbedder(rag, diEmbedder, this._deps.resolveEmbedder, extraFactories, this._fileLogger);
1321
+ return toMakeRagInput(rag.store, embedder, `subagent '${name}' rag`);
1292
1322
  }
1293
- /** Resolve a per-role LLM through the normalized map → pipelineFallback chain.
1294
- * 'main' returns the captured mainLlm; 'helper'/'classifier' return the
1295
- * prebuilt instances when present; otherwise the map/fallback config is built. */
1323
+ /** `ctx.resolveLlm(role)` — the role's default, through the held role map. */
1296
1324
  async resolveRoleLlm(role) {
1325
+ return this.roleLlm().resolve(role);
1326
+ }
1327
+ /** `ctx.resolveNamedLlm(key)` — strict: an `llm:` entry of exactly that name. */
1328
+ async resolveNamedRoleLlm(key) {
1329
+ return this.roleLlm().resolveNamed(key);
1330
+ }
1331
+ /** A worker role's LLM: its named key strictly, else the role's own name —
1332
+ * the same resolver the pipeline reads, so instances are shared (§4.6.7). */
1333
+ async resolveWorkerRoleLlm(key, role) {
1334
+ return key !== undefined
1335
+ ? this.roleLlm().resolveNamed(key)
1336
+ : this.roleLlm().resolve(role);
1337
+ }
1338
+ roleLlm() {
1297
1339
  if (!this._roleLlm) {
1298
- throw new Error('resolveRoleLlm invoked before _buildInfra built the resolver');
1340
+ throw new Error('role LLM lookup invoked before _buildInfra built the resolver');
1299
1341
  }
1300
- return this._roleLlm.resolve(role);
1342
+ return this._roleLlm;
1301
1343
  }
1302
1344
  /**
1303
1345
  * Session-scoped knowledge RAG over the shared knowledge backend (built
@@ -1544,22 +1586,13 @@ export class SmartServer {
1544
1586
  : undefined);
1545
1587
  }
1546
1588
  /**
1547
- * Build the per-session pipeline instance from the registry. Selects the
1548
- * plugin by `cfg.pipeline.name` (default 'flat'), parses its config dialect,
1549
- * and builds it against a session-scoped pipeline context. The returned
1550
- * `IPipelineInstance` carries `{ agent, close }` — the session consumes
1551
- * `agent`; `buildSessionAgent` registers `close` into the session-dispose path.
1589
+ * Build the per-session pipeline instance from the plugin selected at startup,
1590
+ * against a session-scoped pipeline context. The returned `IPipelineInstance`
1591
+ * carries `{ agent, close }`; `buildSessionAgent` registers `close` into the
1592
+ * session-dispose path.
1552
1593
  */
1553
1594
  async buildPipelineInstance(scope) {
1554
- const name = this.cfg.pipeline?.name ?? 'flat';
1555
- const plugin = this._pipelineRegistry.get(name);
1556
- if (!plugin) {
1557
- throw new Error(`unknown pipeline '${name}'; available: ${[
1558
- ...this._pipelineRegistry.keys(),
1559
- ].join(', ')}`);
1560
- }
1561
- const cfg = plugin.parseConfig(this.cfg.pipeline?.config ?? {});
1562
- return plugin.build(cfg, await this.buildServerCtx(scope));
1595
+ return this._pipelinePlugin.build(await this.buildServerCtx(scope));
1563
1596
  }
1564
1597
  warn(msg) {
1565
1598
  (this.cfg.log ?? this.noop)({ event: 'config_warning', message: msg });
@@ -1636,6 +1669,7 @@ export class SmartServer {
1636
1669
  : undefined;
1637
1670
  return createServerPipelineContext({
1638
1671
  resolveLlm: (role) => this.resolveRoleLlm(role),
1672
+ resolveNamedLlm: (key) => this.resolveNamedRoleLlm(key),
1639
1673
  knowledgeRagFor: (sid) => this.knowledgeRagFor(sid),
1640
1674
  // Durable backend + resolved embedder shared with every pipeline; the
1641
1675
  // controller pipeline consumes both (session-bundle persistence +
@@ -1699,9 +1733,6 @@ export class SmartServer {
1699
1733
  logger: this._fileLogger,
1700
1734
  logLlmCall: (e) => requestLogger?.logLlmCall?.(e),
1701
1735
  createAgentBuilder: () => this.buildBaseBuilder(this.partsToBaseInput(scope.parts, workerRegistry, extras)),
1702
- makeLlm: (c) => this._makeLlm(c),
1703
- llmMap: this._llmMap,
1704
- pipelineFallback: this._pipelineFallback,
1705
1736
  mainLlm: this._mainLlm,
1706
1737
  helperLlm: this._helperLlm,
1707
1738
  mainTemp: this._mainTemp ?? 0.7,
@@ -1756,6 +1787,10 @@ export class SmartServer {
1756
1787
  if (parts.ragRegistry) {
1757
1788
  builder = builder.setRagRegistry(parts.ragRegistry);
1758
1789
  }
1790
+ // The server's one provider registry: without it build() substitutes an
1791
+ // empty one and sets it on the session's registry, and an adopted
1792
+ // collection's delete then reaches no provider.
1793
+ builder = builder.setRagProviderRegistry(this._ragProviderRegistry);
1759
1794
  if (parts.requestLogger) {
1760
1795
  builder = builder.withRequestLogger(parts.requestLogger);
1761
1796
  }
@@ -1836,6 +1871,19 @@ export class SmartServer {
1836
1871
  (() => new WindowContextStrategy()));
1837
1872
  return builder;
1838
1873
  }
1874
+ /** The registry one session owns; see buildSessionRagRegistry. */
1875
+ _sessionRagRegistry(identity) {
1876
+ if (!this._globalRagRegistry) {
1877
+ throw new Error('A session RAG registry was requested before the server infra was built');
1878
+ }
1879
+ return buildSessionRagRegistry({
1880
+ identity,
1881
+ globals: this._globalRagRegistry,
1882
+ providers: this._ragProviderRegistry,
1883
+ logger: this._fileLogger,
1884
+ reported: this._reportedCatalogFindings,
1885
+ });
1886
+ }
1839
1887
  /**
1840
1888
  * Builds the per-session SmartAgent by routing through the selected pipeline
1841
1889
  * plugin (`buildPipelineInstance`). The pipeline owns coordinator wiring; the
@@ -2081,6 +2129,7 @@ export class SmartServer {
2081
2129
  _configUpdateTarget() {
2082
2130
  return {
2083
2131
  modelResolver: this.cfg.modelResolver,
2132
+ skipModelValidation: this.cfg.skipModelValidation === true,
2084
2133
  setMainLlm: (llm) => {
2085
2134
  this._mainLlm = llm;
2086
2135
  },