Polyweave

@polyweave/bt-model (1.0.1)

Published 2026-07-10 19:11:49 +00:00 by Dvorak

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
  • timeoutMs and maxAttempts are 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.
  • toSerializable drops runtime-only fields (parent, status, evidence, results, attemptCount). Use fromSerializable to reconstruct with proper parent wiring.
  • detectCycles requires properly wired parent references. Trees built with createNode/createBehaviorTree/buildFromFlatList always 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 *

Keywords

behavior-tree bt polyweave
Details
npm
2026-07-10 19:11:49 +00:00
4
John Dvorak
SEE LICENSE IN LICENSE
8.2 KiB
Assets (1)
Versions (4) View all
1.0.5 2026-07-11
1.0.4 2026-07-11
1.0.2 2026-07-10
1.0.1 2026-07-10