Tinderbox Forum

Keep link on agent's query update

Thanks for the clarification. Indeed, it’s a particular need I guess… my current workflow is trying to be like this:

  1. working in a deeply nested hierarchy of original notes – in outline view;
  2. using agents to selectively pull aliases of those notes’ aliases based on key attributes into a flat - non-hierarchical view;
  3. creating links and positioning (xPos, yPos) those aliases inside the agent, to make visual connections – in map view;

So I guess I am trying to get an agent to have both a dynamic nature (pulling new aliases or redefining its query) with a static nature (preserving the manually defined xPos, yPos and links of notes that have already been fetched by the agent).

All the background points you made make sense, I just wished a “tiny” change, in order for

“updating the agent (especially if the query changes) may cause a new alias to be created”

to become

“updating the agent (especially if the query changes) may cause a new alias to be created if an alias with the same name does not exist yet inside the agent”

(but this little change has deep repercussions on TBX logic I guess! :smiley: )

1 Like

You’d have to ask the closing question of Eastgate. I think agent only make a new one if needed, i.e. not each cycle.

If you want the agent update and not move/re-arrange aliases on the map, see $CleanupAction as that may help. The other you can do is to turn the agent off via $AgentPriority or the Action Inspector’s query tab. One the agent is ‘off’ the query isn’t run so obviously the aliases are now unaffected and the agent map functions as if it were a map of manually created aliases in a normal container.

My problem is that I may often reset the agent’s query … and that makes TBX delete and re-collect all aliases. I guess by design.

I’m trying a different approach … with this rule:


I can make a first alias inside the agent automatically create a link to a second alias inside that same agent whose $Name is defined in the $item keyattribute on the first alias.

$item is of set type. This works for one value, for eg., when $item = “1”; but I’d like it to work for all values of $item, for eg. when $item = “1;5;6” it should make connections to all aliases whose $Name is 1, 5 or 6 … but I can’t seem to make it work…

1 Like

Please ignore this last comment. I’m getting senile :slight_smile: already answered in the old forum at http://www.eastgate.com/Tinderbox/forum//YaBB.cgi?num=1467627314/0#0. I just had forgot about it! Thanks.

I found this link to a suggestion @mwra made a year ago in the former eastgate-Forum where Mark also provided a demo-file. This works very well. Thanks.

There are 2 questions inspired by this thread here and the example Mark gave in the former eastgate-forum I’d like to put forward:

  1. How would it look like to take Mark’s example one step further by implementing an if-else-formula so that when an actor is removed the agent would update appropriately?
  2. How to construct an agent that copies/extracts some original-notes (no Aliases) that are link-connected (Link Type “example”) out of a big Map that contains hundreds of other original-notes link-connected using different Link Types?

Q1. Why. I took that old demo and deleted actor3’s original from the Parts container and the agent updated the map as I’d expect. I sense you’re trying to do something different but it’s not clear quite what that is - at least to me!

Q2. Agents can’t copy original notes, only move them. Moving is done by altering the $Container of the relevant original note. Or, referring to my old demo file, were ‘Parts’ a container (whether viewed as a map or not) of 100s of items the agent could simply have a more specific query to generate only the right aliases.

Pertinent points:

  • Agents can’t create new notes.
  • Agents can move originals of matched notes’ aliases by editing the original’s $Container
  • Agents can link aliases to notes or aliases to aliases but though needs to be given to ensure the right alias(es) are targeted.
  • You can turn off auto-cleanup of agent maps (via $CleanupAction) but beyond that layout becomes a manual task. This reflects that (historically at least) spatial hypertext maps were for user exploration of relationships rather than auto-generation of network diagrams.

to Q1. I did not mean to delete actor3’s original from the Parts container but – within the “agent1” – to remove “actor3” from the the attribute “Actor” in “plan1”-Alias so that only “actor1” remains in “plan1” . Doing this, at least for me, does not remove the agree-link between “plan1” and “actor3”.

to Q2. Taking @frr149’s example here in this forum I was wondering whether one could create an agent that “extracts” only those notes linked with the link type “Uses” by showing both the aliases of the originals as well as the link (=link typ “Uses”) without the need to relink them manually as they are already originally linked.

Q1. OK, I see now. The code you need is:

