@mcp-abap-adt/llm-agent-libs 26.0.0 → 27.0.1
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.
- package/README.md +11 -14
- package/dist/agent.d.ts +2 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +6 -5
- package/dist/agent.js.map +1 -1
- package/dist/builder.d.ts +36 -14
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +472 -348
- package/dist/builder.js.map +1 -1
- package/dist/config/config-watcher.d.ts.map +1 -1
- package/dist/config/config-watcher.js +9 -5
- package/dist/config/config-watcher.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/vectorize-mcp-tools.d.ts.map +1 -1
- package/dist/mcp/vectorize-mcp-tools.js +21 -2
- package/dist/mcp/vectorize-mcp-tools.js.map +1 -1
- package/dist/pipeline/handlers/history-upsert.d.ts.map +1 -1
- package/dist/pipeline/handlers/history-upsert.js +8 -1
- package/dist/pipeline/handlers/history-upsert.js.map +1 -1
- package/dist/pipeline/handlers/rag-query.d.ts +1 -1
- package/dist/pipeline/handlers/rag-query.d.ts.map +1 -1
- package/dist/pipeline/handlers/rag-query.js +10 -2
- package/dist/pipeline/handlers/rag-query.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -1
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/types.d.ts +7 -0
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/types.js +85 -14
- package/dist/plugins/types.js.map +1 -1
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +8 -5
- package/dist/providers.js.map +1 -1
- package/dist/session/session-graph-factory.d.ts +49 -16
- package/dist/session/session-graph-factory.d.ts.map +1 -1
- package/dist/session/session-graph-factory.js +140 -62
- package/dist/session/session-graph-factory.js.map +1 -1
- package/dist/skills/plugin-host/qdrant-store.d.ts +6 -1
- package/dist/skills/plugin-host/qdrant-store.d.ts.map +1 -1
- package/dist/skills/plugin-host/qdrant-store.js +7 -7
- package/dist/skills/plugin-host/qdrant-store.js.map +1 -1
- package/dist/util/stop-all.d.ts +19 -0
- package/dist/util/stop-all.d.ts.map +1 -0
- package/dist/util/stop-all.js +30 -0
- package/dist/util/stop-all.js.map +1 -0
- package/package.json +5 -35
- package/dist/coordinator/dag/reviewer-error-strategy.d.ts +0 -15
- package/dist/coordinator/dag/reviewer-error-strategy.d.ts.map +0 -1
- package/dist/coordinator/dag/reviewer-error-strategy.js +0 -38
- package/dist/coordinator/dag/reviewer-error-strategy.js.map +0 -1
- package/dist/skills/plugin-host/chunker.test.d.ts +0 -2
- package/dist/skills/plugin-host/chunker.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/chunker.test.js +0 -31
- package/dist/skills/plugin-host/chunker.test.js.map +0 -1
- package/dist/skills/plugin-host/compatible-skills-rag.test.d.ts +0 -2
- package/dist/skills/plugin-host/compatible-skills-rag.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/compatible-skills-rag.test.js +0 -174
- package/dist/skills/plugin-host/compatible-skills-rag.test.js.map +0 -1
- package/dist/skills/plugin-host/http-marketplace-source.test.d.ts +0 -2
- package/dist/skills/plugin-host/http-marketplace-source.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/http-marketplace-source.test.js +0 -71
- package/dist/skills/plugin-host/http-marketplace-source.test.js.map +0 -1
- package/dist/skills/plugin-host/in-memory-store.test.d.ts +0 -2
- package/dist/skills/plugin-host/in-memory-store.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/in-memory-store.test.js +0 -100
- package/dist/skills/plugin-host/in-memory-store.test.js.map +0 -1
- package/dist/skills/plugin-host/marketplace-adapter.test.d.ts +0 -2
- package/dist/skills/plugin-host/marketplace-adapter.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/marketplace-adapter.test.js +0 -46
- package/dist/skills/plugin-host/marketplace-adapter.test.js.map +0 -1
- package/dist/skills/plugin-host/qdrant-store.test.d.ts +0 -2
- package/dist/skills/plugin-host/qdrant-store.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/qdrant-store.test.js +0 -396
- package/dist/skills/plugin-host/qdrant-store.test.js.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.d.ts +0 -2
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.js +0 -520
- package/dist/skills/plugin-host/skill-plugin-host.ingest.test.js.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.d.ts +0 -2
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.js +0 -328
- package/dist/skills/plugin-host/skill-plugin-host.recall-and-reload.test.js.map +0 -1
- package/dist/skills/plugin-host/skills-rag-source.test.d.ts +0 -2
- package/dist/skills/plugin-host/skills-rag-source.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/skills-rag-source.test.js +0 -69
- package/dist/skills/plugin-host/skills-rag-source.test.js.map +0 -1
- package/dist/skills/plugin-host/source-strategies.test.d.ts +0 -2
- package/dist/skills/plugin-host/source-strategies.test.d.ts.map +0 -1
- package/dist/skills/plugin-host/source-strategies.test.js +0 -52
- package/dist/skills/plugin-host/source-strategies.test.js.map +0 -1
package/dist/builder.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* .setToolsRag(myRag)
|
|
13
13
|
* .build();
|
|
14
14
|
*/
|
|
15
|
-
import { buildNamespacedTools, CircuitBreaker, CircuitBreakerLlm, defaultToolNamespace, FallbackRag, InMemoryRag, QueryEmbedding, SimpleRagProviderRegistry, SimpleRagRegistry, } from '@mcp-abap-adt/llm-agent';
|
|
15
|
+
import { buildNamespacedTools, CircuitBreaker, CircuitBreakerLlm, collectServerDescriptors, defaultToolNamespace, FallbackRag, InMemoryRag, normaliseLogger, QueryEmbedding, ragStoreKey, SimpleRagProviderRegistry, SimpleRagRegistry, } from '@mcp-abap-adt/llm-agent';
|
|
16
16
|
import { makeConnectionStrategy } from '@mcp-abap-adt/llm-agent-mcp';
|
|
17
17
|
import { wrapEmbedder } from './adapters/usage-logging-embedder.js';
|
|
18
18
|
import { SmartAgent } from './agent.js';
|
|
@@ -30,6 +30,7 @@ import { RateLimiterLlm } from './resilience/rate-limiter-llm.js';
|
|
|
30
30
|
import { RetryLlm } from './resilience/retry-llm.js';
|
|
31
31
|
import { DefaultSubAgentContextBuilder, } from './subagent/default-context-builder.js';
|
|
32
32
|
import { SmartAgentSubAgent } from './subagent/smart-agent-subagent.js';
|
|
33
|
+
import { stopAll } from './util/stop-all.js';
|
|
33
34
|
// ---------------------------------------------------------------------------
|
|
34
35
|
// Helpers
|
|
35
36
|
// ---------------------------------------------------------------------------
|
|
@@ -59,6 +60,7 @@ export class SmartAgentBuilder {
|
|
|
59
60
|
_historyRag;
|
|
60
61
|
_pipeline;
|
|
61
62
|
_mcpClients;
|
|
63
|
+
_mcpServers;
|
|
62
64
|
_classifier;
|
|
63
65
|
_assembler;
|
|
64
66
|
_logger;
|
|
@@ -179,6 +181,30 @@ export class SmartAgentBuilder {
|
|
|
179
181
|
this._mcpClients = clients;
|
|
180
182
|
return this;
|
|
181
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Servers this builder owns: `build()` starts each one and `handle.close()`
|
|
186
|
+
* stops it. Use this when the caller knows HOW a server is started — a stdio
|
|
187
|
+
* child with its own environment, an HTTP target with a credential — rather
|
|
188
|
+
* than handing over an already-connected client.
|
|
189
|
+
*
|
|
190
|
+
* Beside `withMcpClients`, never replacing it: a consumer that already holds
|
|
191
|
+
* clients keeps passing clients. Setting BOTH is a configuration error and
|
|
192
|
+
* `build()` throws, rather than one silently winning over the other.
|
|
193
|
+
*
|
|
194
|
+
* Every server must carry a `descriptor`, or none may — a partly-filled set
|
|
195
|
+
* throws, because dropping it would re-namespace tools from their `label`
|
|
196
|
+
* to `s${slotIndex}` with nothing reporting it.
|
|
197
|
+
*
|
|
198
|
+
* Those descriptors DO reach the pipeline (`PipelineDeps.mcpClientDescriptors`,
|
|
199
|
+
* so namespacing works), but do NOT reach `SmartAgentHandle` — unlike the
|
|
200
|
+
* YAML `mcp:` branch, `handle.mcpClientDescriptors`/`configuredSlotCount`
|
|
201
|
+
* stay unset here. A caller forced to supply descriptors all-or-none
|
|
202
|
+
* currently gets nothing back on the handle for it.
|
|
203
|
+
*/
|
|
204
|
+
withMcpServers(servers) {
|
|
205
|
+
this._mcpServers = servers;
|
|
206
|
+
return this;
|
|
207
|
+
}
|
|
182
208
|
/** Override the intent classifier. */
|
|
183
209
|
withClassifier(classifier) {
|
|
184
210
|
this._classifier = classifier;
|
|
@@ -189,9 +215,18 @@ export class SmartAgentBuilder {
|
|
|
189
215
|
this._assembler = assembler;
|
|
190
216
|
return this;
|
|
191
217
|
}
|
|
192
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* Set a logger for internal pipeline events.
|
|
220
|
+
*
|
|
221
|
+
* Takes either shape: the event `ILogger`, or an ordinary `ITextLogger`
|
|
222
|
+
* (`info`/`warn`/`error`/`debug`). A text logger is normalised here, at the
|
|
223
|
+
* boundary, so everything downstream — `PipelineDeps.logger`, the agent, the
|
|
224
|
+
* connection strategy — keeps receiving the event logger it already expects,
|
|
225
|
+
* and `IPipelineContext.logger` still hands plugins the type they compile
|
|
226
|
+
* against.
|
|
227
|
+
*/
|
|
193
228
|
withLogger(logger) {
|
|
194
|
-
this._logger = logger;
|
|
229
|
+
this._logger = normaliseLogger(logger);
|
|
195
230
|
return this;
|
|
196
231
|
}
|
|
197
232
|
/** Set a tool execution policy (allow/deny list). */
|
|
@@ -583,9 +618,13 @@ export class SmartAgentBuilder {
|
|
|
583
618
|
}
|
|
584
619
|
}
|
|
585
620
|
} // end skipModelValidation guard
|
|
586
|
-
// Auto-create tools RAG if MCP clients will be configured and embedder available
|
|
621
|
+
// Auto-create tools RAG if MCP clients will be configured and embedder available.
|
|
622
|
+
// `this._mcpServers` (withMcpServers) must agree with `this._mcpClients`
|
|
623
|
+
// (withMcpClients) here — a consumer migrating from one seam to the other
|
|
624
|
+
// must not silently lose the auto-created tools store (and, with it,
|
|
625
|
+
// DefaultPipeline's rag-tools stage).
|
|
587
626
|
const toolsRag = this._toolsRag ??
|
|
588
|
-
((this.cfg.mcp || this._mcpClients) && this._embedder
|
|
627
|
+
((this.cfg.mcp || this._mcpClients || this._mcpServers) && this._embedder
|
|
589
628
|
? new InMemoryRag()
|
|
590
629
|
: undefined);
|
|
591
630
|
// Auto-create history RAG if history summarization is enabled
|
|
@@ -615,19 +654,19 @@ export class SmartAgentBuilder {
|
|
|
615
654
|
// leave it unset so a genuine name collision still surfaces instead of
|
|
616
655
|
// silently keeping the old RAG/editor/meta.
|
|
617
656
|
for (const c of this._staticCollections) {
|
|
618
|
-
if (c.idempotent && ragRegistry.get(c.name))
|
|
657
|
+
if (c.idempotent && ragRegistry.get(c.name, c.meta?.scope ?? 'global'))
|
|
619
658
|
continue;
|
|
620
659
|
ragRegistry.register(c.name, c.rag, c.editor, c.meta);
|
|
621
660
|
}
|
|
622
661
|
// Mirror the built-in toolsRag / historyRag into the registry so the projection preserves
|
|
623
662
|
// ragStores.tools / ragStores.history behavior that existing handlers rely on.
|
|
624
|
-
if (toolsRag && !ragRegistry.get('tools')) {
|
|
663
|
+
if (toolsRag && !ragRegistry.get('tools', 'global')) {
|
|
625
664
|
ragRegistry.register('tools', toolsRag, undefined, {
|
|
626
665
|
displayName: 'tools',
|
|
627
666
|
scope: 'global',
|
|
628
667
|
});
|
|
629
668
|
}
|
|
630
|
-
if (historyRag && !ragRegistry.get('history')) {
|
|
669
|
+
if (historyRag && !ragRegistry.get('history', 'global')) {
|
|
631
670
|
ragRegistry.register('history', historyRag, undefined, {
|
|
632
671
|
displayName: 'history',
|
|
633
672
|
scope: 'global',
|
|
@@ -640,17 +679,19 @@ export class SmartAgentBuilder {
|
|
|
640
679
|
throw new Error(`Failed to create collection '${c.collectionName}': ${res.error.message}`);
|
|
641
680
|
}
|
|
642
681
|
}
|
|
643
|
-
// Derive ragStores as a live projection of the registry
|
|
644
|
-
// registry's mutation listener
|
|
645
|
-
//
|
|
682
|
+
// Derive ragStores as a live projection of the registry, kept in sync by the
|
|
683
|
+
// registry's mutation listener. A global keeps its bare name — every stage
|
|
684
|
+
// configuration naming one keeps working — and a user or session collection
|
|
685
|
+
// is keyed user/<name> or session/<name>, so one name in several scopes gives
|
|
686
|
+
// several keys and nothing overwrites anything (§6.4).
|
|
646
687
|
const ragStores = {};
|
|
647
688
|
const rebuildProjection = () => {
|
|
648
689
|
for (const k of Object.keys(ragStores))
|
|
649
690
|
delete ragStores[k];
|
|
650
691
|
for (const m of ragRegistry.list()) {
|
|
651
|
-
const r = ragRegistry.get(m.name);
|
|
692
|
+
const r = ragRegistry.get(m.name, m.scope ?? 'global');
|
|
652
693
|
if (r)
|
|
653
|
-
ragStores[m
|
|
694
|
+
ragStores[ragStoreKey(m)] = r;
|
|
654
695
|
}
|
|
655
696
|
};
|
|
656
697
|
rebuildProjection();
|
|
@@ -683,17 +724,33 @@ export class SmartAgentBuilder {
|
|
|
683
724
|
onStateChange: cbCfg.onStateChange ?? makeOnStateChange('embedder'),
|
|
684
725
|
});
|
|
685
726
|
circuitBreakers.push(embedderBreaker);
|
|
686
|
-
|
|
727
|
+
// list() is a snapshot, so changing entries while iterating is safe.
|
|
728
|
+
for (const meta of ragRegistry.list()) {
|
|
729
|
+
const scope = meta.scope ?? 'global';
|
|
730
|
+
const store = ragRegistry.get(meta.name, scope);
|
|
731
|
+
if (!store)
|
|
732
|
+
continue;
|
|
687
733
|
const wrapped = new FallbackRag(store, new InMemoryRag(), embedderBreaker);
|
|
688
|
-
//
|
|
689
|
-
//
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
ragRegistry
|
|
693
|
-
|
|
694
|
-
|
|
734
|
+
// Later lookups (and the mutation-listener rebuild) see the wrapped
|
|
735
|
+
// store. The entry keeps its scope, owner, editor, provider and store
|
|
736
|
+
// name: without the last two a hydrated collection's delete would reach
|
|
737
|
+
// no store and its catalog record would bring it back (§6.3).
|
|
738
|
+
if (ragRegistry instanceof SimpleRagRegistry) {
|
|
739
|
+
ragRegistry.replaceRag(meta.name, scope, wrapped);
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
// Another IRagRegistry: re-register with everything register carries.
|
|
743
|
+
const editor = ragRegistry.getEditor(meta.name, scope);
|
|
744
|
+
ragRegistry.unregister(meta.name, scope);
|
|
745
|
+
ragRegistry.register(meta.name, wrapped, editor, {
|
|
746
|
+
displayName: meta.displayName,
|
|
747
|
+
description: meta.description,
|
|
748
|
+
scope,
|
|
749
|
+
sessionId: meta.sessionId,
|
|
750
|
+
userId: meta.userId,
|
|
751
|
+
providerName: meta.providerName,
|
|
752
|
+
tags: meta.tags,
|
|
695
753
|
});
|
|
696
|
-
// Projection gets rebuilt by the mutation listener; no direct write to ragStores needed.
|
|
697
754
|
}
|
|
698
755
|
}
|
|
699
756
|
// ---- Request logger ---------------------------------------------------
|
|
@@ -716,352 +773,419 @@ export class SmartAgentBuilder {
|
|
|
716
773
|
// `vectorizeMcpTools` below consumes this SAME view rather than relisting.
|
|
717
774
|
let namespacedTools;
|
|
718
775
|
let toolProvenance;
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const mcpConfigs = prepareMcpConfigs(this.cfg.mcp, this._mcpRequestHeadersStrategy);
|
|
732
|
-
if (mcpConfigs.length > 0 && !connectionStrategy) {
|
|
733
|
-
connectionStrategy = makeConnectionStrategy(mcpConfigs, log ? { logger: log } : undefined);
|
|
734
|
-
}
|
|
735
|
-
resolved = connectionStrategy
|
|
736
|
-
? await connectionStrategy.resolve([])
|
|
737
|
-
: { clients: [], toolsChanged: false };
|
|
738
|
-
mcpClients = resolved.clients;
|
|
739
|
-
// Same snapshot as `mcpClients` above — paired so the pipeline context's
|
|
740
|
-
// `mcpClientDescriptors` never drifts relative to `mcpClients` across a
|
|
741
|
-
// reconnect (both refresh paths zip them by index at point-of-use).
|
|
742
|
-
mcpClientDescriptors = resolved.clientDescriptors;
|
|
743
|
-
// ---- Single listTools() pass, settled per client -------------------
|
|
744
|
-
// Preserve the ORIGINAL client index on a partial failure: build the
|
|
745
|
-
// per-client input index-preservingly (flatMap over the settled array
|
|
746
|
-
// aligned by position), NOT via filter().map() — a middle-client
|
|
747
|
-
// failure must not shift later slots' slotIndex.
|
|
748
|
-
const descs = resolved.clientDescriptors ??
|
|
749
|
-
mcpClients.map((_, i) => ({ slotIndex: i }));
|
|
750
|
-
const settled = await Promise.all(mcpClients.map(async (client) => {
|
|
776
|
+
try {
|
|
777
|
+
if (this._mcpServers) {
|
|
778
|
+
if (this._mcpClients)
|
|
779
|
+
throw new Error('withMcpServers and withMcpClients are mutually exclusive: pass clients you already hold, or servers this builder should start');
|
|
780
|
+
// A partly-filled descriptor set is a caller bug: dropping it would
|
|
781
|
+
// re-namespace every tool from its label to `s${slotIndex}`. Check
|
|
782
|
+
// before starting anything.
|
|
783
|
+
const descriptors = collectServerDescriptors(this._mcpServers, 'withMcpServers');
|
|
784
|
+
// Caller-provided servers: start each, keep its stop() for handle.close().
|
|
785
|
+
// Auto-connect and vectorization are skipped, exactly as on the
|
|
786
|
+
// `withMcpClients` branch.
|
|
787
|
+
const started = [];
|
|
751
788
|
try {
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
789
|
+
for (const server of this._mcpServers) {
|
|
790
|
+
started.push(await server.start());
|
|
791
|
+
closeFns.push(() => server.stop());
|
|
792
|
+
}
|
|
756
793
|
}
|
|
757
|
-
catch {
|
|
758
|
-
|
|
794
|
+
catch (err) {
|
|
795
|
+
// Stop what did start, so a failure half-way leaves no live child.
|
|
796
|
+
await stopAll(closeFns.splice(0));
|
|
797
|
+
throw err;
|
|
759
798
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
799
|
+
mcpClients = started;
|
|
800
|
+
mcpClientDescriptors = descriptors;
|
|
801
|
+
}
|
|
802
|
+
else if (this._mcpClients) {
|
|
803
|
+
// Caller-provided clients: skip auto-connect and vectorization
|
|
804
|
+
mcpClients = this._mcpClients;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// YAML `mcp:` → route connection through an IMcpConnectionStrategy
|
|
808
|
+
// (build-on-components). Default to a resilient `makeConnectionStrategy`
|
|
809
|
+
// (connect + periodic reconnect + readiness via IReadinessReporter) unless
|
|
810
|
+
// the consumer injected their own. The strategy OWNS lifecycle: its initial
|
|
811
|
+
// `resolve([])` connects the targets and the agent resolves through it each
|
|
812
|
+
// iteration; disposal is `connectionStrategy.dispose()` (no per-client
|
|
813
|
+
// closeFns). `BuilderMcpConfig` is structurally `McpConnectionConfig`.
|
|
814
|
+
const mcpConfigs = prepareMcpConfigs(this.cfg.mcp, this._mcpRequestHeadersStrategy);
|
|
815
|
+
if (mcpConfigs.length > 0 && !connectionStrategy) {
|
|
816
|
+
connectionStrategy = makeConnectionStrategy(mcpConfigs, log ? { logger: log } : undefined);
|
|
767
817
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
{
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
818
|
+
resolved = connectionStrategy
|
|
819
|
+
? await connectionStrategy.resolve([])
|
|
820
|
+
: { clients: [], toolsChanged: false };
|
|
821
|
+
mcpClients = resolved.clients;
|
|
822
|
+
// Same snapshot as `mcpClients` above — paired so the pipeline context's
|
|
823
|
+
// `mcpClientDescriptors` never drifts relative to `mcpClients` across a
|
|
824
|
+
// reconnect (both refresh paths zip them by index at point-of-use).
|
|
825
|
+
mcpClientDescriptors = resolved.clientDescriptors;
|
|
826
|
+
// ---- Single listTools() pass, settled per client -------------------
|
|
827
|
+
// Preserve the ORIGINAL client index on a partial failure: build the
|
|
828
|
+
// per-client input index-preservingly (flatMap over the settled array
|
|
829
|
+
// aligned by position), NOT via filter().map() — a middle-client
|
|
830
|
+
// failure must not shift later slots' slotIndex.
|
|
831
|
+
const descs = resolved.clientDescriptors ??
|
|
832
|
+
mcpClients.map((_, i) => ({ slotIndex: i }));
|
|
833
|
+
const settled = await Promise.all(mcpClients.map(async (client) => {
|
|
834
|
+
try {
|
|
835
|
+
const result = await client.listTools();
|
|
836
|
+
return result.ok
|
|
837
|
+
? { ok: true, value: result.value }
|
|
838
|
+
: { ok: false };
|
|
839
|
+
}
|
|
840
|
+
catch {
|
|
841
|
+
return { ok: false };
|
|
842
|
+
}
|
|
843
|
+
}));
|
|
844
|
+
let clientFailures = 0;
|
|
845
|
+
let total = 0;
|
|
846
|
+
const perClient = settled.flatMap((entry, i) => {
|
|
847
|
+
if (!entry.ok) {
|
|
848
|
+
clientFailures++;
|
|
849
|
+
return [];
|
|
850
|
+
}
|
|
851
|
+
total += entry.value.length;
|
|
852
|
+
return [
|
|
853
|
+
{
|
|
854
|
+
slotIndex: descs[i]?.slotIndex ?? i,
|
|
855
|
+
label: descs[i]?.label,
|
|
856
|
+
client: mcpClients[i],
|
|
857
|
+
tools: entry.value,
|
|
858
|
+
},
|
|
859
|
+
];
|
|
860
|
+
});
|
|
861
|
+
const built = buildNamespacedTools(perClient, this._toolNamespace ?? defaultToolNamespace);
|
|
862
|
+
namespacedTools = built.tools;
|
|
863
|
+
toolProvenance = built.provenance;
|
|
864
|
+
const toolSummary = await vectorizeMcpTools(mcpClients, toolsRag, requestLogger, log, this._toolRecordKey, undefined, {
|
|
865
|
+
descriptors: mcpClientDescriptors,
|
|
866
|
+
configuredSlotCount: resolved.configuredSlotCount,
|
|
867
|
+
toolNamespace: this._toolNamespace ?? defaultToolNamespace,
|
|
868
|
+
prebuiltView: {
|
|
869
|
+
tools: namespacedTools,
|
|
870
|
+
provenance: toolProvenance,
|
|
871
|
+
clientFailures,
|
|
872
|
+
total,
|
|
775
873
|
},
|
|
776
|
-
];
|
|
777
|
-
});
|
|
778
|
-
const built = buildNamespacedTools(perClient, this._toolNamespace ?? defaultToolNamespace);
|
|
779
|
-
namespacedTools = built.tools;
|
|
780
|
-
toolProvenance = built.provenance;
|
|
781
|
-
const toolSummary = await vectorizeMcpTools(mcpClients, toolsRag, requestLogger, log, this._toolRecordKey, undefined, {
|
|
782
|
-
descriptors: mcpClientDescriptors,
|
|
783
|
-
configuredSlotCount: resolved.configuredSlotCount,
|
|
784
|
-
toolNamespace: this._toolNamespace ?? defaultToolNamespace,
|
|
785
|
-
prebuiltView: {
|
|
786
|
-
tools: namespacedTools,
|
|
787
|
-
provenance: toolProvenance,
|
|
788
|
-
clientFailures,
|
|
789
|
-
total,
|
|
790
|
-
},
|
|
791
|
-
});
|
|
792
|
-
// Published only when defined: a skipped run must leave the holder empty
|
|
793
|
-
// rather than storing a zeroed summary. Computing the snapshot above is
|
|
794
|
-
// unconditional; publishing a catalog status is NOT — it still happens
|
|
795
|
-
// ONLY through this guard, exactly as before (no writable store → no
|
|
796
|
-
// publish, even though the snapshot now always exists).
|
|
797
|
-
if (toolSummary)
|
|
798
|
-
toolCatalogStatus.publish(toolSummary);
|
|
799
|
-
}
|
|
800
|
-
// ---- SmartAgent Config ------------------------------------------------
|
|
801
|
-
const agentCfg = {
|
|
802
|
-
maxIterations: 10,
|
|
803
|
-
maxToolCalls: 30,
|
|
804
|
-
ragQueryK: 10,
|
|
805
|
-
ragTranslatePrompt: this.cfg.prompts?.ragTranslate,
|
|
806
|
-
historySummaryPrompt: this.cfg.prompts?.historySummary,
|
|
807
|
-
historyAutoSummarizeLimit: this.cfg.agent?.historyAutoSummarizeLimit,
|
|
808
|
-
...this.cfg.agent,
|
|
809
|
-
...(this.cfg.sessionPolicy
|
|
810
|
-
? { sessionPolicy: this.cfg.sessionPolicy }
|
|
811
|
-
: {}),
|
|
812
|
-
// Fluent overrides take precedence over cfg.agent
|
|
813
|
-
...this._agentOverrides,
|
|
814
|
-
onBeforeStream: this._onBeforeStream,
|
|
815
|
-
};
|
|
816
|
-
// ---- Retry wrapping (outside circuit breaker) ----------------------------
|
|
817
|
-
// Enable retry by default with sensible defaults; explicit config overrides.
|
|
818
|
-
const retryOpts = agentCfg.retry ?? {
|
|
819
|
-
maxAttempts: 3,
|
|
820
|
-
backoffMs: 2000,
|
|
821
|
-
retryOn: [429, 500, 502, 503],
|
|
822
|
-
retryOnMidStream: [],
|
|
823
|
-
};
|
|
824
|
-
wrappedMainLlm = new RetryLlm(wrappedMainLlm, retryOpts);
|
|
825
|
-
// ---- Rate limiter wrapping (outermost — retry attempts also throttled) ----
|
|
826
|
-
if (this._rateLimiter) {
|
|
827
|
-
wrappedMainLlm = new RateLimiterLlm(wrappedMainLlm, this._rateLimiter);
|
|
828
|
-
}
|
|
829
|
-
// ---- Classifier -------------------------------------------------------
|
|
830
|
-
const classifierCfg = {};
|
|
831
|
-
if (this.cfg.prompts?.classifier)
|
|
832
|
-
classifierCfg.systemPrompt = this.cfg.prompts.classifier;
|
|
833
|
-
const classifier = this._classifier ??
|
|
834
|
-
new LlmClassifier(classifierLlm, classifierCfg, requestLogger);
|
|
835
|
-
// ---- Assembler --------------------------------------------------------
|
|
836
|
-
const assemblerCfg = {
|
|
837
|
-
maxTokens: agentCfg.contextBudgetTokens,
|
|
838
|
-
showReasoning: agentCfg.showReasoning,
|
|
839
|
-
reasoningInstruction: this.cfg.prompts?.reasoning,
|
|
840
|
-
historyRecencyWindow: agentCfg.historyRecencyWindow,
|
|
841
|
-
};
|
|
842
|
-
if (this.cfg.prompts?.system)
|
|
843
|
-
assemblerCfg.systemPromptPreamble = this.cfg.prompts.system;
|
|
844
|
-
const assembler = this._assembler ?? new ContextAssembler(assemblerCfg);
|
|
845
|
-
// ---- History memory & summarizer ----------------------------------------
|
|
846
|
-
let historyMemory;
|
|
847
|
-
let historySummarizer;
|
|
848
|
-
if (agentCfg.semanticHistoryEnabled) {
|
|
849
|
-
historyMemory =
|
|
850
|
-
this._historyMemory ??
|
|
851
|
-
new HistoryMemory({
|
|
852
|
-
maxSize: agentCfg.historyRecencyWindow ?? 3,
|
|
853
|
-
});
|
|
854
|
-
const summarizerLlm = this._helperLlm ?? mainLlm;
|
|
855
|
-
historySummarizer =
|
|
856
|
-
this._historySummarizer ??
|
|
857
|
-
new HistorySummarizer(summarizerLlm, agentCfg.historyTurnSummaryPrompt
|
|
858
|
-
? { prompt: agentCfg.historyTurnSummaryPrompt }
|
|
859
|
-
: undefined);
|
|
860
|
-
if (historyRag && !ragRegistry.get('history')) {
|
|
861
|
-
ragRegistry.register('history', historyRag, undefined, {
|
|
862
|
-
displayName: 'history',
|
|
863
|
-
scope: 'global',
|
|
864
874
|
});
|
|
865
|
-
//
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
loadedPlugins = plugins;
|
|
873
|
-
if (plugins.reranker && !this._reranker) {
|
|
874
|
-
this._reranker = plugins.reranker;
|
|
875
|
+
// Published only when defined: a skipped run must leave the holder empty
|
|
876
|
+
// rather than storing a zeroed summary. Computing the snapshot above is
|
|
877
|
+
// unconditional; publishing a catalog status is NOT — it still happens
|
|
878
|
+
// ONLY through this guard, exactly as before (no writable store → no
|
|
879
|
+
// publish, even though the snapshot now always exists).
|
|
880
|
+
if (toolSummary)
|
|
881
|
+
toolCatalogStatus.publish(toolSummary);
|
|
875
882
|
}
|
|
876
|
-
|
|
877
|
-
|
|
883
|
+
// ---- SmartAgent Config ------------------------------------------------
|
|
884
|
+
const agentCfg = {
|
|
885
|
+
maxIterations: 10,
|
|
886
|
+
maxToolCalls: 30,
|
|
887
|
+
ragQueryK: 10,
|
|
888
|
+
ragTranslatePrompt: this.cfg.prompts?.ragTranslate,
|
|
889
|
+
historySummaryPrompt: this.cfg.prompts?.historySummary,
|
|
890
|
+
historyAutoSummarizeLimit: this.cfg.agent?.historyAutoSummarizeLimit,
|
|
891
|
+
...this.cfg.agent,
|
|
892
|
+
...(this.cfg.sessionPolicy
|
|
893
|
+
? { sessionPolicy: this.cfg.sessionPolicy }
|
|
894
|
+
: {}),
|
|
895
|
+
// Fluent overrides take precedence over cfg.agent
|
|
896
|
+
...this._agentOverrides,
|
|
897
|
+
onBeforeStream: this._onBeforeStream,
|
|
898
|
+
};
|
|
899
|
+
// ---- Retry wrapping (outside circuit breaker) ----------------------------
|
|
900
|
+
// Enable retry by default with sensible defaults; explicit config overrides.
|
|
901
|
+
const retryOpts = agentCfg.retry ?? {
|
|
902
|
+
maxAttempts: 3,
|
|
903
|
+
backoffMs: 2000,
|
|
904
|
+
retryOn: [429, 500, 502, 503],
|
|
905
|
+
retryOnMidStream: [],
|
|
906
|
+
};
|
|
907
|
+
wrappedMainLlm = new RetryLlm(wrappedMainLlm, retryOpts);
|
|
908
|
+
// ---- Rate limiter wrapping (outermost — retry attempts also throttled) ----
|
|
909
|
+
if (this._rateLimiter) {
|
|
910
|
+
wrappedMainLlm = new RateLimiterLlm(wrappedMainLlm, this._rateLimiter);
|
|
878
911
|
}
|
|
879
|
-
|
|
880
|
-
|
|
912
|
+
// ---- Classifier -------------------------------------------------------
|
|
913
|
+
const classifierCfg = {};
|
|
914
|
+
if (this.cfg.prompts?.classifier)
|
|
915
|
+
classifierCfg.systemPrompt = this.cfg.prompts.classifier;
|
|
916
|
+
const classifier = this._classifier ??
|
|
917
|
+
new LlmClassifier(classifierLlm, classifierCfg, requestLogger);
|
|
918
|
+
// ---- Assembler --------------------------------------------------------
|
|
919
|
+
const assemblerCfg = {
|
|
920
|
+
maxTokens: agentCfg.contextBudgetTokens,
|
|
921
|
+
showReasoning: agentCfg.showReasoning,
|
|
922
|
+
reasoningInstruction: this.cfg.prompts?.reasoning,
|
|
923
|
+
historyRecencyWindow: agentCfg.historyRecencyWindow,
|
|
924
|
+
};
|
|
925
|
+
if (this.cfg.prompts?.system)
|
|
926
|
+
assemblerCfg.systemPromptPreamble = this.cfg.prompts.system;
|
|
927
|
+
const assembler = this._assembler ?? new ContextAssembler(assemblerCfg);
|
|
928
|
+
// ---- History memory & summarizer ----------------------------------------
|
|
929
|
+
let historyMemory;
|
|
930
|
+
let historySummarizer;
|
|
931
|
+
if (agentCfg.semanticHistoryEnabled) {
|
|
932
|
+
historyMemory =
|
|
933
|
+
this._historyMemory ??
|
|
934
|
+
new HistoryMemory({
|
|
935
|
+
maxSize: agentCfg.historyRecencyWindow ?? 3,
|
|
936
|
+
});
|
|
937
|
+
const summarizerLlm = this._helperLlm ?? mainLlm;
|
|
938
|
+
historySummarizer =
|
|
939
|
+
this._historySummarizer ??
|
|
940
|
+
new HistorySummarizer(summarizerLlm, agentCfg.historyTurnSummaryPrompt
|
|
941
|
+
? { prompt: agentCfg.historyTurnSummaryPrompt }
|
|
942
|
+
: undefined);
|
|
943
|
+
if (historyRag && !ragRegistry.get('history', 'global')) {
|
|
944
|
+
ragRegistry.register('history', historyRag, undefined, {
|
|
945
|
+
displayName: 'history',
|
|
946
|
+
scope: 'global',
|
|
947
|
+
});
|
|
948
|
+
// ragStores projection updates via mutation listener.
|
|
949
|
+
}
|
|
881
950
|
}
|
|
882
|
-
|
|
883
|
-
|
|
951
|
+
// ---- Plugin loader (optional) -------------------------------------------
|
|
952
|
+
let loadedPlugins;
|
|
953
|
+
if (this._pluginLoader) {
|
|
954
|
+
const plugins = await this._pluginLoader.load();
|
|
955
|
+
loadedPlugins = plugins;
|
|
956
|
+
if (plugins.reranker && !this._reranker) {
|
|
957
|
+
this._reranker = plugins.reranker;
|
|
958
|
+
}
|
|
959
|
+
if (plugins.queryExpander && !this._queryExpander) {
|
|
960
|
+
this._queryExpander = plugins.queryExpander;
|
|
961
|
+
}
|
|
962
|
+
if (plugins.outputValidator && !this._outputValidator) {
|
|
963
|
+
this._outputValidator = plugins.outputValidator;
|
|
964
|
+
}
|
|
965
|
+
if (plugins.skillManager && !this._skillManager) {
|
|
966
|
+
this._skillManager = plugins.skillManager;
|
|
967
|
+
}
|
|
968
|
+
if (plugins.clientAdapters.length > 0) {
|
|
969
|
+
this._clientAdapters.push(...plugins.clientAdapters);
|
|
970
|
+
}
|
|
884
971
|
}
|
|
885
|
-
|
|
886
|
-
|
|
972
|
+
// ---- Skill vectorization (optional) ------------------------------------
|
|
973
|
+
if (this._skillManager && toolsRag) {
|
|
974
|
+
await vectorizeSkills(this._skillManager, toolsRag, requestLogger, log);
|
|
887
975
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
976
|
+
// ---- Pipeline initialization -------------------------------------------
|
|
977
|
+
let resolvedCoordinator;
|
|
978
|
+
if (this._coordinator) {
|
|
979
|
+
const plannerLlm = this._coordinator.plannerLlm ?? wrappedMainLlm;
|
|
980
|
+
if (!plannerLlm) {
|
|
981
|
+
throw new Error('withCoordinator: requires either cfg.plannerLlm or withMainLlm() to be called');
|
|
982
|
+
}
|
|
983
|
+
// Construct a default context builder from this builder's available RAG
|
|
984
|
+
// + embedder resources. `toolSource` comes from the toolsRag the parent
|
|
985
|
+
// already uses for tool-loop retrieval. `projectSource` is left unset
|
|
986
|
+
// until a dedicated project/domain RAG slot is exposed on the builder.
|
|
987
|
+
const toolSource = this.buildRetrievalSource(toolsRag, this._embedder);
|
|
988
|
+
const defaultContextBuilder = new DefaultSubAgentContextBuilder({
|
|
989
|
+
toolSource,
|
|
990
|
+
});
|
|
991
|
+
resolvedCoordinator = {
|
|
992
|
+
...this._coordinator,
|
|
993
|
+
planning: this._coordinator.planning ?? new OneShotPlanning(plannerLlm),
|
|
994
|
+
dispatch: this._coordinator.dispatch ??
|
|
995
|
+
new HybridDispatch(new SubAgentDispatch(defaultContextBuilder), new SelfDispatch(plannerLlm)),
|
|
996
|
+
};
|
|
899
997
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
998
|
+
const pipeline = this._pipeline ??
|
|
999
|
+
new DefaultPipeline({
|
|
1000
|
+
subAgents: this._subAgents,
|
|
1001
|
+
coordinator: resolvedCoordinator,
|
|
1002
|
+
dagCoordinator: this._dagCoordinator,
|
|
1003
|
+
stepperCoordinator: this._stepperCoordinator,
|
|
1004
|
+
});
|
|
1005
|
+
pipeline.initialize({
|
|
1006
|
+
mainLlm: wrappedMainLlm,
|
|
1007
|
+
helperLlm,
|
|
1008
|
+
classifierLlm,
|
|
1009
|
+
classifier,
|
|
1010
|
+
assembler,
|
|
1011
|
+
mcpClients,
|
|
1012
|
+
mcpClientDescriptors,
|
|
1013
|
+
toolNamespace: this._toolNamespace,
|
|
1014
|
+
toolsRag,
|
|
1015
|
+
historyRag,
|
|
1016
|
+
ragStores,
|
|
1017
|
+
ragRegistry,
|
|
1018
|
+
ragProviderRegistry,
|
|
1019
|
+
embedder: this._embedder,
|
|
1020
|
+
toolSelectionStrategy: this._toolSelectionStrategy,
|
|
1021
|
+
reranker: this._reranker,
|
|
1022
|
+
queryExpander: this._queryExpander,
|
|
1023
|
+
toolPolicy: this._toolPolicy,
|
|
1024
|
+
injectionDetector: this._injectionDetector,
|
|
1025
|
+
toolCache: this._toolCache,
|
|
1026
|
+
outputValidator: this._outputValidator,
|
|
1027
|
+
sessionManager: this._sessionManager,
|
|
1028
|
+
skillManager: this._skillManager,
|
|
1029
|
+
logger: log,
|
|
1030
|
+
requestLogger,
|
|
1031
|
+
tracer: this._tracer,
|
|
1032
|
+
metrics: this._metrics,
|
|
1033
|
+
historyMemory,
|
|
1034
|
+
historySummarizer,
|
|
1035
|
+
llmCallStrategy: this._llmCallStrategy,
|
|
1036
|
+
agentConfig: agentCfg,
|
|
1037
|
+
...(this._mcpFailureClassifier
|
|
1038
|
+
? { mcpFailureClassifier: this._mcpFailureClassifier }
|
|
1039
|
+
: {}),
|
|
1040
|
+
...(this._toolLoopContextStrategyFactory
|
|
1041
|
+
? {
|
|
1042
|
+
toolLoopContextStrategyFactory: this._toolLoopContextStrategyFactory,
|
|
1043
|
+
}
|
|
1044
|
+
: {}),
|
|
921
1045
|
});
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
: {}),
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1046
|
+
const agent = new SmartAgent({
|
|
1047
|
+
mainLlm: wrappedMainLlm,
|
|
1048
|
+
helperLlm: this._helperLlm,
|
|
1049
|
+
mcpClients,
|
|
1050
|
+
ragStores,
|
|
1051
|
+
ragRegistry,
|
|
1052
|
+
ragProviderRegistry,
|
|
1053
|
+
classifier,
|
|
1054
|
+
classifierLlm,
|
|
1055
|
+
classifierConfig: classifierCfg,
|
|
1056
|
+
assembler,
|
|
1057
|
+
pipeline,
|
|
1058
|
+
...(log ? { logger: log } : {}),
|
|
1059
|
+
...(this._toolPolicy ? { toolPolicy: this._toolPolicy } : {}),
|
|
1060
|
+
...(this._injectionDetector
|
|
1061
|
+
? { injectionDetector: this._injectionDetector }
|
|
1062
|
+
: {}),
|
|
1063
|
+
...(this._reranker ? { reranker: this._reranker } : {}),
|
|
1064
|
+
...(this._queryExpander
|
|
1065
|
+
? { queryExpander: this._queryExpander }
|
|
1066
|
+
: {}),
|
|
1067
|
+
...(this._tracer ? { tracer: this._tracer } : {}),
|
|
1068
|
+
...(this._metrics ? { metrics: this._metrics } : {}),
|
|
1069
|
+
...(this._toolCache ? { toolCache: this._toolCache } : {}),
|
|
1070
|
+
...(this._outputValidator
|
|
1071
|
+
? { outputValidator: this._outputValidator }
|
|
1072
|
+
: {}),
|
|
1073
|
+
...(this._sessionManager
|
|
1074
|
+
? { sessionManager: this._sessionManager }
|
|
1075
|
+
: {}),
|
|
1076
|
+
...(this._skillManager ? { skillManager: this._skillManager } : {}),
|
|
1077
|
+
...(this._clientAdapters.length > 0
|
|
1078
|
+
? { clientAdapters: this._clientAdapters }
|
|
1079
|
+
: {}),
|
|
1080
|
+
...(this._embedder ? { embedder: this._embedder } : {}),
|
|
1081
|
+
...(connectionStrategy ? { connectionStrategy } : {}),
|
|
1082
|
+
toolCatalogStatus,
|
|
1083
|
+
...(this._toolRecordKey
|
|
1084
|
+
? { toolRecordKey: this._toolRecordKey }
|
|
1085
|
+
: {}),
|
|
1086
|
+
...(this._toolNamespace
|
|
1087
|
+
? { toolNamespace: this._toolNamespace }
|
|
1088
|
+
: {}),
|
|
1089
|
+
...(historyMemory ? { historyMemory } : {}),
|
|
1090
|
+
...(historySummarizer ? { historySummarizer } : {}),
|
|
1091
|
+
...(this._llmCallStrategy
|
|
1092
|
+
? { llmCallStrategy: this._llmCallStrategy }
|
|
1093
|
+
: {}),
|
|
1094
|
+
...(translateQueryStores.size > 0 ? { translateQueryStores } : {}),
|
|
1095
|
+
...(this._mcpFailureClassifier
|
|
1096
|
+
? { mcpFailureClassifier: this._mcpFailureClassifier }
|
|
1097
|
+
: {}),
|
|
1098
|
+
...(this._toolLoopContextStrategyFactory
|
|
1099
|
+
? {
|
|
1100
|
+
toolLoopContextStrategyFactory: this._toolLoopContextStrategyFactory,
|
|
1101
|
+
}
|
|
1102
|
+
: {}),
|
|
1103
|
+
requestLogger,
|
|
1104
|
+
}, agentCfg);
|
|
1105
|
+
// ---- Model provider auto-detection ------------------------------------
|
|
1106
|
+
let modelProvider = this._modelProvider;
|
|
1107
|
+
if (!modelProvider) {
|
|
1108
|
+
const candidate = mainLlm;
|
|
1109
|
+
if (isModelProvider(candidate)) {
|
|
1110
|
+
modelProvider = candidate;
|
|
960
1111
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
ragProviderRegistry,
|
|
970
|
-
classifier,
|
|
971
|
-
classifierLlm,
|
|
972
|
-
classifierConfig: classifierCfg,
|
|
973
|
-
assembler,
|
|
974
|
-
pipeline,
|
|
975
|
-
...(log ? { logger: log } : {}),
|
|
976
|
-
...(this._toolPolicy ? { toolPolicy: this._toolPolicy } : {}),
|
|
977
|
-
...(this._injectionDetector
|
|
978
|
-
? { injectionDetector: this._injectionDetector }
|
|
979
|
-
: {}),
|
|
980
|
-
...(this._reranker ? { reranker: this._reranker } : {}),
|
|
981
|
-
...(this._queryExpander ? { queryExpander: this._queryExpander } : {}),
|
|
982
|
-
...(this._tracer ? { tracer: this._tracer } : {}),
|
|
983
|
-
...(this._metrics ? { metrics: this._metrics } : {}),
|
|
984
|
-
...(this._toolCache ? { toolCache: this._toolCache } : {}),
|
|
985
|
-
...(this._outputValidator
|
|
986
|
-
? { outputValidator: this._outputValidator }
|
|
987
|
-
: {}),
|
|
988
|
-
...(this._sessionManager
|
|
989
|
-
? { sessionManager: this._sessionManager }
|
|
990
|
-
: {}),
|
|
991
|
-
...(this._skillManager ? { skillManager: this._skillManager } : {}),
|
|
992
|
-
...(this._clientAdapters.length > 0
|
|
993
|
-
? { clientAdapters: this._clientAdapters }
|
|
994
|
-
: {}),
|
|
995
|
-
...(this._embedder ? { embedder: this._embedder } : {}),
|
|
996
|
-
...(connectionStrategy ? { connectionStrategy } : {}),
|
|
997
|
-
toolCatalogStatus,
|
|
998
|
-
...(this._toolRecordKey ? { toolRecordKey: this._toolRecordKey } : {}),
|
|
999
|
-
...(this._toolNamespace ? { toolNamespace: this._toolNamespace } : {}),
|
|
1000
|
-
...(historyMemory ? { historyMemory } : {}),
|
|
1001
|
-
...(historySummarizer ? { historySummarizer } : {}),
|
|
1002
|
-
...(this._llmCallStrategy
|
|
1003
|
-
? { llmCallStrategy: this._llmCallStrategy }
|
|
1004
|
-
: {}),
|
|
1005
|
-
...(translateQueryStores.size > 0 ? { translateQueryStores } : {}),
|
|
1006
|
-
...(this._mcpFailureClassifier
|
|
1007
|
-
? { mcpFailureClassifier: this._mcpFailureClassifier }
|
|
1008
|
-
: {}),
|
|
1009
|
-
...(this._toolLoopContextStrategyFactory
|
|
1010
|
-
? {
|
|
1011
|
-
toolLoopContextStrategyFactory: this._toolLoopContextStrategyFactory,
|
|
1112
|
+
}
|
|
1113
|
+
// ---- API adapters: merge plugin adapters → builder adapters (builder wins) ---
|
|
1114
|
+
// plugins.apiAdapters does not exist yet (added in Task 4); safe future-compat check.
|
|
1115
|
+
const apiAdapters = new Map();
|
|
1116
|
+
const pluginApiAdapters = loadedPlugins?.apiAdapters;
|
|
1117
|
+
if (pluginApiAdapters) {
|
|
1118
|
+
for (const [name, adapter] of pluginApiAdapters) {
|
|
1119
|
+
apiAdapters.set(name, adapter);
|
|
1012
1120
|
}
|
|
1013
|
-
: {}),
|
|
1014
|
-
requestLogger,
|
|
1015
|
-
}, agentCfg);
|
|
1016
|
-
// ---- Model provider auto-detection ------------------------------------
|
|
1017
|
-
let modelProvider = this._modelProvider;
|
|
1018
|
-
if (!modelProvider) {
|
|
1019
|
-
const candidate = mainLlm;
|
|
1020
|
-
if (isModelProvider(candidate)) {
|
|
1021
|
-
modelProvider = candidate;
|
|
1022
1121
|
}
|
|
1023
|
-
|
|
1024
|
-
// ---- API adapters: merge plugin adapters → builder adapters (builder wins) ---
|
|
1025
|
-
// plugins.apiAdapters does not exist yet (added in Task 4); safe future-compat check.
|
|
1026
|
-
const apiAdapters = new Map();
|
|
1027
|
-
const pluginApiAdapters = loadedPlugins?.apiAdapters;
|
|
1028
|
-
if (pluginApiAdapters) {
|
|
1029
|
-
for (const [name, adapter] of pluginApiAdapters) {
|
|
1122
|
+
for (const [name, adapter] of this._apiAdapters) {
|
|
1030
1123
|
apiAdapters.set(name, adapter);
|
|
1031
1124
|
}
|
|
1125
|
+
return {
|
|
1126
|
+
agent,
|
|
1127
|
+
chat: (messages, tools, options) => agent.currentMainLlm.chat(messages, tools, options),
|
|
1128
|
+
streamChat: (messages, tools, options) => agent.currentMainLlm.streamChat(messages, tools, options),
|
|
1129
|
+
requestLogger,
|
|
1130
|
+
close: async () => {
|
|
1131
|
+
// Best-effort, like every other teardown routine in this codebase
|
|
1132
|
+
// (SessionGraph.dispose(), SessionGraphFactory's per-stop loop): a
|
|
1133
|
+
// failure in ONE resource must not stop the rest from being torn
|
|
1134
|
+
// down, and close() itself never throws — it is routinely awaited
|
|
1135
|
+
// in a `finally` block, where a raised error would either mask
|
|
1136
|
+
// whatever the `try` block was doing or force every caller to wrap
|
|
1137
|
+
// a routine cleanup call in its own try/catch. Failures are never
|
|
1138
|
+
// swallowed SILENTLY, though: both surface through `log` (or
|
|
1139
|
+
// console.warn without one), the same pattern used everywhere else
|
|
1140
|
+
// teardown failures are reported in this branch.
|
|
1141
|
+
const warn = (message) => {
|
|
1142
|
+
if (log) {
|
|
1143
|
+
log.log({ type: 'warning', traceId: 'builder', message });
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
console.warn(`[builder] ${message}`);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
try {
|
|
1150
|
+
await connectionStrategy?.dispose?.();
|
|
1151
|
+
}
|
|
1152
|
+
catch (err) {
|
|
1153
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1154
|
+
warn(`close_connection_strategy_dispose_failed: ${message}`);
|
|
1155
|
+
}
|
|
1156
|
+
await stopAll(closeFns, (err) => {
|
|
1157
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1158
|
+
warn(`close_mcp_stop_failed: ${message}`);
|
|
1159
|
+
});
|
|
1160
|
+
},
|
|
1161
|
+
circuitBreakers,
|
|
1162
|
+
ragStores,
|
|
1163
|
+
ragRegistry,
|
|
1164
|
+
mcpClients,
|
|
1165
|
+
modelProvider,
|
|
1166
|
+
getApiAdapter: (name) => apiAdapters.get(name),
|
|
1167
|
+
listApiAdapters: () => [...apiAdapters.keys()],
|
|
1168
|
+
// Absent (not just undefined-valued) on the caller-provided-`mcpClients`
|
|
1169
|
+
// branch, where `resolved` is undefined and namespacing was never
|
|
1170
|
+
// computed — matches the SmartAgentHandle field docs.
|
|
1171
|
+
...(resolved
|
|
1172
|
+
? {
|
|
1173
|
+
namespacedTools,
|
|
1174
|
+
toolProvenance,
|
|
1175
|
+
mcpClientDescriptors: resolved.clientDescriptors,
|
|
1176
|
+
configuredSlotCount: resolved.configuredSlotCount,
|
|
1177
|
+
}
|
|
1178
|
+
: {}),
|
|
1179
|
+
};
|
|
1032
1180
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1181
|
+
catch (err) {
|
|
1182
|
+
// Everything from here down (model validation was already done above)
|
|
1183
|
+
// can throw before a handle exists to own `close()`: stop every server
|
|
1184
|
+
// already registered in `closeFns` — via `withMcpServers` — on the
|
|
1185
|
+
// ORIGINAL failure, then let that failure through unchanged.
|
|
1186
|
+
await stopAll(closeFns);
|
|
1187
|
+
throw err;
|
|
1035
1188
|
}
|
|
1036
|
-
return {
|
|
1037
|
-
agent,
|
|
1038
|
-
chat: (messages, tools, options) => agent.currentMainLlm.chat(messages, tools, options),
|
|
1039
|
-
streamChat: (messages, tools, options) => agent.currentMainLlm.streamChat(messages, tools, options),
|
|
1040
|
-
requestLogger,
|
|
1041
|
-
close: async () => {
|
|
1042
|
-
await connectionStrategy?.dispose?.();
|
|
1043
|
-
for (const fn of closeFns)
|
|
1044
|
-
await fn();
|
|
1045
|
-
},
|
|
1046
|
-
circuitBreakers,
|
|
1047
|
-
ragStores,
|
|
1048
|
-
ragRegistry,
|
|
1049
|
-
mcpClients,
|
|
1050
|
-
modelProvider,
|
|
1051
|
-
getApiAdapter: (name) => apiAdapters.get(name),
|
|
1052
|
-
listApiAdapters: () => [...apiAdapters.keys()],
|
|
1053
|
-
// Absent (not just undefined-valued) on the caller-provided-`mcpClients`
|
|
1054
|
-
// branch, where `resolved` is undefined and namespacing was never
|
|
1055
|
-
// computed — matches the SmartAgentHandle field docs.
|
|
1056
|
-
...(resolved
|
|
1057
|
-
? {
|
|
1058
|
-
namespacedTools,
|
|
1059
|
-
toolProvenance,
|
|
1060
|
-
mcpClientDescriptors: resolved.clientDescriptors,
|
|
1061
|
-
configuredSlotCount: resolved.configuredSlotCount,
|
|
1062
|
-
}
|
|
1063
|
-
: {}),
|
|
1064
|
-
};
|
|
1065
1189
|
}
|
|
1066
1190
|
}
|
|
1067
1191
|
//# sourceMappingURL=builder.js.map
|