JavaScript Agent Machine

build Fri 15 Mar 20:32:07 CET 2024 1 month ago
demo/ sejam2 Fri 15 Mar 20:33:36 CET 2024 1 month ago
dist Sun 27 Aug 2023 06:36:39 PM CEST 8 months ago
doc Sunday, August 27, 2023 04:04:27 PM CEST 8 months ago
js Sunday, August 27, 2023 03:17:42 PM CEST 8 months ago
tools Sun 27 Aug 2023 06:43:50 PM CEST 8 months ago
README.md Update README.md 8 months ago
README.md

jam

Languages: JavaScript, HTML, C

JavaScript Agent Machine

The JavaScript Agent Machine (JAM) is a multi-agent system framework. The agent processing platform satisfies the following constraints from a software design perspective:

  1. Portability with low hardware and operating system dependencies;
  2. Possibility for deployment on low-resource platforms;
  3. Embeddable in any software application, including Web pages;
  4. Scalability with respect to agent number and computational agent complexity.

JAM is entirely programmed in JavaScript and satisfies the first and third constraints immediately. The satisfaction of the second and fourth constraints depends on the underlying JavaScript Virtual Machine (JVM). But even the node.js JVM can be deployed on embedded and mobile systems like smartphones (typically requiring at least 1000 MIPS CPU power and 50 MB main memory for satisfying responsiveness and scalability).

The Software

  1. The JAM shell jamsh can be used from the command line (requiring node.js, jx, or pl3 JavScript VM)
  2. The JAM Web laboratory jamweb.html can be used in any browser
  3. The JAM library jamlib.js and jamlib.browser.js can be embedded in any node.js or Web Browser program.
  4. The Simulation Environment for JAM sejam2 is a standalone application using nw.js or can be run in a Web browser.

The software can be found in the dist folder.

Creation of Agents

Agents are either created by the platform and host application from a constructor function or by agents, either from a constructor function or as a copy (child forking by the parent agent). Constructor functions must be recompiled in a restricted context with respect to the initial privilege level of the agent. To speed-up agent creation, agent constructor functions can be precompiled (and analysed) and added to the JAM platform dictionary in advance. The compilation of agent class constructor functions is only possible by the platform discussed in the application chapter [JAM for Users and Programmers], not by the agent itself. Creation of new agents by agents requires precompiled constructor functions stored in the node dictionary.

Before an agent can be created either by the platform or by an agent itself, the constructor function must be compiled (analysed) and stored in the platform class library dictionary. Agent forking can be done without an installed agent constructor function. The fork operation creates an exact child copy of the parent agent with an optionally modified set of body variables.

