Releases: joshuaisaact/petri-flow
@petriflow/gate v0.3.2
@petriflow/gate v0.3.2
Bug fixes
- autoAdvance infinite loop: tracks seen markings across iterations to detect and terminate on structural self-loops and cycles
- TOCTOU race in handleToolCall: re-checks
canFireafterawait ctx.confirm()in both single-net and composed paths, preventing concurrent callers from double-spending tokens - Meta rollback off-by-one: the rejecting validator's own meta mutations are now included in the rollback
- Non-atomic composed commit: pre-validates
canFirefor all gated verdicts before committing any - Registry config.active validation: unknown net names are silently filtered instead of causing a runtime crash
- Replay toolCallId collisions: includes entry index in generated toolCallIds
Other
- Tests colocated in
src/alongside source files - Added CHANGELOG.md
@petriflow/engine v0.2.0
@petriflow/engine v0.2.0
petri-ts 0.2.0 upgrade
- Upgraded petri-ts dependency from 0.1.x to 0.2.0
sqliteAdapter.save()supports optimistic concurrency via newexpectedVersionparameter- Re-exports new
DispatcherandDispatcherOptionstypes
Security hardening
- SSRF protection in
httpNode: blocks private IPv4 ranges (full 127.0.0.0/8, 10/8, 172.16/12, 192.168/16, 169.254/16, 100.64/10), IPv6 private/link-local/mapped addresses, and localhost fetch()usesredirect: "error"to prevent redirect-based SSRF bypassfetch()usesAbortSignal.timeout(30s)to prevent hanging requests- Default server bind changed from
0.0.0.0to127.0.0.1 - Stricter input validation on definition PUT and token injection endpoints
Bug fixes
serializeDefinitionnow derives places from transitions + initialMarking + terminalPlaces (fixes loss of zero-token places on round-trip)- Scheduler clears orphaned timeouts when an instance fails
timerNoderejects negativedelayMs- Fixed stale direct
petri-tsimports in cli and server packages
Other
- Tests colocated in
src/alongside source files - New test coverage for SSRF protection and
analysemodule
@petriflow/engine v0.3.0
What's new
Batch concurrent execution
stepBatch()fires multiple independent transitions concurrently viaPromise.all- Greedy conflict resolution — competing transitions don't double-fire
- Context patches merge in transition-name order (deterministic)
Scheduler.tick({ maxConcurrent })opts into batch mode; default behavior unchanged
Dynamic net expansion
expandWorkflow()returns a newWorkflowDefinitionwith additional transitions and places (pure, validates likedefineWorkflow)injectTokens()helper for placing tokens into new or existing placesScheduler.updateExecutor()swaps the executor at runtime; takes effect on next tick
New exports
expandWorkflow, injectTokens, BatchStepResult, WorkflowExecutor.stepBatch, Scheduler.updateExecutor
Docs
New engine documentation page at /docs/engine/
v0.3.1
@petriflow/gate, @petriflow/vercel-ai
Replay: derive gate state from message history
The AI SDK is stateless — each generateText/streamText call takes the full message history. Gate state should be derived from that same history.
@petriflow/gate:manager.replay(entries)advances markings from completed tool results. AcceptsReplayEntry[]orstring[](successful tool names). Idempotent — skips transitions that can't fire.@petriflow/vercel-ai:wrapToolsaccepts{ messages }to initialize gate state from conversation history.
const session = gate.wrapTools(myTools, { messages });
const result = await generateText({ tools: session.tools, messages });v0.3.0
@petriflow/gate, @petriflow/rules, @petriflow/vercel-ai
Breaking: constraint-stating block reasons
Block reasons now state the constraint plainly instead of exposing internal Petri net state. LLMs can interpret these and recover instead of giving up.
# Before
Tool 'deploy' blocked: [require-test-before-deploy] Tool 'deploy' not available in current state. Marking: ready:1
# After
Tool 'deploy' blocked: deploy requires a successful call to test first.
@petriflow/gate:RuleMetadatatype onSkillNet,formatBlockReason()export@petriflow/rules: compiler emitsruleMetadataon every compiled net@petriflow/vercel-ai: optionaltransformBlockReasonhook onGateOptions— an escape hatch for custom wording. Most users won't need it since gate 0.3.0 produces good messages by default- Messages by rule type:
require A before B→ "B requires a successful call to A first."block A→ "A is blocked and cannot be called."limit A to N per session→ "A has reached its limit of N calls per session."require human-approval before B→ "B requires human approval."
- Hand-built nets without metadata: "Tool 'X' is not available in the current state."
v0.2.0
@petriflow/vercel-ai
Breaking: stateless gate, session-scoped state
createPetriflowGate is now stateless. wrapTools() returns a GateSession with fresh markings, deferred tracking, and rate-limit budgets. The gate is safe to share across requests.
// Before (0.1.x)
const gate = createPetriflowGate(nets, opts);
const tools = gate.wrapTools(myTools);
generateText({ tools, system: gate.systemPrompt() });
// After (0.2.0)
const gate = createPetriflowGate(nets, opts);
const session = gate.wrapTools(myTools);
generateText({ tools: session.tools, system: session.systemPrompt() });wrapTools()returnsGateSession<T>instead ofTsystemPrompt(),formatStatus(),addNet(),removeNet()moved from gate to sessioncreatePetriflowFactoryremoved (no longer needed)- Tutorial 6: Server Integration added
- CHANGELOG.md added to package
v0.1.0 — Initial Release
Initial Release
@petriflow/engine
Core Petri net execution engine with formal verification.
- Workflow definitions with places, transitions, guards, and context
- Filtrex expression guards (compile-time validated)
- Deferred execution: transitions fire on successful tool result
- Timeout support for transitions with configurable delay and target place
WorkflowExecutorinterface withcreateExecutorfactorySchedulerwith automatic advancement, timeout handling, andinjectToken()for external events- Built-in node executors: HTTP (
httpNode) and timer (timerNode) - SQLite persistence adapter with definition store, transition history, and timeout entries
analyse()for exhaustive reachable state enumeration, deadlock detection, and invariant checking
@petriflow/gate
Tool-gating layer that sits between an agent and its tools.
SkillNettype: Petri nets with tool-gated transitions, free tools, and toolMappershandleToolCall/handleToolResultfor 4-phase gating protocol (classify → gate → allow/block → resolve)autoAdvancefires structural transitions automatically on state changeclassifyNetsfor multi-net composition: free, gated, blocked, or abstain verdictscreateGateManagerorchestrates multiple nets with shadow/enforce modesGateManager.formatStatus()andformatSystemPrompt()for observability- Dynamic net management:
addNet()/removeNet()with state preservation onDecisioncallback for logging and evaluationdefineSkillNettype-safe helper
@petriflow/rules
Declarative rules DSL and presets for tool gating. Three layers of control.
- Presets (Layer 1):
backupBeforeDelete(),observeBeforeSend(),testBeforeDeploy(),researchBeforeShare() - Declarative DSL (Layer 2):
require A before B— sequence enforcement with deferred transitionsrequire human-approval before B— manual gate per invocationblock A— provably dead transitionlimit A to N per session— finite budgetlimit A to N per action— refillable budget with spent/budget token conservation
- Dot notation:
discord.sendMessageauto-generates toolMapper for action-dispatch tools - Map statements:
map bash.command rm as deletefor pattern-based virtual tool names- Bare word patterns with automatic word-boundary matching
- Regex escape hatch with
/pattern/delimiters
- Compile-time verification:
compile()runspetri-tsanalysis on every net, returning reachable state counts - All three layers produce
SkillNetand compose viacreateGateManager
@petriflow/claude-code
Claude Code hooks adapter.
configure(projectDir)generates hooks config for.claude/settings.json- Hook entry point handles
SessionStart,PreToolUse,PostToolUseevents - Loads nets from
.claude/petriflow.config.tsproject config file - Re-exports
defineSkillNetandcreateGateManagerfor config files
@petriflow/pi-extension
pi-coding-agent extension adapter.
composeGates(nets, opts)returns a(pi: ExtensionAPI) => voidsetup functioncreatePetriGate(net, opts)convenience wrapper for single-net use- Gates
tool_callevents and resolves deferred transitions ontool_result - System prompt injection with active net status
/net-status,/add-net,/remove-netcommands for dynamic management
@petriflow/pi-assistant
Pre-built skill nets for common personal assistant patterns.
cleanupNet— backup-before-destroy with bash command toolMapper and path coverage validationcommunicateNet— observe-before-send with action-dispatch toolMapperdeployNet— test → build → stage → ship pipeline with manual production gateresearchNet— fetch-before-share with 1:1 token ratio
@petriflow/openclaw
OpenClaw plugin adapter.
createPetriGatePlugin(nets, opts)returns anOpenClawPluginDefinition- Synthetic
toolCallIdcorrelation for OpenClaw's hook protocol - System prompt injection and
/net-status,/add-net,/remove-netcommands - Bundled nets:
openclawToolApprovalNet,whatsappSafetyNet
@petriflow/cli
Command-line analysis tool.
petriflow analyse <workflow.ts>— enumerate reachable states, detect deadlocks, check invariants--dotfor Graphviz output,--jsonfor machine-readable output--strictexits with error on deadlocks or invariant violations
@petriflow/server
HTTP server for workflow management.
- Hono-based HTTP API with SSE streaming
- Workflow instance lifecycle: create, fire transitions, query state
- Definition CRUD with persistence
- Transition history and timeout management