Polyweave

@polyweave/bt-model (1.0.4)

Published 2026-07-11 04:07:18 +00:00 by Dvorak

Installation

@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/
npm install @polyweave/bt-model@1.0.4
"@polyweave/bt-model": "1.0.4"

About this package

@polyweave/bt-model

Immutable behavior tree data model with node construction, five traversal orders, structural mutation, serialization, diffing, and validation. Zero dependencies — pure data structures using only Node.js built-ins.

Synopsis

import { createNode, createBehaviorTree, graftSubtree, diffTree, validateTree, traversePreOrder, NodeType } from '@polyweave/bt-model';

const tree = createBehaviorTree({
  id: 'root',
  type: NodeType.SELECTOR,
  children: [
    createNode({ id: 'seq1', type: NodeType.SEQUENCE, children: [
      createNode({ id: 'task1', type: NodeType.TASK, name: 'codehand' }),
      createNode({ id: 'task2', type: NodeType.TASK, name: 'sleuth' })
    ]}),
    createNode({ id: 'fb1', type: NodeType.FALLBACK, children: [
      createNode({ id: 'task3', type: NodeType.TASK, name: 'retry' })
    ]})
  ]
});

const cloned = cloneTree(tree);
const grafted = graftSubtree(cloned, 'task1', createNode({ id: 'replacement', type: NodeType.TASK, name: 'new' }));

const diff = diffTree(tree, grafted);
// diff → { added: ['replacement'], removed: ['task1'], modified: ['seq1', 'root'] }

const validation = validateTree(tree);
// validation → { valid: true, errors: [] }

Install

npm install @polyweave/bt-model

Why

Behavior trees in Polyweave need a canonical data model that is immutable, serializable, validatable, and diffable. This package provides pure functions for constructing, traversing, mutating (immutably), serializing, diffing, and validating behavior trees. Zero external dependencies — the model is self-contained and portable. Used by @polyweave/bt-harness as the underlying tree representation and by @polyweave/bt-spec-parser as the compilation target.

API

Node Construction

createNode(definition)

Creates a behavior tree node. Returns an immutable node object.

Field Type Required Description
id string Yes Unique node identifier within the tree
type NodeType Yes Node type (TASK, CONDITION, SELECTOR, SEQUENCE, FALLBACK, PARALLEL, DECORATOR)
name string No Human-readable name
children Node[] No Child nodes (for composite/decorator types)
metadata object No Arbitrary metadata

createBehaviorTree(rootDef)

Creates a tree from a root node definition. Equivalent to createNode({ ...rootDef, id: rootDef.id || 'root' }).

buildFromFlatList(nodes)

Builds a tree from a flat array of node definitions with parentId references. Each node must have id, type, and optionally parentId. Nodes without parentId or referencing unknown parents become root nodes.

toFlatList(tree)

Flattens a tree into an array of nodes with parentId references. Inverse of buildFromFlatList.

Enums

Enum Values
NodeType TASK, CONDITION, SELECTOR, SEQUENCE, FALLBACK, PARALLEL, DECORATOR
NodeStatus PENDING, RUNNING, SUCCESS, FAILURE
NodeCategory LEAF, COMPOSITE, DECORATOR

nodeTypes

Set: { TASK, CONDITION, SELECTOR, SEQUENCE, FALLBACK, PARALLEL, DECORATOR }

leafTypes

Set: { TASK, CONDITION } — node types that cannot have children.

compositeTypes

Set: { SELECTOR, SEQUENCE, FALLBACK, PARALLEL } — node types that orchestrate children.

decoratorTypes

Set: { DECORATOR } — node types that wrap a single child.

Queries

findNodeById(tree, id)

Finds a node by its id. Returns the node or null.

findNodesByType(tree, type)

Returns all nodes of the given NodeType in the tree. Always returns an array.

getParent(tree, nodeId)

Returns the parent node of the node with the given ID, or null if root.

getPath(tree, nodeId)

Returns the path from root to the node as an array of node IDs.

getRoot(tree)

Returns the root node of the tree.

Traversal

All traversal functions return arrays of nodes in traversal order.

Function Order
traversePreOrder(tree) Root first, then children recursively
traversePostOrder(tree) Children first, then root
traverseBFS(tree) Level by level, left to right

Metrics

Function Returns
treeDepth(tree) Maximum depth of the tree (root = 0)
leafCount(tree) Number of leaf nodes (TASK, CONDITION)
nodeCount(tree) Total number of nodes

Immutable Mutation

All mutation functions return a new tree. The original is never modified.

cloneTree(tree)

Deep-clones the entire tree. All nodes and metadata are copied.

graftSubtree(tree, targetId, newNode)

Replaces the node at targetId with newNode. Returns a new tree or null if target not found.

replaceNode(tree, targetId, newNode)

Alias for graftSubtree.

pruneNode(tree, targetId)

Removes the node at targetId. If the node is a child, its parent's children array is updated. If the node is the root, returns null.

insertChild(tree, parentId, childNode, index?)

Inserts childNode as a child of parentId at the given index (default: end). Returns a new tree or null if parent not found or parent is a leaf type.

Serialization

toSerializable(tree)

Converts a tree to a plain JSON-serializable object. Circular references are broken. All node data is preserved.

fromSerializable(data)

Reconstructs a tree from the output of toSerializable. Validates the structure and returns a valid tree node or throws.

Diffing

diffTree(treeA, treeB)

Computes the structural difference between two trees. Returns:

{
  added: string[],      // IDs present in B but not A
  removed: string[],    // IDs present in A but not B
  modified: string[],   // IDs present in both but with different content
  unchanged: string[]    // IDs present in both with identical content
}

treeEquals(treeA, treeB)

Returns true if the two trees are structurally identical (same nodes, same hierarchy, same metadata).

Validation

validateTree(tree)

Validates tree structure. Returns { valid: boolean, errors: string[] }.

Checks performed:

  • Root node exists
  • All node IDs are unique
  • All nodes have valid NodeType values
  • Leaf nodes (TASK, CONDITION) have no children
  • Decorator nodes have exactly one child
  • No circular references in parent-child relationships

detectCycles(tree)

Returns true if the tree contains any cycle in its parent-child graph.

validateIds(tree)

Returns { valid: boolean, duplicates: string[] }. Checks for duplicate node IDs.

isWellFormed(tree)

Returns true if the tree passes all validation checks. Shorthand for validateTree(tree).valid.

Tests

npm test

59 native tests + 500 js-rigor property cases covering node construction, all node types, buildFromFlatList/toFlatList round-trip, five traversal orders with expected output ordering, all mutation operations (clone, graft, prune, insert) with idempotency invariants, depth/leaf/node count metrics, toSerializable/fromSerializable round-trip, diffTree completeness (added/removed/modified/unchanged correctness), treeEquals reflexivity/symmetry/transitivity, validateTree error enumeration, cycle detection, and ID uniqueness validation.

Requirements

Node.js 22 or later. ESM only. Zero dependencies.

Dependencies

Development Dependencies

ID Version
@rigor/core *

Keywords

behavior-tree bt polyweave
Details
npm
2026-07-11 04:07:18 +00:00
8
John Dvorak
SEE LICENSE IN LICENSE
7.8 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