linkTo(find($Actor(that).contains($Name) & $IsAlias),"agree");
$MySet = links.outbound."agree".$Path;
	if($IsAlias(X) & linkedTo(X) & $Actor.contains($Name(X))==false) {

This works for me, building off the TBX you quote above and working (FWIW, using v7.2.1).

Q1Q2 - I think this is a feature request at this point. For good reasons, related to other Tinderbox features, basic links aren’t always shared by originals and their aliases. This raise problems in your use case as it seems you want new aliases but which replicate links between other originals/aliases. I’m not arguing against the idea but simply stating I don’t believe it is possible with (simple) existing action code. Another issue is that height/width/Xpos/Ypos are intrinsic so would need to also be correctly ‘extracted’ and copied. Even than you can’t assume the auto-layout of link lines will replicate the source.

Put another way, it’s clear what you want the end point to look like but it’s less clear howe you’d do that with existing functions.

Edit: corrected reference to wrong preceding question (as noted in posts below)

to Q1 --> actually Q2: I see what you mean, @mwra. Thanks for looking at this and sharing your thorough thoughts.

(May I suggest to rename “Q1” to “Q2” to clarify things for future forum-readers.)

1 Like

to Q1. Thank you ever so much, @mwra. Works beautifully. Now it’s time for me to sit down and deconstruct and understand what code you’ve written here.

The new part is this:

$MySet = links.outbound."agree".$Path;
	if($IsAlias(X) & linkedTo(X) & $Actor.contains($Name(X))==false) {

Edit - removed unintended blank line from code sample

Why $MySet? Firstly, from vague memory, using lists generated by links() tended to need evaluating or caching into an attribute before further use. So here I past the list of output from links() into a Set-type attribute. Of course you could use a more appropriate attribute name is you want, or already use $MySet for something else. I use a Set rather than a list as this de-dupes the links() output - i.e. we don’t have to worry about possible dupes further into the process.

Why gather $Path rather than $Name? Remember we’re trying to work with aliases. For the latter, the title is not unique (being shared with its original) but the path is. Thus we have a de-duped list of paths.

Now we iterate the list using the .each() operator. The if() test is:

if the tested object X is an alias AND if the currently processed alias links to X AND _the $Name of X is NOT is a value in the current aliases $Actors

IOW, if all true we have found a link that needs to be deleted - or rather any link of type ‘agree’ needs to be deleted. This is done using unlink(). Lastly we reset $MySet to nothing so lots of loop-process data isn’t stored unnecessarily.

We can actually improve this a bit more… (new post)

We really only want to process ‘plan’ objects rather than every alias is the agent. We can’t narrow the agent query as we want aliases of the actor notes. So we wrap the agent action in a test of prototype:

	linkTo(find($Actor(that).contains($Name) & $IsAlias),"agree");
	$MySet = links.outbound."agree".$Path;
		if($IsAlias(X) & linkedTo(X,"agree") & $Actor.contains($Name(X))==false) {

The extra change is in the list ‘.each’ evaluation where we add the desired link type to the linkedTo() query term for even finer control. so not only must the conditions described in my last post be met but the link must be specifically of the link type ‘agree’. Thus if the link type were ‘disagree’ and all other conditions were the same the if condition would evaluate as false.

Why bother with link types? Bear in mind - even if not in this exact case - you may actually want other links - of the same direction and source/target for other reasons. By using a link type we know we are acting only on the links associated with this part of the process and not disturbing anything else. Indeed, if the links are there to enable you to compute values via the hypertext (links), then once tested you may chose to hide (that type of) links. I used the built-in link type ‘agree’ for rapid testing purposes; for your own project you might prefer a more descriptive name. If so, that’s fine, just don’t forget to use you type to replace ‘agree’ in the code.

I hope that helps explain a bit more of the how and why of the additional code and to help others to take the concept and adapt it to other uses.

Edit:a couple of minor typos (but not in the code!).

Great, @mwra!

And yet: Now, I need to look at this and digest what you suggest – before I can possibly reply.

Thank you very much.

Hi All, I came across this thread and I think it is exactly what I’m looking for but the old reference list is missing and so I can’t backtrack to the original sample document.

I tried using this in an agent query:
$Organization==“Bitdefender”;If($Prototype=“Organization Prototype”){linkTo(find($Organization(that).contains($Name)&IsAlias,“Product”);&MySet.each(X){if($IsAlias(X)&linkedTo(X,“Product”)&$Organization.contains($Name(X))==false){unlinkTo(X,“Product”);};};$MySet=;}

I get the expected organizations and products but they’re not linked. I would like to 1) set the links and 2) have a Service link type come in as well as products.

Does anyone of the original example I could look at? Can someone lend a hand with the above?

Finally, is there a way to lay this out so that the Organization, in this case Bitdefender, is on to top of the map view and the products are laid out underneath it and linked to it? I’d like it to show more like a stacked org. chart than an grid, column, or box.

This thread dates back to Tinderbox 7 or Tinderbox 6. Lots has happened since then. Perhaps a new thread with the new case you are trying to solve.

Also, there is (now) erroneous syntax in the code you posted. E.g.,

if($Prototype="Organization Prototype") should be
if($Prototype=="Organization Prototype")

Looks like other issues, but no time now to unravel the problems.

Also short on time here, but I’ve checked the syntax of your full example and I believe it is now correct in

if($Prototype=="Organization Prototype"){

I’m confused as to which part of this you think is making a layout. A pretty please, that in this barrage of questions you give a little more detail as to the outcome. Why not show a screen grab of what you get and one of what you expect. In this case it would help to see your layout code.

Thanks :slight_smile:

Edit: corrected = to == in one of the queries

Hey there, thanks but this does not seem to work…this is as far as I got with my original. It results in the correct notes being called but the links do not appear.


As for the layout question, it was just that, as a question. I have no idea how to approach that yet. See the image as to what I hope for someday.

Thanks, this is not urgent, was just hopeful. :slight_smile:

The code here is run on the alias of every note in the agent. linkTo() will thus link the current processed alias to the matched note. Do you want to link the alias or the alias’ original note. In which case try consider.

If you’re not sure what the code does, try writing some pseudo code stepping through what you want to happen. If the code is, as intimated, something that was just copied from an old thread likely it uses data not in your file. What is stored in the $MySet of each note? The code calls that and iterates the list.