Tinderbox v9 Icon

Self-referring agents

It can be useful for an agent's query or action to refer to the agent itself. This is especially helpful if creating generic agents where the same query/action needs to be applied many times in different contexts.

The key to reining this process is the 'agent1' designator can be used within the agent to refer to its own attributes. The 'agent' designator is understood only by agents. Note: smart adornments must use the 'adornment' designator instead of 'agent' but otherwise in the same fashion.

The designator allows things like:

Query: $Tags.contains($Name(agent))

This will match all notes where the $Tags attribute matches the $Name of the agent. For Lists/Sets the match is to a whole exact list value. For a String, the match could be to a sub-string. For an exact match in either case, but especially String attributes:

Query: $Tag == $Name(agent)

Now, if the agent's title is changed the query is automatically adjusted. The same trick can be used in the agent's action or rule. Here the $LessonNumber in a matched note is set to the value of the same attribute in the agent:

Action: $LessonNumber = $LessonNumber(agent)

The designator can be used on the left side of an action. Here the matched notes $Author2 value is added to the agent's $Authors Set attribute:

Action: $Authors(agent) = $Authors(agent) + $Author2

In actions and rule, as with the query, the 'agent' designator passes the current value of the cited attribute so changing the source attribute value will automatically affect the result of the agent. Often the need is to run the agent action only once, e.g. once per newly added note. In that case, consider altering action and/or query such that the action causes the acted on notes to no longer match the query. There are some other techniques.

If the agent queries are going to use inside(), descendedFrom() or test note $Name, then it may be better not using use $Name(agent) to see the process and instead add a user attribute to hold the customisable value. This avoids the scenario for value A seeing the agent for value B and so on, with unexpected consequences.

Indeed, the more clever the automation is made here, the more it makes sense to test the process on a copy of your date before committing it to the master unless you overlooked a flaw in your logic. Writing a badly thought-out agent can do a lot of edits very fast that can take much longer to unwind as there is no simple 'undo' for agent actions (unless the action's code logic allows for this)

Using a prototype

As well as a re-configurable single agent, the 'agent' designator also offers a useful way to make a prototype agent that can be set up quickly. Creating and using prototypes with agents is the same as with notes, though care needs to be taken not to run code in the prototype or to start altering a new agent's setting before the prototype is applied. Side note: Tinderbox actions cannot create new agents (nor notes) and there is no shortcut to create an agent: use the menus or context menus.


Query: descendedFrom($Name(agent))

To avoid the agent miscuing, it is better to avoid the apparent convenience of $Name and to use a user attribute, which is then accessed and edited via a Displayed Attribute. Thus:

Query: descendedFrom($TargetValue(agent))

In the latter, the prototype agent's $TargetValue is deliberately left blank, to ensure noting is matched and no actions occur by mistake. The prototype has $TargetValue set as a Displayed Attribute. To use the system make a new agent, apply the prototype, select the agent and in the text pane set its $TargetValue to the name of the container you wish to analyse. This way a generic agent with many attributes/actions can be pre-made and re-used many times.

For prototype smart adornments, do the same except use the 'adornment' designator in the same way as agents use the 'agent' designator.

Another approach to avoid premature execution in the prototype is first set $RuleDisabled to true. Leave the query blank. Then set this rule in the prototype:

Rule: $AgentQuery|="descendedFrom("+$Name+")";$Rule="";

This time we do not need the 'agent' designator the current note context is already the agent. It is important that $RuleDisabled is set before adding the prototype's rule as the latter must not fire except in agents using the prototype. In this example the $Rule also sets itself to nothing—if it was fully reset—it would, of course, re-inherit the prototype's code each cycle. The point of the rule re-writing itself is so the query is set just the once, just as a simple exercise in avoiding running code more than needed, if the query was set anew each cycle it would not really matter. This technique no use if the intent is to use a Displayed Attribute rather than $Name for the source value as by the time the attribute is set the query is already set. The above rule also uses a "|=" assignment so the rule runs just once to set the query. Otherwise every agent cycle the query would get re-set and never get to run as the query gets used the next cycle after being set, before which the rule would reset it.

However, the latter approach does allow for a more complex setting where the query regex must be derived from an expression using several attributes:

Rule: if(!$IsPrototype){$AgentQuery|="descendedFrom("+$TargetValue+" - " $TargetValue(Other note)+")"}

If wanting to set OnAdd code that uses a literal value of an agent's attribute, then the prototype method is needed.

If wanting to set the query/action just the once, besides using |=, you may consider using a self-cancelling code. However, you are better using a "" (empty string ) reset than resetting inheritance. Otherwise, the note re-inherits from its prototype in a never-ending loop. In the latter case the process will not run away but code will re-run each agent cycle.

As noted above, the more inventive the customisation used the more important it becomes, especially for less experienced, to test before letting the process on a real data file. Better to discover any mistakes in test!