@agentforge/patterns 0.16.91 → 0.17.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.
package/README.md CHANGED
@@ -84,7 +84,7 @@ The Multi-Agent pattern coordinates multiple specialized agents for complex task
84
84
  - Flexible routing strategies (LLM-based, skill-based, rule-based, round-robin, load-balanced)
85
85
  - Parallel execution support
86
86
  - Intelligent result aggregation
87
- - Dynamic worker registration
87
+ - Fixed Worker topology construction before compilation
88
88
  - **22+ tests** - Comprehensive coverage
89
89
 
90
90
  ## Installation
@@ -418,6 +418,7 @@ import {
418
418
  ```typescript
419
419
  import {
420
420
  createMultiAgentSystem,
421
+ createWorkersRegistry,
421
422
  registerWorkers,
422
423
  createSupervisorNode,
423
424
  createWorkerNode,
@@ -428,7 +429,14 @@ import {
428
429
  **Main API**:
429
430
  - `createMultiAgentSystem(config)` - Create a complete Multi-Agent system
430
431
  - `MultiAgentSystemBuilder` - Builder for creating Multi-Agent systems with workers
431
- - `registerWorkers(system, workers)` - Update worker capabilities in state (Note: does not add worker nodes to compiled graphs; use builder or pass workers to createMultiAgentSystem)
432
+ - `createWorkersRegistry(workers)` - Construct detached, normalized Worker registry records containing declared capabilities and invocation status without admitting Workers or creating graph topology
433
+ - `registerWorkers(system, workers)` - Deprecated adapter that updates routing skills for known Workers; supplied tools must match the compiled executable tool set
434
+
435
+ Worker identities and executable tools are fixed when the Multi-Agent System is
436
+ compiled. The builder's `registerWorkers(workers)` method constructs that topology
437
+ before `build()`. The deprecated `registerWorkers(system, workers)` adapter does
438
+ not add executable Worker nodes: it may update routing skills for known Workers,
439
+ and those updates are visible only to later executions.
432
440
 
433
441
  **Configuration**:
434
442
  ```typescript
@@ -540,4 +548,3 @@ pnpm typecheck
540
548
  ## License
541
549
 
542
550
  MIT © 2026 Tom Van Schoor
543
-
package/dist/index.cjs CHANGED
@@ -71,6 +71,7 @@ __export(index_exports, {
71
71
  ToolCallSchema: () => ToolCallSchema,
72
72
  ToolResultSchema: () => ToolResultSchema,
73
73
  WorkerCapabilitiesSchema: () => WorkerCapabilitiesSchema,
74
+ WorkerLifecycleError: () => WorkerLifecycleError,
74
75
  buildDeduplicationMetrics: () => buildDeduplicationMetrics,
75
76
  calculateDeduplicationSavings: () => calculateDeduplicationSavings,
76
77
  createAggregatorNode: () => createAggregatorNode,
@@ -90,6 +91,7 @@ __export(index_exports, {
90
91
  createReviserNode: () => createReviserNode,
91
92
  createSupervisorNode: () => createSupervisorNode,
92
93
  createWorkerNode: () => createWorkerNode,
94
+ createWorkersRegistry: () => createWorkersRegistry,
93
95
  generateToolCallCacheKey: () => generateToolCallCacheKey,
94
96
  getRoutingStrategy: () => getRoutingStrategy,
95
97
  llmBasedRouting: () => llmBasedRouting,
@@ -431,9 +433,9 @@ function stringifyActionArguments(arguments_) {
431
433
  function getLatestThought(thoughts) {
432
434
  return thoughts[thoughts.length - 1]?.content ?? "";
433
435
  }
434
- function debugIfVerbose(logger6, verbose, message, data) {
436
+ function debugIfVerbose(logger7, verbose, message, data) {
435
437
  if (verbose) {
436
- logger6.debug(message, data);
438
+ logger7.debug(message, data);
437
439
  }
438
440
  }
439
441
 
@@ -2265,10 +2267,7 @@ var MultiAgentStateConfig = {
2265
2267
  */
2266
2268
  workers: {
2267
2269
  schema: import_zod13.z.record(import_zod13.z.string(), WorkerCapabilitiesSchema),
2268
- reducer: (left, right) => ({
2269
- ...left,
2270
- ...right
2271
- }),
2270
+ reducer: (_left, right) => right,
2272
2271
  default: () => ({}),
2273
2272
  description: "Available worker agents and their capabilities"
2274
2273
  },
@@ -3477,163 +3476,295 @@ function createWorkerNode(config) {
3477
3476
  // src/multi-agent/agent-graph.ts
3478
3477
  var import_langgraph4 = require("@langchain/langgraph");
3479
3478
 
3480
- // src/multi-agent/agent-workers.ts
3481
- function getToolName(tool) {
3482
- if (!tool || typeof tool !== "object") {
3483
- return "unknown";
3479
+ // src/multi-agent/worker-lifecycle.ts
3480
+ var WorkerLifecycleError = class extends Error {
3481
+ reason;
3482
+ constructor(reason, message) {
3483
+ super(message);
3484
+ this.name = "WorkerLifecycleError";
3485
+ this.reason = reason;
3484
3486
  }
3485
- const candidate = tool;
3486
- if (typeof candidate.metadata?.name === "string") {
3487
- return candidate.metadata.name;
3487
+ };
3488
+ var workerLifecycles = /* @__PURE__ */ new WeakMap();
3489
+ function normalizeToolName(tool, workerId, index) {
3490
+ const candidate = tool && typeof tool === "object" ? tool : void 0;
3491
+ const rawName = candidate?.metadata?.name ?? candidate?.name;
3492
+ const name = typeof rawName === "string" ? rawName.trim() : "";
3493
+ if (!name) {
3494
+ throw new WorkerLifecycleError(
3495
+ "invalid-tool",
3496
+ `Worker "${workerId}" has a nameless tool at index ${index}.`
3497
+ );
3488
3498
  }
3489
- if (typeof candidate.name === "string") {
3490
- return candidate.name;
3499
+ return name;
3500
+ }
3501
+ function normalizeWorkerToolNames(workerId, tools) {
3502
+ const toolNames = (tools ?? []).map((tool, index) => normalizeToolName(tool, workerId, index));
3503
+ const uniqueToolNames = new Set(toolNames);
3504
+ if (uniqueToolNames.size !== toolNames.length) {
3505
+ throw new WorkerLifecycleError(
3506
+ "invalid-tool",
3507
+ `Worker "${workerId}" has duplicate normalized tool names.`
3508
+ );
3491
3509
  }
3492
- return "unknown";
3510
+ return Object.freeze(toolNames);
3493
3511
  }
3494
- function toWorkerCapabilities(worker) {
3495
- return {
3496
- skills: worker.capabilities,
3497
- tools: worker.tools?.map((tool) => getToolName(tool)) || [],
3498
- available: true,
3499
- currentWorkload: 0
3500
- };
3512
+ function validateIdentities(workers) {
3513
+ for (const worker of workers) {
3514
+ if (!worker.id || worker.id.trim() !== worker.id || worker.id.includes(",")) {
3515
+ throw new WorkerLifecycleError(
3516
+ "invalid-identity",
3517
+ `Worker identity "${worker.id}" must be non-empty, trimmed, and comma-free.`
3518
+ );
3519
+ }
3520
+ }
3521
+ const identities = /* @__PURE__ */ new Set();
3522
+ for (const worker of workers) {
3523
+ if (identities.has(worker.id)) {
3524
+ throw new WorkerLifecycleError(
3525
+ "duplicate-identity",
3526
+ `Worker identity "${worker.id}" appears more than once in the Worker batch.`
3527
+ );
3528
+ }
3529
+ identities.add(worker.id);
3530
+ }
3501
3531
  }
3502
- function toWorkerConfig(worker, fallbackModel) {
3532
+ function normalizeWorkerDeclaredCapabilities(workerId, capabilities, tools) {
3503
3533
  return {
3504
- id: worker.name,
3505
- capabilities: toWorkerCapabilities(worker),
3506
- model: worker.model || fallbackModel,
3507
- tools: worker.tools,
3508
- systemPrompt: worker.systemPrompt
3534
+ skills: Object.freeze([...capabilities.skills]),
3535
+ tools: normalizeWorkerToolNames(workerId, tools)
3509
3536
  };
3510
3537
  }
3511
-
3512
- // src/multi-agent/agent-runtime.ts
3513
- function mergeWorkers(input, workerCapabilities) {
3538
+ function normalizeWorkerStatus(capabilities) {
3514
3539
  return {
3515
- ...input,
3516
- workers: {
3517
- ...workerCapabilities,
3518
- ...input.workers || {}
3519
- }
3540
+ available: capabilities.available,
3541
+ currentWorkload: capabilities.currentWorkload
3520
3542
  };
3521
3543
  }
3522
- function wrapCompiledSystem(system, workerCapabilities) {
3523
- const originalInvoke = system.invoke.bind(system);
3524
- system.invoke = (async function(input, config) {
3525
- return originalInvoke(
3526
- mergeWorkers(input, workerCapabilities),
3527
- config
3528
- );
3544
+ function normalizeWorkerRegistryRecord(workerId, capabilities, tools) {
3545
+ return Object.freeze({
3546
+ ...normalizeWorkerDeclaredCapabilities(workerId, capabilities, tools),
3547
+ ...normalizeWorkerStatus(capabilities)
3548
+ });
3549
+ }
3550
+ function asCompatibleWorkerCapabilities(record) {
3551
+ return record;
3552
+ }
3553
+ function admitWorker(worker) {
3554
+ const capabilities = asCompatibleWorkerCapabilities(
3555
+ normalizeWorkerRegistryRecord(worker.id, worker.capabilities, worker.tools)
3556
+ );
3557
+ const tools = worker.tools ? Object.freeze([...worker.tools]) : void 0;
3558
+ return Object.freeze({
3559
+ ...worker,
3560
+ capabilities,
3561
+ ...tools ? { tools } : {}
3529
3562
  });
3530
- const originalStream = system.stream.bind(system);
3531
- system.stream = (async function(input, config) {
3532
- return originalStream(
3533
- mergeWorkers(input, workerCapabilities),
3534
- config
3563
+ }
3564
+ function admitWorkerTopology(workers) {
3565
+ if (workers.length === 0) {
3566
+ throw new WorkerLifecycleError(
3567
+ "empty-topology",
3568
+ "A Multi-Agent System requires at least one Worker."
3535
3569
  );
3570
+ }
3571
+ validateIdentities(workers);
3572
+ const topology = Object.freeze(workers.map(admitWorker));
3573
+ let workerCapabilities = Object.freeze(
3574
+ Object.fromEntries(topology.map((worker) => [worker.id, worker.capabilities]))
3575
+ );
3576
+ return Object.freeze({
3577
+ topology,
3578
+ captureWorkerSnapshot: () => workerCapabilities,
3579
+ updateRoutingSkills: (updates) => {
3580
+ if (updates.length === 0) {
3581
+ return;
3582
+ }
3583
+ validateIdentities(updates);
3584
+ const replacements = updates.map((update) => {
3585
+ const current = workerCapabilities[update.id];
3586
+ if (!Object.hasOwn(workerCapabilities, update.id) || !current) {
3587
+ throw new WorkerLifecycleError(
3588
+ "unknown-worker",
3589
+ `Cannot update unknown Worker "${update.id}" after compilation.`
3590
+ );
3591
+ }
3592
+ if (update.assertedTools !== void 0) {
3593
+ const assertedTools = [
3594
+ ...normalizeWorkerToolNames(update.id, update.assertedTools)
3595
+ ].sort();
3596
+ const executableTools = [...current.tools].sort();
3597
+ if (assertedTools.length !== executableTools.length || assertedTools.some((toolName, index) => toolName !== executableTools[index])) {
3598
+ throw new WorkerLifecycleError(
3599
+ "invalid-tool",
3600
+ `Worker "${update.id}" tools do not match its compiled executable tools.`
3601
+ );
3602
+ }
3603
+ }
3604
+ return [
3605
+ update.id,
3606
+ asCompatibleWorkerCapabilities(
3607
+ Object.freeze({
3608
+ ...current,
3609
+ skills: Object.freeze([...update.skills])
3610
+ })
3611
+ )
3612
+ ];
3613
+ });
3614
+ workerCapabilities = Object.freeze({
3615
+ ...workerCapabilities,
3616
+ ...Object.fromEntries(replacements)
3617
+ });
3618
+ }
3536
3619
  });
3537
- return system;
3538
3620
  }
3539
- function registerWorkerCapabilities(system, workers) {
3540
- if (!system._workerRegistry) {
3541
- system._workerRegistry = {};
3621
+ function associateWorkerLifecycle(system, lifecycle) {
3622
+ workerLifecycles.set(system, lifecycle);
3623
+ }
3624
+ function resolveWorkerLifecycle(system) {
3625
+ const lifecycle = workerLifecycles.get(system);
3626
+ if (!lifecycle) {
3627
+ throw new WorkerLifecycleError(
3628
+ "unsupported-system",
3629
+ "The compiled graph was not created by the Worker lifecycle."
3630
+ );
3542
3631
  }
3543
- for (const worker of workers) {
3544
- system._workerRegistry[worker.name] = toWorkerCapabilities(worker);
3545
- }
3546
- if (!system._originalInvoke) {
3547
- system._originalInvoke = system.invoke.bind(system);
3548
- system.invoke = (async function(input, config) {
3549
- return system._originalInvoke(
3550
- mergeWorkers(input, system._workerRegistry || {}),
3551
- config
3632
+ return lifecycle;
3633
+ }
3634
+ function createWorkerRegistryData(workers) {
3635
+ validateIdentities(workers);
3636
+ return Object.freeze(
3637
+ Object.fromEntries(
3638
+ workers.map((worker) => [
3639
+ worker.id,
3640
+ asCompatibleWorkerCapabilities(
3641
+ normalizeWorkerRegistryRecord(
3642
+ worker.id,
3643
+ worker.capabilities,
3644
+ worker.capabilities.tools.map((name) => ({ name }))
3645
+ )
3646
+ )
3647
+ ])
3648
+ )
3649
+ );
3650
+ }
3651
+
3652
+ // src/multi-agent/worker-initialization.ts
3653
+ var logger4 = createPatternLogger("agentforge:patterns:multi-agent:worker-initialization");
3654
+ function initializeWorkerState(topologyCapabilities, invocationWorkers) {
3655
+ for (const workerId of Object.keys(invocationWorkers)) {
3656
+ if (!Object.hasOwn(topologyCapabilities, workerId)) {
3657
+ throw new WorkerLifecycleError(
3658
+ "unknown-worker",
3659
+ `Invocation state contains unknown Worker "${workerId}".`
3552
3660
  );
3553
- });
3661
+ }
3554
3662
  }
3555
- if (!system._originalStream) {
3556
- system._originalStream = system.stream.bind(system);
3557
- system.stream = (async function(input, config) {
3558
- return system._originalStream(
3559
- mergeWorkers(input, system._workerRegistry || {}),
3560
- config
3561
- );
3663
+ return Object.fromEntries(
3664
+ Object.entries(topologyCapabilities).map(([workerId, capabilities]) => {
3665
+ const status = Object.hasOwn(invocationWorkers, workerId) ? invocationWorkers[workerId] : capabilities;
3666
+ return [
3667
+ workerId,
3668
+ {
3669
+ skills: [...capabilities.skills],
3670
+ tools: [...capabilities.tools],
3671
+ available: status.available,
3672
+ currentWorkload: status.currentWorkload
3673
+ }
3674
+ ];
3675
+ })
3676
+ );
3677
+ }
3678
+ function createWorkerInitializationNode(captureWorkerSnapshot) {
3679
+ return async (state) => {
3680
+ const topologyCapabilities = captureWorkerSnapshot();
3681
+ logger4.info("Worker initialization started", {
3682
+ workerCount: Object.keys(topologyCapabilities).length,
3683
+ statusOverrideCount: Object.keys(state.workers).length
3562
3684
  });
3563
- }
3685
+ let workers;
3686
+ try {
3687
+ workers = initializeWorkerState(topologyCapabilities, state.workers);
3688
+ } catch (error) {
3689
+ logger4.error("Worker initialization failed", {
3690
+ error: error instanceof Error ? error.message : String(error),
3691
+ invocationWorkerIds: Object.keys(state.workers),
3692
+ topologyWorkerIds: Object.keys(topologyCapabilities)
3693
+ });
3694
+ throw error;
3695
+ }
3696
+ logger4.info("Worker initialization complete", {
3697
+ workerCount: Object.keys(workers).length,
3698
+ workerIds: Object.keys(workers)
3699
+ });
3700
+ return { workers };
3701
+ };
3564
3702
  }
3565
3703
 
3566
3704
  // src/multi-agent/agent-graph.ts
3567
- var logger4 = createPatternLogger("agentforge:patterns:multi-agent:system");
3705
+ var logger5 = createPatternLogger("agentforge:patterns:multi-agent:system");
3568
3706
  function createSupervisorRouter() {
3569
3707
  return (state) => {
3570
- logger4.debug("Supervisor router executing", {
3708
+ logger5.debug("Supervisor router executing", {
3571
3709
  status: state.status,
3572
3710
  ...state.currentAgent ? { currentAgent: state.currentAgent } : {},
3573
3711
  iteration: state.iteration
3574
3712
  });
3575
3713
  if (state.status === "completed" || state.status === "failed") {
3576
- logger4.info("Supervisor router: ending workflow", { status: state.status });
3714
+ logger5.info("Supervisor router: ending workflow", { status: state.status });
3577
3715
  return import_langgraph4.END;
3578
3716
  }
3579
3717
  if (state.status === "aggregating") {
3580
- logger4.info("Supervisor router: routing to aggregator");
3718
+ logger5.info("Supervisor router: routing to aggregator");
3581
3719
  return "aggregator";
3582
3720
  }
3583
3721
  if (state.currentAgent && state.currentAgent !== "supervisor") {
3584
3722
  if (state.currentAgent.includes(",")) {
3585
3723
  const agents = state.currentAgent.split(",").map((agent) => agent.trim());
3586
- logger4.info("Supervisor router: parallel routing", {
3724
+ logger5.info("Supervisor router: parallel routing", {
3587
3725
  agents,
3588
3726
  count: agents.length
3589
3727
  });
3590
3728
  return agents;
3591
3729
  }
3592
- logger4.info("Supervisor router: single agent routing", {
3730
+ logger5.info("Supervisor router: single agent routing", {
3593
3731
  targetAgent: state.currentAgent
3594
3732
  });
3595
3733
  return state.currentAgent;
3596
3734
  }
3597
- logger4.debug("Supervisor router: staying at supervisor");
3735
+ logger5.debug("Supervisor router: staying at supervisor");
3598
3736
  return "supervisor";
3599
3737
  };
3600
3738
  }
3601
3739
  function createWorkerRouter() {
3602
3740
  return (state) => {
3603
- logger4.debug("Worker router executing", {
3741
+ logger5.debug("Worker router executing", {
3604
3742
  iteration: state.iteration,
3605
3743
  completedTasks: state.completedTasks.length
3606
3744
  });
3607
- logger4.debug("Worker router: returning to supervisor");
3745
+ logger5.debug("Worker router: returning to supervisor");
3608
3746
  return "supervisor";
3609
3747
  };
3610
3748
  }
3611
3749
  function createAggregatorRouter() {
3612
3750
  return (state) => {
3613
- logger4.info("Aggregator router: ending workflow", {
3751
+ logger5.info("Aggregator router: ending workflow", {
3614
3752
  completedTasks: state.completedTasks.length,
3615
3753
  status: state.status
3616
3754
  });
3617
3755
  return import_langgraph4.END;
3618
3756
  };
3619
3757
  }
3620
- function createCompiledMultiAgentSystem(config) {
3621
- const {
3622
- supervisor,
3623
- workers,
3624
- aggregator,
3625
- maxIterations = 10,
3626
- verbose = false,
3627
- checkpointer
3628
- } = config;
3758
+ function createCompiledMultiAgentSystem(config, lifecycle = admitWorkerTopology(config.workers)) {
3759
+ const { supervisor, aggregator, maxIterations = 10, verbose = false, checkpointer } = config;
3629
3760
  const workflow = new import_langgraph4.StateGraph(MultiAgentState);
3630
3761
  const workerIds = [];
3631
- const workerCapabilities = {};
3632
3762
  workflow.addNode(
3633
- "supervisor",
3634
- createSupervisorNode({ ...supervisor, maxIterations, verbose })
3763
+ "initializeWorkers",
3764
+ createWorkerInitializationNode(lifecycle.captureWorkerSnapshot)
3635
3765
  );
3636
- for (const workerConfig of workers) {
3766
+ workflow.addNode("supervisor", createSupervisorNode({ ...supervisor, maxIterations, verbose }));
3767
+ for (const workerConfig of lifecycle.topology) {
3637
3768
  workflow.addNode(
3638
3769
  workerConfig.id,
3639
3770
  createWorkerNode({
@@ -3642,7 +3773,6 @@ function createCompiledMultiAgentSystem(config) {
3642
3773
  })
3643
3774
  );
3644
3775
  workerIds.push(workerConfig.id);
3645
- workerCapabilities[workerConfig.id] = workerConfig.capabilities;
3646
3776
  }
3647
3777
  workflow.addNode(
3648
3778
  "aggregator",
@@ -3654,65 +3784,110 @@ function createCompiledMultiAgentSystem(config) {
3654
3784
  const supervisorRouter = createSupervisorRouter();
3655
3785
  const workerRouter = createWorkerRouter();
3656
3786
  const aggregatorRouter = createAggregatorRouter();
3657
- workflow.setEntryPoint("supervisor");
3787
+ workflow.setEntryPoint("initializeWorkers");
3788
+ workflow.addEdge("initializeWorkers", "supervisor");
3658
3789
  workflow.addConditionalEdges("supervisor", supervisorRouter, ["aggregator", import_langgraph4.END, ...workerIds]);
3659
3790
  for (const workerId of workerIds) {
3660
3791
  workflow.addConditionalEdges(workerId, workerRouter, ["supervisor"]);
3661
3792
  }
3662
3793
  workflow.addConditionalEdges("aggregator", aggregatorRouter, [import_langgraph4.END]);
3663
- const compiled = workflow.compile(
3794
+ const system = workflow.compile(
3664
3795
  checkpointer ? { checkpointer } : void 0
3665
3796
  );
3666
- return wrapCompiledSystem(compiled, workerCapabilities);
3797
+ associateWorkerLifecycle(system, lifecycle);
3798
+ return system;
3799
+ }
3800
+
3801
+ // src/multi-agent/agent-runtime.ts
3802
+ function registerWorkerCapabilities(system, workers) {
3803
+ resolveWorkerLifecycle(system).updateRoutingSkills(
3804
+ workers.map((worker) => ({
3805
+ id: worker.name,
3806
+ skills: worker.capabilities,
3807
+ ...worker.tools === void 0 ? {} : { assertedTools: worker.tools }
3808
+ }))
3809
+ );
3810
+ }
3811
+
3812
+ // src/multi-agent/agent-workers.ts
3813
+ function toWorkerConfig(worker, fallbackModel) {
3814
+ return {
3815
+ id: worker.name,
3816
+ capabilities: {
3817
+ skills: worker.capabilities,
3818
+ tools: [],
3819
+ available: true,
3820
+ currentWorkload: 0
3821
+ },
3822
+ model: worker.model || fallbackModel,
3823
+ tools: worker.tools,
3824
+ systemPrompt: worker.systemPrompt
3825
+ };
3667
3826
  }
3668
3827
 
3669
3828
  // src/multi-agent/agent-builder.ts
3670
3829
  var MultiAgentSystemBuilder = class {
3671
3830
  config;
3672
- additionalWorkers = [];
3831
+ lifecycle;
3673
3832
  compiled = false;
3674
3833
  constructor(config) {
3675
3834
  this.config = {
3676
3835
  ...config,
3677
- workers: config.workers || []
3836
+ workers: []
3678
3837
  };
3838
+ this.lifecycle = config.workers?.length ? admitWorkerTopology(config.workers) : void 0;
3679
3839
  }
3680
3840
  registerWorkers(workers) {
3681
3841
  if (this.compiled) {
3682
3842
  throw new Error("Cannot register workers after the system has been compiled");
3683
3843
  }
3684
- for (const worker of workers) {
3685
- this.additionalWorkers.push(toWorkerConfig(worker, this.config.supervisor.model));
3844
+ if (workers.length === 0) {
3845
+ return this;
3686
3846
  }
3847
+ const existingWorkers = this.lifecycle?.topology ?? [];
3848
+ const pendingWorkers = workers.map(
3849
+ (worker) => toWorkerConfig(worker, this.config.supervisor.model)
3850
+ );
3851
+ const lifecycle = admitWorkerTopology([...existingWorkers, ...pendingWorkers]);
3852
+ this.lifecycle = lifecycle;
3687
3853
  return this;
3688
3854
  }
3689
3855
  build() {
3690
3856
  if (this.compiled) {
3691
3857
  throw new Error("System has already been compiled");
3692
3858
  }
3693
- const allWorkers = [...this.config.workers, ...this.additionalWorkers];
3694
- if (allWorkers.length === 0) {
3695
- throw new Error("At least one worker must be registered before building the system");
3696
- }
3859
+ const lifecycle = this.lifecycle ?? admitWorkerTopology([]);
3860
+ const system = createCompiledMultiAgentSystem(
3861
+ {
3862
+ ...this.config,
3863
+ workers: [...lifecycle.topology]
3864
+ },
3865
+ lifecycle
3866
+ );
3697
3867
  this.compiled = true;
3698
- return createCompiledMultiAgentSystem({
3699
- ...this.config,
3700
- workers: allWorkers
3701
- });
3868
+ return system;
3702
3869
  }
3703
3870
  };
3704
3871
 
3705
3872
  // src/multi-agent/agent.ts
3706
- var logger5 = createPatternLogger("agentforge:patterns:multi-agent:system");
3873
+ var logger6 = createPatternLogger("agentforge:patterns:multi-agent:system");
3874
+ var systemsWarnedAboutLegacyRegistration = /* @__PURE__ */ new WeakSet();
3707
3875
  function createMultiAgentSystem(config) {
3708
- return createCompiledMultiAgentSystem(config);
3876
+ const lifecycle = admitWorkerTopology(config.workers);
3877
+ return createCompiledMultiAgentSystem(config, lifecycle);
3709
3878
  }
3710
3879
  function registerWorkers(system, workers) {
3711
- logger5.warn(
3712
- "[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/TVScoundrel/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
3713
- );
3880
+ if (!systemsWarnedAboutLegacyRegistration.has(system)) {
3881
+ systemsWarnedAboutLegacyRegistration.add(system);
3882
+ logger6.warn(
3883
+ "[AgentForge] registerWorkers() on a compiled system only updates routing skills for known Workers.\nIt does NOT add Worker nodes or change compiled tools. Use MultiAgentSystemBuilder before compilation.\nSee: https://github.com/TVScoundrel/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
3884
+ );
3885
+ }
3714
3886
  registerWorkerCapabilities(system, workers);
3715
3887
  }
3888
+ function createWorkersRegistry(workers) {
3889
+ return createWorkerRegistryData(workers);
3890
+ }
3716
3891
  // Annotate the CommonJS export names for ESM import in node:
3717
3892
  0 && (module.exports = {
3718
3893
  AgentMessageSchema,
@@ -3766,6 +3941,7 @@ function registerWorkers(system, workers) {
3766
3941
  ToolCallSchema,
3767
3942
  ToolResultSchema,
3768
3943
  WorkerCapabilitiesSchema,
3944
+ WorkerLifecycleError,
3769
3945
  buildDeduplicationMetrics,
3770
3946
  calculateDeduplicationSavings,
3771
3947
  createAggregatorNode,
@@ -3785,6 +3961,7 @@ function registerWorkers(system, workers) {
3785
3961
  createReviserNode,
3786
3962
  createSupervisorNode,
3787
3963
  createWorkerNode,
3964
+ createWorkersRegistry,
3788
3965
  generateToolCallCacheKey,
3789
3966
  getRoutingStrategy,
3790
3967
  llmBasedRouting,