@polyweave/bt-model (1.0.1)
Installation
@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/npm install @polyweave/bt-model@1.0.1"@polyweave/bt-model": "1.0.1"About this package
bt-model
Behavior tree model primitives for Polyweave — construct, traverse, validate, serialize, and manipulate behavior trees.
Published as @polyweave/bt-model.
Synopsis
import {
createNode, createBehaviorTree, NodeType, NodeStatus,
findNodeById, findNodesByType,
traversePreOrder, traversePostOrder, traverseBFS,
treeDepth, leafCount, nodeCount,
cloneTree, graftSubtree, replaceNode, pruneNode, insertChild,
validateTree, detectCycles, isWellFormed,
toSerializable, fromSerializable, diffTree, treeEquals
} from '@polyweave/bt-model';
const tree = createBehaviorTree({
type: 'sequence',
id: 'main',
children: [
{ type: 'condition', id: 'isReady', predicate: true },
{ type: 'action', id: 'doWork', taskDescription: 'Complete the task', timeoutMs: 5000 }
]
});
const errors = validateTree(tree);
if (errors.length === 0) {
const replacement = createNode({ type: 'selector', id: 'branch',
children: [{ type: 'action', id: 'planB' }] });
const updated = graftSubtree(tree, 'doWork', replacement);
}
Install
npm install @polyweave/bt-model
Description
Provides a full behavior tree model: node type system (13 types across composite/leaf/decorator categories), tree construction from hierarchical definitions or flat node lists, five traversal orders, structural validation, cycle detection, serialization to/from JSON, subtree grafting, pruning, insertion, and structural diffing. All operations are immutable — tree mutations return new trees.
Node type hierarchy:
- Composites (4):
sequence,selector,parallel,random— control flow nodes with multiple children - Leaves (2):
action,condition— executable nodes (conditions require a predicate) - Decorators (7):
inverter,repeater,succeeder,failer,limiter,delay,timeout— single-child modifiers
Node statuses: PENDING, RUNNING, SUCCESS, FAILURE, WAITING
API
Construction
createNode(def)
Build a node from a definition object. Recursively constructs children.
const node = createNode({
type: 'action', // required, one of NodeType values
id: 'myAction', // required, unique within tree
description: '...', // human-readable description
taskDescription: '...', // task prompt for LLM agents
successCriteria: {}, // evaluation criteria
constraints: [], // string array of constraints
toolGrants: [], // string array of granted tools
timeoutMs: 5000, // timeout in milliseconds
maxAttempts: 3, // max retry attempts
predicate: expr, // required for condition nodes
onSuccess: [], // symbolic updates on success
onFailure: [], // symbolic updates on failure
precondition: expr, // precondition expression
reads: [], // state paths read
writes: [], // state paths written
metadata: {}, // arbitrary metadata
children: [] // array of child definitions
});
createBehaviorTree(definition)
Shorthand for createNode(definition) on a root definition. Throws if definition is not an object.
buildFromFlatList(nodeDescriptors)
Build a tree from a flat array of node descriptors, each with id and optional parentId. Returns the root node. Throws if no root, multiple roots, missing parent.
NodeType, NodeStatus, NodeCategory
Enums of all valid types, statuses, and categories.
nodeTypes, leafTypes, compositeTypes, decoratorTypes
Sets of valid type strings. nodeTypes is the union of all three.
Traversal
findNodeById(root, id)
Find a node in the tree by its string id. Returns the node or null.
findNodesByType(root, type)
Return all nodes of the given type. Returns an array.
getParent(root, nodeId)
Return the parent node of the given id, or null if it is the root.
getPath(root, nodeId)
Return the path as an array of nodes from root to the target, or null if not found.
getRoot(node)
Walk up parent references to find the root of the tree.
traversePreOrder(root)
Return array of nodes in pre-order (root first, then children left to right).
traversePostOrder(root)
Return array of nodes in post-order (children first, then root).
traverseBFS(root)
Return array of nodes in breadth-first order (level by level).
treeDepth(root)
Return the maximum depth of the tree (root = depth 1).
leafCount(root)
Return the number of leaf nodes (nodes with no children).
nodeCount(root)
Return the total number of nodes in the tree.
Mutation (immutable — returns new trees)
cloneTree(root)
Deep clone a tree. The clone has no shared references with the original.
graftSubtree(root, targetId, subtree)
Replace the node at targetId with a cloned copy of subtree. Returns a new tree. Throws if target not found.
replaceNode(root, nodeId, newNode)
Alias for graftSubtree.
pruneNode(root, nodeId)
Remove the node at nodeId from the tree. Returns a new tree. Throws on root or missing node.
insertChild(root, parentId, childDef, index?)
Insert a new child node under parentId at the given index (appends if omitted). Returns a new tree.
Serialization
toSerializable(node)
Convert a tree to a plain JSON-serializable object. Drops runtime-only fields (parent, status, evidence, results).
fromSerializable(obj)
Reconstruct a tree from a serialized object. Calls createNode for proper parent/child wiring.
toFlatList(root)
Flatten a tree to an array of { id, type, description, parentId } objects.
Comparison
diffTree(a, b)
Structural comparison of two trees. Returns { added, removed, changed, unchanged } — arrays of node ids.
treeEquals(a, b)
Shallow structural equality check. Returns true if both trees have the same id/type/child structure.
Validation
validateTree(root)
Return an array of error objects { path, message }. Checks:
- Node ID presence and uniqueness
- Composite nodes have at least one child
- Leaf nodes have no children
- Decorator nodes have at most one child
- Condition nodes have a predicate
timeoutMsandmaxAttemptsare positive if set
detectCycles(root)
Return an array of detected cycles (each cycle is an array of node ids). Empty array if acyclic.
validateIds(root)
Return { duplicates, missing, uniqueIds } — lists of duplicate ids, nodes missing ids, and all unique ids.
isWellFormed(root)
Return true if the tree passes all validation and cycle checks.
Examples
Building from a flat specification
import { buildFromFlatList, validateTree } from '@polyweave/bt-model';
const spec = [
{ id: 'root', type: 'sequence' },
{ id: 'setup', type: 'action', taskDescription: 'Initialize', parentId: 'root' },
{ id: 'main', type: 'selector', parentId: 'root' },
{ id: 'planA', type: 'action', taskDescription: 'Try plan A', parentId: 'main', toolGrants: ['cli'] },
{ id: 'planB', type: 'action', taskDescription: 'Try plan B', parentId: 'main' }
];
const tree = buildFromFlatList(spec);
const errors = validateTree(tree);
console.log(errors.length === 0 ? 'Valid' : errors);
Diffing two trees
import { createBehaviorTree, diffTree } from '@polyweave/bt-model';
const before = createBehaviorTree({
type: 'sequence', id: 'root',
children: [
{ type: 'action', id: 'step1' },
{ type: 'action', id: 'step2' }
]
});
const after = createBehaviorTree({
type: 'sequence', id: 'root',
children: [
{ type: 'action', id: 'step1' },
{ type: 'action', id: 'step3' }
]
});
const diff = diffTree(before, after);
// diff.added = ['step3'], diff.removed = ['step2'], diff.unchanged = ['root', 'step1']
Tests
npm test
59 tests covering: node construction (action/composite/decorator), type validation, child/parent wiring, flat-list round-tripping, traversal orders (pre-order, post-order, BFS), tree metrics (depth, leaf count, node count), cloning, subtree grafting, replacement, pruning, insertion, serialization round-trips, structural diffing, tree equality, validation (composite children, condition predicates, decorator limits, duplicate IDs, invalid timeout/attempts), cycle detection, and well-formedness.
Requirements
Node.js 18 or later. ESM only. Zero external dependencies — uses only node: built-ins.
Caveats
- All mutation operations (
graftSubtree,pruneNode,insertChild) return NEW tree objects. The original tree is never modified. toSerializabledrops runtime-only fields (parent,status,evidence,results,attemptCount). UsefromSerializableto reconstruct with proper parent wiring.detectCyclesrequires properly wired parent references. Trees built withcreateNode/createBehaviorTree/buildFromFlatListalways have correct parent references.- This package models BT structure and provides static analysis — it does NOT execute or tick behavior trees.
See Also
@polyweave/bt-harness— behavior tree execution, tick engine, and LLM-integrated BT runner@polyweave/meta-bt-harness— outer-loop BT spec optimization
License
MIT
Dependencies
Development Dependencies
| ID | Version |
|---|---|
| @rigor/core | * |