Operation(s) Description
create(class,parameter,level) Create an agent from a class constructor function (or alternatively, it's a string name of an already compiled agent class) with optional parameter settings. The AIOS level argument is optional and may not be higher than the level of the parent process
fork (parameter) Make a duplicate of the calling agent with the same code and data state but (optionally) different parameter settings (direct modification of the forked agent's body variables)

In the following example one agent is created programmatically by the JAM platform (using jamsh). Firstly, the agent constructor function is defined, finally compiled, and added to the JAM platform. The agent begins in activity init and remembers its identifier in body variable master. A straight transition to the replicate activity forks a child agent with a modified set of body variables (message). The identifier of the newly created agent is stored in the parent child variable. The child agent will move to another node via the migrate activity. If this variable is set, the parent agent will sleep after the wait activity has been processed. The child agent (having still an empty child variable) will print out the message it has gotten from its parent and sleep forever. After the parent is woken up, it will kill its child agent. The kill signal is forwarded from the parent to the child node.

Examples of agent creation either by instantiating from a constructor function or by forking (cloning). The code is processed by the jamsh program.

function ag (options) {
  this.child=null;
  this.master=null;
  this.message=null;
  this.act = {
    init : () =>) {
      this.master=me(); 
    },    
    replicate : () => {
      this.child=fork({message:'I am John.'});
    },
    migrate : () => {
      var nodes = link(DIR.IP('%'));
      if (nodes.length) moveto(DIR.NODE(random(nodes)));
    },
    killing : () => {
      kill(this.child);
    },
    wait : () => {
      if (this.child) {
        sleep(1000);
      } else {
        log(this.message);
        sleep();
      }
    },
    end : () => {
      kill();
    }
  }
  
  this.trans = {
    init      : replicate,
    replicate : () => { return this.child?wait:migrate },
    migrate   : wait,
    killing   : end,
    wait      : () => { return this.child?killing:wait },
  }
  
  this.next=init;

}
// Add agent class
compile(ag,{verbose:false});
// Create level 2 agent
create('ag',{ },2);

Agent Worlds: Virtual and Physical Nodes

JAM provides a physical platform for agent processing. A physical JAM node includes the following components and capabilities:

  • Agent process scheduler;
  • Tuple space databases;
  • Protection and security;
  • Agent management and modification;
  • Communication including signal and agent mobility.

Besides the physical node, there is a virtualization layer providing virtual nodes all processed by the physical node. Each JAM instance has at least one virtual node and agents are associated with virtual nodes. The virtual node concept is used for simulation, discussed later. AMP communication ports (and links) are always attached to virtual nodes. This enables virtual networks with virtual circuit links inside a physical node world, as shown in Fig. [#worlds]. All virtual nodes have individual tuple spaces and process tables, but share the same agent process agent scheduler and AIOS. Therefore, virtual worlds do not provide speed-up by parallelization, in contrast, to JAM clusters, which can be easily created by the jamsh program. In a JAM cluster, each JAM node is processed by its own VM instance.

Agent Communication

Agents are loosely coupled processes that interact, typically by messaging. In JAM, agents use tuple spaces and signals for inter-agent communication and, in some cases, agent-platform communication. Tuple spaces provide connectionless, anonymous, non-addressed, and generative communication, whereas signals provide message-based, addressed communication between agents. Tuple space communication does not require that the agents know each other, in contrast, to signals. An agent is identified in JAM by a node-unique randomly generated identifier string (typically consisting of eight characters).

Agents can exchange data tuples if they are processed by the same physical or virtual node (local tuple space scope). Each JAM virtual node (each physical node has at least one virtual node) provides a tuple space for tuple of arity 1 up to 10 (the upper number of tuple elements can be extended by the host application). Each single tuple arity is handled independently since tuples can be only accessed by patterns that must have the same number of elements (arity). The node tuple spaces can be accessed by the host application (embedding JAM or the stand-alone JAM shell), too. The host application can provide tuple consumer handlers that are called by the tuple space API if a new tuple is inserted. Read requests can be passed to producer handler that can create new tuples on request.

Signals were originally used between agents processed on the same (virtual) node. But signals can be propagated to remote nodes, too (or other virtual nodes of a physical node), if, and only if, there is a migration trace between the sender and receiver agents. That means, two agents can exchange signals remotely if they were processed on the same node some time ago. This is always true for parent-child groups. If an agent leaves a node on migration, the current node will remember the agent id and the destination node with the outgoing network link in a temporary cache table. Each time a signal is propagated along this path, the cache table is updated. A garbage collector will clean up traces if they have not been used for some time.

Tuple spaces of remote linked nodes can be accessed by using special tuple request signals, used basically for tuple duplication or forwarding.

Agent Mobility

An agent can migrate from its current node to another (remote) node. This operation requires an established virtual link between the source and destination nodes. A node link can be tested by using the link operation. The link operation can be used to get all current links, too. In IP networks, the link(DIR.IP("%")) call returns the node identifier names, and link(DIR.IP("*")) returns the URL addresses of the currently linked nodes. The operations are summarized in the following table. Different directions specifying destination nodes used by these operations are summarized in Def. [#dirlinkapi]. Some direction flags are static values without a parameter, some are functions returning a parametrized direction. The often used DIR.IP() direction expects a pattern or URL parameter. If the destination node name is known, the DIR.NODE(name) direction function can be used to test the link or migrate to this node. The host program or level 3 agents can connect their JAM node to a remote node, typically using IP-based AMP links and the DIR.IP(proto://host:port) destination direction.

Operation(s) Description
moveto(dir) Send this agent to new destination node specified by dir.
link(dir) Test a link to a node or returns current link information.
dirDIR Link destination direction object