@petriflow/gate 0.1.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ __export(index_exports, {
26
26
  createGateManager: () => createGateManager,
27
27
  createGateState: () => createGateState,
28
28
  defineSkillNet: () => defineSkillNet,
29
+ formatBlockReason: () => formatBlockReason,
29
30
  formatMarking: () => formatMarking,
30
31
  getEnabledToolTransitions: () => getEnabledToolTransitions,
31
32
  handleToolCall: () => handleToolCall,
@@ -39,6 +40,24 @@ function defineSkillNet(net) {
39
40
  return net;
40
41
  }
41
42
 
43
+ // src/format.ts
44
+ function formatBlockReason(net, resolvedTool) {
45
+ const meta = net.ruleMetadata;
46
+ if (meta) {
47
+ switch (meta.kind) {
48
+ case "sequence":
49
+ return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;
50
+ case "limit":
51
+ return meta.scope === "session" ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.` : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;
52
+ case "block":
53
+ return `${meta.tool} is blocked and cannot be called.`;
54
+ case "approval":
55
+ return `${meta.tool} requires human approval.`;
56
+ }
57
+ }
58
+ return `Tool '${resolvedTool}' is not available in the current state.`;
59
+ }
60
+
42
61
  // src/advance.ts
43
62
  var import_engine = require("@petriflow/engine");
44
63
  function isStructural(t) {
@@ -109,7 +128,7 @@ async function handleToolCall(event, ctx, net, state) {
109
128
  if (matching.length === 0) {
110
129
  return {
111
130
  block: true,
112
- reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`
131
+ reason: formatBlockReason(net, resolvedTool)
113
132
  };
114
133
  }
115
134
  const transition = matching[0];
@@ -124,14 +143,16 @@ async function handleToolCall(event, ctx, net, state) {
124
143
  }
125
144
  if (transition.type === "manual") {
126
145
  if (!ctx.hasUI) {
127
- return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };
146
+ const meta = net.ruleMetadata;
147
+ const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${resolvedTool} requires human approval.`;
148
+ return { block: true, reason };
128
149
  }
129
150
  const approved = await ctx.confirm(
130
151
  `Approve: ${transition.name}`,
131
152
  `Allow '${resolvedTool}' via transition '${transition.name}'?`
132
153
  );
133
154
  if (!approved) {
134
- return { block: true, reason: `Human rejected '${transition.name}'` };
155
+ return { block: true, reason: `${resolvedTool} was rejected by human review.` };
135
156
  }
136
157
  }
137
158
  if (transition.deferred) {
@@ -191,7 +212,7 @@ function classifyNets(nets, states, event) {
191
212
  return {
192
213
  ...base,
193
214
  kind: "blocked",
194
- reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`
215
+ reason: formatBlockReason(net, resolvedTool)
195
216
  };
196
217
  }
197
218
  return { ...base, kind: "gated", transition: matching[0] };
@@ -217,10 +238,9 @@ async function composedToolCall(getNets, getStates, event, ctx) {
217
238
  for (const v of gated) {
218
239
  if (v.transition.type === "manual") {
219
240
  if (!ctx.hasUI) {
220
- return {
221
- block: true,
222
- reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`
223
- };
241
+ const meta = v.net.ruleMetadata;
242
+ const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${v.resolvedTool} requires human approval.`;
243
+ return { block: true, reason };
224
244
  }
225
245
  const approved = await ctx.confirm(
226
246
  `Approve: ${v.transition.name} (${v.net.name})`,
@@ -229,7 +249,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
229
249
  if (!approved) {
230
250
  return {
231
251
  block: true,
232
- reason: `[${v.net.name}] Human rejected '${v.transition.name}'`
252
+ reason: `${v.resolvedTool} was rejected by human review.`
233
253
  };
234
254
  }
235
255
  }
@@ -268,6 +288,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
268
288
  }
269
289
 
270
290
  // src/manager.ts
291
+ var import_engine4 = require("@petriflow/engine");
271
292
  function createGateManager(input, opts) {
272
293
  const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);
273
294
  if (opts) {
@@ -283,6 +304,40 @@ function createGateManager(input, opts) {
283
304
  }
284
305
  return manager;
285
306
  }
307
+ function normalizeEntries(entries) {
308
+ if (entries.length === 0) return [];
309
+ if (typeof entries[0] === "string") {
310
+ return entries.map((toolName) => ({ toolName, isError: false }));
311
+ }
312
+ return entries;
313
+ }
314
+ function replayNets(nets, states, entries) {
315
+ for (const entry of entries) {
316
+ if (entry.isError) continue;
317
+ for (let i = 0; i < nets.length; i++) {
318
+ const net = nets[i];
319
+ const state = states[i];
320
+ const resolved = resolveTool(net, { toolName: entry.toolName, input: entry.input ?? {} });
321
+ if (net.freeTools.includes(resolved)) continue;
322
+ const enabled = getEnabledToolTransitions(net, state.marking);
323
+ const matching = enabled.filter((t) => t.tools.includes(resolved));
324
+ if (matching.length === 0) continue;
325
+ const transition = matching[0];
326
+ if ((0, import_engine4.canFire)(state.marking, transition)) {
327
+ state.marking = (0, import_engine4.fire)(state.marking, transition);
328
+ if (transition.deferred && net.onDeferredResult) {
329
+ net.onDeferredResult(
330
+ { toolCallId: `replay-${i}`, input: entry.input ?? {}, isError: false },
331
+ resolved,
332
+ transition,
333
+ state
334
+ );
335
+ }
336
+ state.marking = autoAdvance(net, state.marking);
337
+ }
338
+ }
339
+ }
340
+ }
286
341
  function createArrayManager(nets) {
287
342
  const states = nets.map(
288
343
  (net) => createGateState(autoAdvance(net, { ...net.initialMarking }))
@@ -298,6 +353,9 @@ function createArrayManager(nets) {
298
353
  handleToolResult(event, nets[i], states[i]);
299
354
  }
300
355
  },
356
+ replay(entries) {
357
+ replayNets(nets, states, normalizeEntries(entries));
358
+ },
301
359
  addNet() {
302
360
  return { ok: false, message: "Static composition does not support dynamic nets" };
303
361
  },
@@ -339,6 +397,11 @@ function createRegistryManager(config) {
339
397
  handleToolResult(event, net, state);
340
398
  }
341
399
  },
400
+ replay(entries) {
401
+ const activeNets = getActiveNets();
402
+ const activeStates = getActiveStates();
403
+ replayNets(activeNets, activeStates, normalizeEntries(entries));
404
+ },
342
405
  addNet(name) {
343
406
  if (!registry.has(name)) {
344
407
  return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(", ")}` };
@@ -402,6 +465,7 @@ ${sections.join("\n\n")}`;
402
465
  createGateManager,
403
466
  createGateState,
404
467
  defineSkillNet,
468
+ formatBlockReason,
405
469
  formatMarking,
406
470
  getEnabledToolTransitions,
407
471
  handleToolCall,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["// Types\nexport { defineSkillNet } from \"./types.js\";\nexport type { SkillNet, GatedTransition, ToolEvent } from \"./types.js\";\n\n// Generic event types\nexport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\n\n// Auto-advance\nexport { autoAdvance } from \"./advance.js\";\n\n// Single-net gating\nexport {\n handleToolCall,\n handleToolResult,\n formatMarking,\n getEnabledToolTransitions,\n createGateState,\n resolveTool,\n} from \"./gate.js\";\nexport type { GateState } from \"./gate.js\";\n\n// Multi-net composition\nexport { classifyNets, composedToolCall } from \"./compose.js\";\nexport type { ComposeConfig, NetVerdict } from \"./compose.js\";\n\n// Manager\nexport { createGateManager } from \"./manager.js\";\nexport type { GateManager, GateManagerOptions } from \"./manager.js\";\n","import type { Marking } from \"@petriflow/engine\";\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `Human rejected '${transition.name}'` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n formatMarking,\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return {\n block: true,\n reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`,\n };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `[${v.net.name}] Human rejected '${v.transition.name}'`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport type { GateState } from \"./gate.js\";\nimport {\n createGateState,\n formatMarking,\n getEnabledToolTransitions,\n handleToolResult as handleToolResultSingle,\n} from \"./gate.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { composedToolCall } from \"./compose.js\";\nimport type { ComposeConfig } from \"./compose.js\";\n\nexport type GateManager = {\n handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;\n handleToolResult: (event: GateToolResult) => void;\n addNet: (name: string) => { ok: boolean; message: string };\n removeNet: (name: string) => { ok: boolean; message: string };\n getActiveNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string> }>;\n getAllNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string>; active: boolean }>;\n formatStatus: () => string;\n formatSystemPrompt: () => string;\n isDynamic: boolean;\n};\n\nexport type GateManagerOptions = {\n /** \"enforce\" blocks disallowed tools. \"shadow\" logs but never blocks. */\n mode: \"enforce\" | \"shadow\";\n /** Called after every gating decision. Use for logging, metrics, debugging. */\n onDecision?: (event: GateToolCall, decision: GateDecision) => void;\n};\n\nexport function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager {\n const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);\n\n if (opts) {\n const original = manager.handleToolCall;\n manager.handleToolCall = async (event, ctx) => {\n const decision = await original.call(manager, event, ctx);\n opts.onDecision?.(event, decision);\n if (opts.mode === \"shadow\" && decision?.block) {\n return undefined;\n }\n return decision;\n };\n }\n\n return manager;\n}\n\nfunction createArrayManager(nets: SkillNet<string>[]): GateManager {\n const states = nets.map((net) =>\n createGateState(autoAdvance(net, { ...net.initialMarking })),\n );\n\n const getNets = () => nets;\n const getStates = () => states;\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getNets, getStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (let i = 0; i < nets.length; i++) {\n handleToolResultSingle(event, nets[i]!, states[i]!);\n }\n },\n\n addNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n removeNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n getActiveNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]! }));\n },\n\n getAllNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]!, active: true }));\n },\n\n formatStatus() {\n return nets\n .map((n, i) => `${n.name}: ${formatMarking(states[i]!.marking)}`)\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(nets, states);\n },\n\n isDynamic: false,\n };\n}\n\nfunction createRegistryManager(config: ComposeConfig): GateManager {\n const registry = new Map<string, { net: SkillNet<string>; state: GateState<string> }>();\n for (const [name, net] of Object.entries(config.registry)) {\n registry.set(name, {\n net,\n state: createGateState(autoAdvance(net, { ...net.initialMarking })),\n });\n }\n\n const activeNames = new Set<string>(config.active ?? Object.keys(config.registry));\n\n const getActiveNets = () => [...activeNames].map((n) => registry.get(n)!.net);\n const getActiveStates = () => [...activeNames].map((n) => registry.get(n)!.state);\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getActiveNets, getActiveStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (const { net, state } of registry.values()) {\n handleToolResultSingle(event, net, state);\n }\n },\n\n addNet(name) {\n if (!registry.has(name)) {\n return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(\", \")}` };\n }\n if (activeNames.has(name)) {\n return { ok: false, message: `'${name}' is already active` };\n }\n activeNames.add(name);\n return { ok: true, message: `Activated '${name}'` };\n },\n\n removeNet(name) {\n if (!activeNames.has(name)) {\n return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(\", \")}` };\n }\n activeNames.delete(name);\n return { ok: true, message: `Deactivated '${name}' (state preserved)` };\n },\n\n getActiveNets() {\n return [...activeNames].map((name) => {\n const entry = registry.get(name)!;\n return { name, net: entry.net, state: entry.state };\n });\n },\n\n getAllNets() {\n return [...registry.entries()].map(([name, { net, state }]) => ({\n name,\n net,\n state,\n active: activeNames.has(name),\n }));\n },\n\n formatStatus() {\n return [...registry.entries()]\n .map(([name, { state }]) => {\n const status = activeNames.has(name) ? \"active\" : \"inactive\";\n return `${name} (${status}): ${formatMarking(state.marking)}`;\n })\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(getActiveNets(), getActiveStates());\n },\n\n isDynamic: true,\n };\n}\n\nfunction formatPromptForNets(nets: SkillNet<string>[], states: GateState<string>[]): string {\n const sections = nets.map((net, i) => {\n const enabled = getEnabledToolTransitions(net, states[i]!.marking);\n const toolList = enabled.flatMap((t) => t.tools ?? []);\n return `### ${net.name}\\nAvailable gated tools: ${toolList.join(\", \") || \"none\"}\\nFree tools: ${net.freeTools.join(\", \")}\\nState: ${formatMarking(states[i]!.marking)}`;\n });\n return `## Active Petri Nets (composed)\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"mappings":"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","names":["import_engine","import_engine"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/format.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["// Types\nexport { defineSkillNet } from \"./types.js\";\nexport type { SkillNet, GatedTransition, ToolEvent, RuleMetadata } from \"./types.js\";\n\n// Block reason formatting\nexport { formatBlockReason } from \"./format.js\";\n\n// Generic event types\nexport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\n\n// Auto-advance\nexport { autoAdvance } from \"./advance.js\";\n\n// Single-net gating\nexport {\n handleToolCall,\n handleToolResult,\n formatMarking,\n getEnabledToolTransitions,\n createGateState,\n resolveTool,\n} from \"./gate.js\";\nexport type { GateState } from \"./gate.js\";\n\n// Multi-net composition\nexport { classifyNets, composedToolCall } from \"./compose.js\";\nexport type { ComposeConfig, NetVerdict } from \"./compose.js\";\n\n// Manager\nexport { createGateManager } from \"./manager.js\";\nexport type { GateManager, GateManagerOptions, ReplayEntry } from \"./manager.js\";\n","import type { Marking } from \"@petriflow/engine\";\n\n/** Structured metadata for generating user-facing block messages */\nexport type RuleMetadata =\n | { kind: \"sequence\"; prerequisite: string; dependent: string }\n | { kind: \"approval\"; tool: string }\n | { kind: \"block\"; tool: string }\n | { kind: \"limit\"; tool: string; limit: number; scope: \"session\" | string };\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n /** Structured rule metadata for generating constraint-stating block messages */\n ruleMetadata?: RuleMetadata;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import type { SkillNet } from \"./types.js\";\n\n/**\n * Generate a user-facing block reason from a net's rule metadata.\n *\n * With metadata (from the rules compiler), returns a constraint-stating message:\n * - sequence: \"deploy requires a successful call to test first.\"\n * - limit: \"deploy has reached its limit of 3 calls per session.\"\n * - block: \"rm is blocked and cannot be called.\"\n * - approval: \"deploy requires human approval.\"\n *\n * Without metadata (hand-built nets), falls back to a generic message.\n */\nexport function formatBlockReason(\n net: SkillNet<string>,\n resolvedTool: string,\n): string {\n const meta = net.ruleMetadata;\n\n if (meta) {\n switch (meta.kind) {\n case \"sequence\":\n return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;\n case \"limit\":\n return meta.scope === \"session\"\n ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.`\n : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;\n case \"block\":\n return `${meta.tool} is blocked and cannot be called.`;\n case \"approval\":\n return `${meta.tool} requires human approval.`;\n }\n }\n\n return `Tool '${resolvedTool}' is not available in the current state.`;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { formatBlockReason } from \"./format.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n const meta = net.ruleMetadata;\n const reason = meta?.kind === \"approval\"\n ? `${meta.tool} requires human approval.`\n : `${resolvedTool} requires human approval.`;\n return { block: true, reason };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `${resolvedTool} was rejected by human review.` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\nimport { formatBlockReason } from \"./format.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n const meta = v.net.ruleMetadata;\n const reason = meta?.kind === \"approval\"\n ? `${meta.tool} requires human approval.`\n : `${v.resolvedTool} requires human approval.`;\n return { block: true, reason };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `${v.resolvedTool} was rejected by human review.`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport type { GateState } from \"./gate.js\";\nimport {\n createGateState,\n formatMarking,\n getEnabledToolTransitions,\n handleToolResult as handleToolResultSingle,\n resolveTool,\n} from \"./gate.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { composedToolCall } from \"./compose.js\";\nimport type { ComposeConfig } from \"./compose.js\";\n\nexport type ReplayEntry = {\n toolName: string;\n input?: Record<string, unknown>;\n isError: boolean;\n};\n\nexport type GateManager = {\n handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;\n handleToolResult: (event: GateToolResult) => void;\n /**\n * Replay a sequence of completed tool results to advance net state.\n * Skips failed results (isError: true). Idempotent — if a transition\n * can't fire (already advanced past it), it is skipped silently.\n * Accepts an array of ReplayEntry objects or plain tool name strings\n * (treated as successful calls).\n */\n replay: (entries: ReplayEntry[] | string[]) => void;\n addNet: (name: string) => { ok: boolean; message: string };\n removeNet: (name: string) => { ok: boolean; message: string };\n getActiveNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string> }>;\n getAllNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string>; active: boolean }>;\n formatStatus: () => string;\n formatSystemPrompt: () => string;\n isDynamic: boolean;\n};\n\nexport type GateManagerOptions = {\n /** \"enforce\" blocks disallowed tools. \"shadow\" logs but never blocks. */\n mode: \"enforce\" | \"shadow\";\n /** Called after every gating decision. Use for logging, metrics, debugging. */\n onDecision?: (event: GateToolCall, decision: GateDecision) => void;\n};\n\nexport function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager {\n const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);\n\n if (opts) {\n const original = manager.handleToolCall;\n manager.handleToolCall = async (event, ctx) => {\n const decision = await original.call(manager, event, ctx);\n opts.onDecision?.(event, decision);\n if (opts.mode === \"shadow\" && decision?.block) {\n return undefined;\n }\n return decision;\n };\n }\n\n return manager;\n}\n\nfunction normalizeEntries(entries: ReplayEntry[] | string[]): ReplayEntry[] {\n if (entries.length === 0) return [];\n if (typeof entries[0] === \"string\") {\n return (entries as string[]).map((toolName) => ({ toolName, isError: false }));\n }\n return entries as ReplayEntry[];\n}\n\nfunction replayNets(\n nets: SkillNet<string>[],\n states: GateState<string>[],\n entries: ReplayEntry[],\n): void {\n for (const entry of entries) {\n if (entry.isError) continue;\n for (let i = 0; i < nets.length; i++) {\n const net = nets[i]!;\n const state = states[i]!;\n const resolved = resolveTool(net, { toolName: entry.toolName, input: entry.input ?? {} });\n\n if (net.freeTools.includes(resolved)) continue;\n\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolved));\n if (matching.length === 0) continue;\n\n const transition = matching[0]!;\n if (canFire(state.marking, transition)) {\n state.marking = fire(state.marking, transition);\n\n if (transition.deferred && net.onDeferredResult) {\n net.onDeferredResult(\n { toolCallId: `replay-${i}`, input: entry.input ?? {}, isError: false },\n resolved,\n transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n }\n }\n}\n\nfunction createArrayManager(nets: SkillNet<string>[]): GateManager {\n const states = nets.map((net) =>\n createGateState(autoAdvance(net, { ...net.initialMarking })),\n );\n\n const getNets = () => nets;\n const getStates = () => states;\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getNets, getStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (let i = 0; i < nets.length; i++) {\n handleToolResultSingle(event, nets[i]!, states[i]!);\n }\n },\n\n replay(entries) {\n replayNets(nets, states, normalizeEntries(entries));\n },\n\n addNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n removeNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n getActiveNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]! }));\n },\n\n getAllNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]!, active: true }));\n },\n\n formatStatus() {\n return nets\n .map((n, i) => `${n.name}: ${formatMarking(states[i]!.marking)}`)\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(nets, states);\n },\n\n isDynamic: false,\n };\n}\n\nfunction createRegistryManager(config: ComposeConfig): GateManager {\n const registry = new Map<string, { net: SkillNet<string>; state: GateState<string> }>();\n for (const [name, net] of Object.entries(config.registry)) {\n registry.set(name, {\n net,\n state: createGateState(autoAdvance(net, { ...net.initialMarking })),\n });\n }\n\n const activeNames = new Set<string>(config.active ?? Object.keys(config.registry));\n\n const getActiveNets = () => [...activeNames].map((n) => registry.get(n)!.net);\n const getActiveStates = () => [...activeNames].map((n) => registry.get(n)!.state);\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getActiveNets, getActiveStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (const { net, state } of registry.values()) {\n handleToolResultSingle(event, net, state);\n }\n },\n\n replay(entries) {\n const activeNets = getActiveNets();\n const activeStates = getActiveStates();\n replayNets(activeNets, activeStates, normalizeEntries(entries));\n },\n\n addNet(name) {\n if (!registry.has(name)) {\n return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(\", \")}` };\n }\n if (activeNames.has(name)) {\n return { ok: false, message: `'${name}' is already active` };\n }\n activeNames.add(name);\n return { ok: true, message: `Activated '${name}'` };\n },\n\n removeNet(name) {\n if (!activeNames.has(name)) {\n return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(\", \")}` };\n }\n activeNames.delete(name);\n return { ok: true, message: `Deactivated '${name}' (state preserved)` };\n },\n\n getActiveNets() {\n return [...activeNames].map((name) => {\n const entry = registry.get(name)!;\n return { name, net: entry.net, state: entry.state };\n });\n },\n\n getAllNets() {\n return [...registry.entries()].map(([name, { net, state }]) => ({\n name,\n net,\n state,\n active: activeNames.has(name),\n }));\n },\n\n formatStatus() {\n return [...registry.entries()]\n .map(([name, { state }]) => {\n const status = activeNames.has(name) ? \"active\" : \"inactive\";\n return `${name} (${status}): ${formatMarking(state.marking)}`;\n })\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(getActiveNets(), getActiveStates());\n },\n\n isDynamic: true,\n };\n}\n\nfunction formatPromptForNets(nets: SkillNet<string>[], states: GateState<string>[]): string {\n const sections = nets.map((net, i) => {\n const enabled = getEnabledToolTransitions(net, states[i]!.marking);\n const toolList = enabled.flatMap((t) => t.tools ?? []);\n return `### ${net.name}\\nAvailable gated tools: ${toolList.join(\", \") || \"none\"}\\nFree tools: ${net.freeTools.join(\", \")}\\nState: ${formatMarking(states[i]!.marking)}`;\n });\n return `## Active Petri Nets (composed)\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4EO,SAAS,eACd,KACiB;AACjB,SAAO;AACT;;;ACnEO,SAAS,kBACd,KACA,cACQ;AACR,QAAM,OAAO,IAAI;AAEjB,MAAI,MAAM;AACR,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,eAAO,GAAG,KAAK,SAAS,kCAAkC,KAAK,YAAY;AAAA,MAC7E,KAAK;AACH,eAAO,KAAK,UAAU,YAClB,GAAG,KAAK,IAAI,6BAA6B,KAAK,KAAK,wBACnD,GAAG,KAAK,IAAI,6BAA6B,KAAK,KAAK,cAAc,KAAK,KAAK;AAAA,MACjF,KAAK;AACH,eAAO,GAAG,KAAK,IAAI;AAAA,MACrB,KAAK;AACH,eAAO,GAAG,KAAK,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,SAAO,SAAS,YAAY;AAC9B;;;ACnCA,oBAA8B;AAK9B,SAAS,aAA+B,GAAgC;AACtE,SAAO,EAAE,SAAS,WAAW,EAAE,UAAU,UAAa,EAAE,MAAM,WAAW;AAC3E;AAOA,SAAS,iBACP,GACA,GACA,SACS;AACT,aAAW,SAAS,EAAE,QAAQ;AAC5B,QAAI,EAAE,OAAO,SAAS,KAAK,GAAG;AAE5B,YAAM,SAAS,EAAE,OAAO,OAAO,CAAC,MAAM,MAAM,KAAK,EAAE;AACnD,YAAM,SAAS,EAAE,OAAO,OAAO,CAAC,MAAM,MAAM,KAAK,EAAE;AACnD,WAAK,QAAQ,KAAK,KAAK,KAAK,SAAS,OAAQ,QAAO;AAAA,IACtD;AAAA,EACF;AACA,SAAO;AACT;AASO,SAAS,YACd,KACA,SACY;AACZ,MAAI,UAAU,EAAE,GAAG,QAAQ;AAE3B,aAAS;AACP,UAAM,aAAa,IAAI,YAAY;AAAA,MACjC,CAAC,MAAM,aAAa,CAAC,SAAK,uBAAQ,SAAS,CAAC;AAAA,IAC9C;AACA,QAAI,WAAW,WAAW,EAAG;AAG7B,UAAM,cAAc,WAAW;AAAA,MAAO,CAAC,MACrC,WAAW,MAAM,CAAC,UAAU,UAAU,KAAK,CAAC,iBAAiB,GAAG,OAAO,OAAO,CAAC;AAAA,IACjF;AACA,QAAI,YAAY,WAAW,EAAG;AAE9B,eAAW,KAAK,aAAa;AAE3B,cAAI,uBAAQ,SAAS,CAAC,GAAG;AACvB,sBAAU,oBAAK,SAAS,CAAC;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AChEA,IAAAA,iBAA8B;AAQvB,SAAS,YACd,KACA,OACQ;AACR,MAAI,IAAI,YAAY;AAClB,WAAO,IAAI,WAAW;AAAA,MACpB,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,IACf,CAAC;AAAA,EACH;AACA,SAAO,MAAM;AACf;AAGA,SAAS,uBACP,KACA,SACsB;AACtB,SAAO,IAAI,YAAY;AAAA,IACrB,CAAC,MAAM,EAAE,UAAU,UAAa,EAAE,MAAM,SAAS,SAAK,wBAAQ,SAAS,CAAC;AAAA,EAC1E;AACF;AAGO,SAAS,0BACd,KACA,SACsB;AACtB,SAAO,uBAAuB,KAAK,OAAO;AAC5C;AAGO,SAAS,cAAgC,SAA6B;AAC3E,SAAO,OAAO,QAAQ,OAAO,EAC1B,OAAO,CAAC,CAAC,EAAE,CAAC,MAAO,IAAe,CAAC,EACnC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI;AACd;AAiBO,SAAS,gBAAkC,SAAmC;AACnF,SAAO,EAAE,SAAS,MAAM,CAAC,GAAG,SAAS,oBAAI,IAAI,EAAE;AACjD;AAOA,eAAsB,eACpB,OACA,KACA,KACA,OACuB;AACvB,QAAM,eAAe,YAAY,KAAK,KAAK;AAG3C,MAAI,IAAI,UAAU,SAAS,YAAY,GAAG;AACxC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,uBAAuB,KAAK,MAAM,OAAO;AACzD,QAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,MAAO,SAAS,YAAY,CAAC;AAEtE,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,kBAAkB,KAAyB,YAAY;AAAA,IACjE;AAAA,EACF;AAEA,QAAM,aAAa,SAAS,CAAC;AAG7B,MAAI,IAAI,kBAAkB;AACxB,UAAM,YAAY,IAAI;AAAA,MACpB,EAAE,UAAU,MAAM,UAAU,OAAO,MAAM,MAAM;AAAA,MAC/C;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,UAAW,QAAO;AAAA,EACxB;AAEA,MAAI,WAAW,SAAS,UAAU;AAChC,QAAI,CAAC,IAAI,OAAO;AACd,YAAM,OAAO,IAAI;AACjB,YAAM,SAAS,MAAM,SAAS,aAC1B,GAAG,KAAK,IAAI,8BACZ,GAAG,YAAY;AACnB,aAAO,EAAE,OAAO,MAAM,OAAO;AAAA,IAC/B;AACA,UAAM,WAAW,MAAM,IAAI;AAAA,MACzB,YAAY,WAAW,IAAI;AAAA,MAC3B,UAAU,YAAY,qBAAqB,WAAW,IAAI;AAAA,IAC5D;AACA,QAAI,CAAC,UAAU;AACb,aAAO,EAAE,OAAO,MAAM,QAAQ,GAAG,YAAY,iCAAiC;AAAA,IAChF;AAAA,EACF;AAEA,MAAI,WAAW,UAAU;AAEvB,UAAM,QAAQ,IAAI,MAAM,YAAY,EAAE,YAAY,MAAM,YAAY,YAAY,aAAa,CAAC;AAC9F,WAAO;AAAA,EACT;AAGA,QAAM,cAAU,qBAAK,MAAM,SAAS,UAAU;AAC9C,QAAM,UAAU,YAAY,KAAK,MAAM,OAAO;AAE9C,SAAO;AACT;AAMO,SAAS,iBACd,OACA,KACA,OACM;AACN,QAAM,UAAU,MAAM,QAAQ,IAAI,MAAM,UAAU;AAClD,MAAI,CAAC,QAAS;AAEd,QAAM,QAAQ,OAAO,MAAM,UAAU;AAErC,MAAI,MAAM,SAAS;AAEjB;AAAA,EACF;AAGA,UAAI,wBAAQ,MAAM,SAAS,QAAQ,UAAU,GAAG;AAC9C,UAAM,cAAU,qBAAK,MAAM,SAAS,QAAQ,UAAU;AAGtD,QAAI,IAAI,kBAAkB;AACxB,UAAI;AAAA,QACF;AAAA,UACE,YAAY,MAAM;AAAA,UAClB,OAAO,MAAM;AAAA,UACb,SAAS,MAAM;AAAA,QACjB;AAAA,QACA,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAAU,YAAY,KAAK,MAAM,OAAO;AAAA,EAChD;AACF;;;AChLA,IAAAC,iBAAqB;AAiCrB,SAAS,gBACP,KACA,cACS;AACT,SAAO,IAAI,YAAY;AAAA,IACrB,CAAC,MAAM,EAAE,UAAU,UAAa,EAAE,MAAM,SAAS,YAAY;AAAA,EAC/D;AACF;AAMO,SAAS,aACd,MACA,QACA,OACiB;AACjB,SAAO,KAAK,IAAI,CAAC,KAAK,MAAM;AAC1B,UAAM,QAAQ,OAAO,CAAC;AACtB,UAAM,eAAe,YAAY,KAAK,KAAK;AAC3C,UAAM,OAAO,EAAE,KAAK,OAAO,aAAa;AAGxC,QAAI,IAAI,UAAU,SAAS,YAAY,GAAG;AACxC,aAAO,EAAE,GAAG,MAAM,MAAM,OAAgB;AAAA,IAC1C;AAGA,QAAI,CAAC,gBAAgB,KAAK,YAAY,GAAG;AACvC,aAAO,EAAE,GAAG,MAAM,MAAM,UAAmB;AAAA,IAC7C;AAGA,UAAM,UAAU,0BAA0B,KAAK,MAAM,OAAO;AAC5D,UAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,MAAO,SAAS,YAAY,CAAC;AAEtE,QAAI,SAAS,WAAW,GAAG;AACzB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,QAAQ,kBAAkB,KAAyB,YAAY;AAAA,MACjE;AAAA,IACF;AAEA,WAAO,EAAE,GAAG,MAAM,MAAM,SAAkB,YAAY,SAAS,CAAC,EAAG;AAAA,EACrE,CAAC;AACH;AAKA,eAAsB,iBACpB,SACA,WACA,OACA,KACuB;AACvB,QAAM,OAAO,QAAQ;AACrB,QAAM,SAAS,UAAU;AAGzB,QAAM,WAAW,aAAa,MAAM,QAAQ;AAAA,IAC1C,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,EACf,CAAC;AAGD,QAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS;AACzD,MAAI,SAAS;AACX,WAAO,EAAE,OAAO,MAAM,QAAQ,QAAQ,OAAO;AAAA,EAC/C;AAEA,QAAM,QAAQ,SAAS;AAAA,IACrB,CAAC,MAA2D,EAAE,SAAS;AAAA,EACzE;AAGA,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;AAAA,EACT;AAGA,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,WAAW,SAAS,UAAU;AAClC,UAAI,CAAC,IAAI,OAAO;AACd,cAAM,OAAO,EAAE,IAAI;AACnB,cAAM,SAAS,MAAM,SAAS,aAC1B,GAAG,KAAK,IAAI,8BACZ,GAAG,EAAE,YAAY;AACrB,eAAO,EAAE,OAAO,MAAM,OAAO;AAAA,MAC/B;AACA,YAAM,WAAW,MAAM,IAAI;AAAA,QACzB,YAAY,EAAE,WAAW,IAAI,KAAK,EAAE,IAAI,IAAI;AAAA,QAC5C,UAAU,EAAE,YAAY,qBAAqB,EAAE,WAAW,IAAI,aAAa,EAAE,IAAI,IAAI;AAAA,MACvF;AACA,UAAI,CAAC,UAAU;AACb,eAAO;AAAA,UACL,OAAO;AAAA,UACP,QAAQ,GAAG,EAAE,YAAY;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIA,QAAM,gBAAgB,MAAM,IAAI,CAAC,MAAM,gBAAgB,EAAE,MAAM,IAAI,CAAC;AAEpE,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,IAAI,MAAM,CAAC;AACjB,QAAI,EAAE,IAAI,kBAAkB;AAC1B,YAAM,YAAY,EAAE,IAAI;AAAA,QACtB,EAAE,UAAU,MAAM,UAAU,OAAO,MAAM,MAAM;AAAA,QAC/C,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE;AAAA,MACJ;AACA,UAAI,WAAW;AAEb,iBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,gBAAM,CAAC,EAAG,MAAM,OAAO,cAAc,CAAC;AAAA,QACxC;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,aAAW,KAAK,OAAO;AACrB,QAAI,EAAE,WAAW,UAAU;AACzB,QAAE,MAAM,QAAQ,IAAI,MAAM,YAAY;AAAA,QACpC,YAAY,MAAM;AAAA,QAClB,YAAY,EAAE;AAAA,QACd,cAAc,EAAE;AAAA,MAClB,CAAC;AAAA,IACH,OAAO;AACL,QAAE,MAAM,cAAU,qBAAK,EAAE,MAAM,SAAS,EAAE,UAAU;AACpD,QAAE,MAAM,UAAU,YAAY,EAAE,KAAK,EAAE,MAAM,OAAO;AAAA,IACtD;AAAA,EACF;AAEA,SAAO;AACT;;;AChLA,IAAAC,iBAA8B;AAgDvB,SAAS,kBAAkB,OAA2C,MAAwC;AACnH,QAAM,UAAU,MAAM,QAAQ,KAAK,IAAI,mBAAmB,KAAK,IAAI,sBAAsB,KAAK;AAE9F,MAAI,MAAM;AACR,UAAM,WAAW,QAAQ;AACzB,YAAQ,iBAAiB,OAAO,OAAO,QAAQ;AAC7C,YAAM,WAAW,MAAM,SAAS,KAAK,SAAS,OAAO,GAAG;AACxD,WAAK,aAAa,OAAO,QAAQ;AACjC,UAAI,KAAK,SAAS,YAAY,UAAU,OAAO;AAC7C,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,SAAkD;AAC1E,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAClC,MAAI,OAAO,QAAQ,CAAC,MAAM,UAAU;AAClC,WAAQ,QAAqB,IAAI,CAAC,cAAc,EAAE,UAAU,SAAS,MAAM,EAAE;AAAA,EAC/E;AACA,SAAO;AACT;AAEA,SAAS,WACP,MACA,QACA,SACM;AACN,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,QAAS;AACnB,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,MAAM,KAAK,CAAC;AAClB,YAAM,QAAQ,OAAO,CAAC;AACtB,YAAM,WAAW,YAAY,KAAK,EAAE,UAAU,MAAM,UAAU,OAAO,MAAM,SAAS,CAAC,EAAE,CAAC;AAExF,UAAI,IAAI,UAAU,SAAS,QAAQ,EAAG;AAEtC,YAAM,UAAU,0BAA0B,KAAK,MAAM,OAAO;AAC5D,YAAM,WAAW,QAAQ,OAAO,CAAC,MAAM,EAAE,MAAO,SAAS,QAAQ,CAAC;AAClE,UAAI,SAAS,WAAW,EAAG;AAE3B,YAAM,aAAa,SAAS,CAAC;AAC7B,cAAI,wBAAQ,MAAM,SAAS,UAAU,GAAG;AACtC,cAAM,cAAU,qBAAK,MAAM,SAAS,UAAU;AAE9C,YAAI,WAAW,YAAY,IAAI,kBAAkB;AAC/C,cAAI;AAAA,YACF,EAAE,YAAY,UAAU,CAAC,IAAI,OAAO,MAAM,SAAS,CAAC,GAAG,SAAS,MAAM;AAAA,YACtE;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAEA,cAAM,UAAU,YAAY,KAAK,MAAM,OAAO;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,MAAuC;AACjE,QAAM,SAAS,KAAK;AAAA,IAAI,CAAC,QACvB,gBAAgB,YAAY,KAAK,EAAE,GAAG,IAAI,eAAe,CAAC,CAAC;AAAA,EAC7D;AAEA,QAAM,UAAU,MAAM;AACtB,QAAM,YAAY,MAAM;AAExB,SAAO;AAAA,IACL,eAAe,OAAO,KAAK;AACzB,aAAO,iBAAiB,SAAS,WAAW,OAAO,GAAG;AAAA,IACxD;AAAA,IAEA,iBAAiB,OAAO;AACtB,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,yBAAuB,OAAO,KAAK,CAAC,GAAI,OAAO,CAAC,CAAE;AAAA,MACpD;AAAA,IACF;AAAA,IAEA,OAAO,SAAS;AACd,iBAAW,MAAM,QAAQ,iBAAiB,OAAO,CAAC;AAAA,IACpD;AAAA,IAEA,SAAS;AACP,aAAO,EAAE,IAAI,OAAO,SAAS,mDAAmD;AAAA,IAClF;AAAA,IAEA,YAAY;AACV,aAAO,EAAE,IAAI,OAAO,SAAS,mDAAmD;AAAA,IAClF;AAAA,IAEA,gBAAgB;AACd,aAAO,KAAK,IAAI,CAAC,KAAK,OAAO,EAAE,MAAM,IAAI,MAAM,KAAK,OAAO,OAAO,CAAC,EAAG,EAAE;AAAA,IAC1E;AAAA,IAEA,aAAa;AACX,aAAO,KAAK,IAAI,CAAC,KAAK,OAAO,EAAE,MAAM,IAAI,MAAM,KAAK,OAAO,OAAO,CAAC,GAAI,QAAQ,KAAK,EAAE;AAAA,IACxF;AAAA,IAEA,eAAe;AACb,aAAO,KACJ,IAAI,CAAC,GAAG,MAAM,GAAG,EAAE,IAAI,KAAK,cAAc,OAAO,CAAC,EAAG,OAAO,CAAC,EAAE,EAC/D,KAAK,IAAI;AAAA,IACd;AAAA,IAEA,qBAAqB;AACnB,aAAO,oBAAoB,MAAM,MAAM;AAAA,IACzC;AAAA,IAEA,WAAW;AAAA,EACb;AACF;AAEA,SAAS,sBAAsB,QAAoC;AACjE,QAAM,WAAW,oBAAI,IAAiE;AACtF,aAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AACzD,aAAS,IAAI,MAAM;AAAA,MACjB;AAAA,MACA,OAAO,gBAAgB,YAAY,KAAK,EAAE,GAAG,IAAI,eAAe,CAAC,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AAEA,QAAM,cAAc,IAAI,IAAY,OAAO,UAAU,OAAO,KAAK,OAAO,QAAQ,CAAC;AAEjF,QAAM,gBAAgB,MAAM,CAAC,GAAG,WAAW,EAAE,IAAI,CAAC,MAAM,SAAS,IAAI,CAAC,EAAG,GAAG;AAC5E,QAAM,kBAAkB,MAAM,CAAC,GAAG,WAAW,EAAE,IAAI,CAAC,MAAM,SAAS,IAAI,CAAC,EAAG,KAAK;AAEhF,SAAO;AAAA,IACL,eAAe,OAAO,KAAK;AACzB,aAAO,iBAAiB,eAAe,iBAAiB,OAAO,GAAG;AAAA,IACpE;AAAA,IAEA,iBAAiB,OAAO;AACtB,iBAAW,EAAE,KAAK,MAAM,KAAK,SAAS,OAAO,GAAG;AAC9C,yBAAuB,OAAO,KAAK,KAAK;AAAA,MAC1C;AAAA,IACF;AAAA,IAEA,OAAO,SAAS;AACd,YAAM,aAAa,cAAc;AACjC,YAAM,eAAe,gBAAgB;AACrC,iBAAW,YAAY,cAAc,iBAAiB,OAAO,CAAC;AAAA,IAChE;AAAA,IAEA,OAAO,MAAM;AACX,UAAI,CAAC,SAAS,IAAI,IAAI,GAAG;AACvB,eAAO,EAAE,IAAI,OAAO,SAAS,gBAAgB,IAAI,iBAAiB,CAAC,GAAG,SAAS,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;AAAA,MACtG;AACA,UAAI,YAAY,IAAI,IAAI,GAAG;AACzB,eAAO,EAAE,IAAI,OAAO,SAAS,IAAI,IAAI,sBAAsB;AAAA,MAC7D;AACA,kBAAY,IAAI,IAAI;AACpB,aAAO,EAAE,IAAI,MAAM,SAAS,cAAc,IAAI,IAAI;AAAA,IACpD;AAAA,IAEA,UAAU,MAAM;AACd,UAAI,CAAC,YAAY,IAAI,IAAI,GAAG;AAC1B,eAAO,EAAE,IAAI,OAAO,SAAS,IAAI,IAAI,4BAA4B,CAAC,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,GAAG;AAAA,MACjG;AACA,kBAAY,OAAO,IAAI;AACvB,aAAO,EAAE,IAAI,MAAM,SAAS,gBAAgB,IAAI,sBAAsB;AAAA,IACxE;AAAA,IAEA,gBAAgB;AACd,aAAO,CAAC,GAAG,WAAW,EAAE,IAAI,CAAC,SAAS;AACpC,cAAM,QAAQ,SAAS,IAAI,IAAI;AAC/B,eAAO,EAAE,MAAM,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM;AAAA,MACpD,CAAC;AAAA,IACH;AAAA,IAEA,aAAa;AACX,aAAO,CAAC,GAAG,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC,OAAO;AAAA,QAC9D;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,YAAY,IAAI,IAAI;AAAA,MAC9B,EAAE;AAAA,IACJ;AAAA,IAEA,eAAe;AACb,aAAO,CAAC,GAAG,SAAS,QAAQ,CAAC,EAC1B,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM;AAC1B,cAAM,SAAS,YAAY,IAAI,IAAI,IAAI,WAAW;AAClD,eAAO,GAAG,IAAI,KAAK,MAAM,MAAM,cAAc,MAAM,OAAO,CAAC;AAAA,MAC7D,CAAC,EACA,KAAK,IAAI;AAAA,IACd;AAAA,IAEA,qBAAqB;AACnB,aAAO,oBAAoB,cAAc,GAAG,gBAAgB,CAAC;AAAA,IAC/D;AAAA,IAEA,WAAW;AAAA,EACb;AACF;AAEA,SAAS,oBAAoB,MAA0B,QAAqC;AAC1F,QAAM,WAAW,KAAK,IAAI,CAAC,KAAK,MAAM;AACpC,UAAM,UAAU,0BAA0B,KAAK,OAAO,CAAC,EAAG,OAAO;AACjE,UAAM,WAAW,QAAQ,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACrD,WAAO,OAAO,IAAI,IAAI;AAAA,yBAA4B,SAAS,KAAK,IAAI,KAAK,MAAM;AAAA,cAAiB,IAAI,UAAU,KAAK,IAAI,CAAC;AAAA,SAAY,cAAc,OAAO,CAAC,EAAG,OAAO,CAAC;AAAA,EACvK,CAAC;AACD,SAAO;AAAA,EAAoC,SAAS,KAAK,MAAM,CAAC;AAClE;","names":["import_engine","import_engine","import_engine"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { Marking } from '@petriflow/engine';
2
2
 
3
+ /** Structured metadata for generating user-facing block messages */
4
+ type RuleMetadata = {
5
+ kind: "sequence";
6
+ prerequisite: string;
7
+ dependent: string;
8
+ } | {
9
+ kind: "approval";
10
+ tool: string;
11
+ } | {
12
+ kind: "block";
13
+ tool: string;
14
+ } | {
15
+ kind: "limit";
16
+ tool: string;
17
+ limit: number;
18
+ scope: "session" | string;
19
+ };
3
20
  /** A transition that optionally gates tool access */
4
21
  type GatedTransition<Place extends string> = {
5
22
  name: string;
@@ -66,10 +83,25 @@ type SkillNet<Place extends string> = {
66
83
  marking: Marking<Place>;
67
84
  meta: Record<string, unknown>;
68
85
  }): void;
86
+ /** Structured rule metadata for generating constraint-stating block messages */
87
+ ruleMetadata?: RuleMetadata;
69
88
  };
70
89
  /** Type-safe helper — validates places/marking at the type level */
71
90
  declare function defineSkillNet<Place extends string>(net: SkillNet<Place>): SkillNet<Place>;
72
91
 
92
+ /**
93
+ * Generate a user-facing block reason from a net's rule metadata.
94
+ *
95
+ * With metadata (from the rules compiler), returns a constraint-stating message:
96
+ * - sequence: "deploy requires a successful call to test first."
97
+ * - limit: "deploy has reached its limit of 3 calls per session."
98
+ * - block: "rm is blocked and cannot be called."
99
+ * - approval: "deploy requires human approval."
100
+ *
101
+ * Without metadata (hand-built nets), falls back to a generic message.
102
+ */
103
+ declare function formatBlockReason(net: SkillNet<string>, resolvedTool: string): string;
104
+
73
105
  /** Generic tool call event — framework-agnostic */
74
106
  type GateToolCall = {
75
107
  toolCallId: string;
@@ -172,9 +204,22 @@ declare function classifyNets<P extends string>(nets: SkillNet<P>[], states: Gat
172
204
  */
173
205
  declare function composedToolCall(getNets: () => SkillNet<string>[], getStates: () => GateState<string>[], event: GateToolCall, ctx: GateContext): Promise<GateDecision>;
174
206
 
207
+ type ReplayEntry = {
208
+ toolName: string;
209
+ input?: Record<string, unknown>;
210
+ isError: boolean;
211
+ };
175
212
  type GateManager = {
176
213
  handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;
177
214
  handleToolResult: (event: GateToolResult) => void;
215
+ /**
216
+ * Replay a sequence of completed tool results to advance net state.
217
+ * Skips failed results (isError: true). Idempotent — if a transition
218
+ * can't fire (already advanced past it), it is skipped silently.
219
+ * Accepts an array of ReplayEntry objects or plain tool name strings
220
+ * (treated as successful calls).
221
+ */
222
+ replay: (entries: ReplayEntry[] | string[]) => void;
178
223
  addNet: (name: string) => {
179
224
  ok: boolean;
180
225
  message: string;
@@ -206,4 +251,4 @@ type GateManagerOptions = {
206
251
  };
207
252
  declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
208
253
 
209
- export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatMarking, getEnabledToolTransitions, handleToolCall, handleToolResult, resolveTool };
254
+ export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type ReplayEntry, type RuleMetadata, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatBlockReason, formatMarking, getEnabledToolTransitions, handleToolCall, handleToolResult, resolveTool };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,22 @@
1
1
  import { Marking } from '@petriflow/engine';
2
2
 
3
+ /** Structured metadata for generating user-facing block messages */
4
+ type RuleMetadata = {
5
+ kind: "sequence";
6
+ prerequisite: string;
7
+ dependent: string;
8
+ } | {
9
+ kind: "approval";
10
+ tool: string;
11
+ } | {
12
+ kind: "block";
13
+ tool: string;
14
+ } | {
15
+ kind: "limit";
16
+ tool: string;
17
+ limit: number;
18
+ scope: "session" | string;
19
+ };
3
20
  /** A transition that optionally gates tool access */
4
21
  type GatedTransition<Place extends string> = {
5
22
  name: string;
@@ -66,10 +83,25 @@ type SkillNet<Place extends string> = {
66
83
  marking: Marking<Place>;
67
84
  meta: Record<string, unknown>;
68
85
  }): void;
86
+ /** Structured rule metadata for generating constraint-stating block messages */
87
+ ruleMetadata?: RuleMetadata;
69
88
  };
70
89
  /** Type-safe helper — validates places/marking at the type level */
71
90
  declare function defineSkillNet<Place extends string>(net: SkillNet<Place>): SkillNet<Place>;
72
91
 
92
+ /**
93
+ * Generate a user-facing block reason from a net's rule metadata.
94
+ *
95
+ * With metadata (from the rules compiler), returns a constraint-stating message:
96
+ * - sequence: "deploy requires a successful call to test first."
97
+ * - limit: "deploy has reached its limit of 3 calls per session."
98
+ * - block: "rm is blocked and cannot be called."
99
+ * - approval: "deploy requires human approval."
100
+ *
101
+ * Without metadata (hand-built nets), falls back to a generic message.
102
+ */
103
+ declare function formatBlockReason(net: SkillNet<string>, resolvedTool: string): string;
104
+
73
105
  /** Generic tool call event — framework-agnostic */
74
106
  type GateToolCall = {
75
107
  toolCallId: string;
@@ -172,9 +204,22 @@ declare function classifyNets<P extends string>(nets: SkillNet<P>[], states: Gat
172
204
  */
173
205
  declare function composedToolCall(getNets: () => SkillNet<string>[], getStates: () => GateState<string>[], event: GateToolCall, ctx: GateContext): Promise<GateDecision>;
174
206
 
207
+ type ReplayEntry = {
208
+ toolName: string;
209
+ input?: Record<string, unknown>;
210
+ isError: boolean;
211
+ };
175
212
  type GateManager = {
176
213
  handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;
177
214
  handleToolResult: (event: GateToolResult) => void;
215
+ /**
216
+ * Replay a sequence of completed tool results to advance net state.
217
+ * Skips failed results (isError: true). Idempotent — if a transition
218
+ * can't fire (already advanced past it), it is skipped silently.
219
+ * Accepts an array of ReplayEntry objects or plain tool name strings
220
+ * (treated as successful calls).
221
+ */
222
+ replay: (entries: ReplayEntry[] | string[]) => void;
178
223
  addNet: (name: string) => {
179
224
  ok: boolean;
180
225
  message: string;
@@ -206,4 +251,4 @@ type GateManagerOptions = {
206
251
  };
207
252
  declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
208
253
 
209
- export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatMarking, getEnabledToolTransitions, handleToolCall, handleToolResult, resolveTool };
254
+ export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type ReplayEntry, type RuleMetadata, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatBlockReason, formatMarking, getEnabledToolTransitions, handleToolCall, handleToolResult, resolveTool };
package/dist/index.js CHANGED
@@ -3,6 +3,24 @@ function defineSkillNet(net) {
3
3
  return net;
4
4
  }
5
5
 
6
+ // src/format.ts
7
+ function formatBlockReason(net, resolvedTool) {
8
+ const meta = net.ruleMetadata;
9
+ if (meta) {
10
+ switch (meta.kind) {
11
+ case "sequence":
12
+ return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;
13
+ case "limit":
14
+ return meta.scope === "session" ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.` : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;
15
+ case "block":
16
+ return `${meta.tool} is blocked and cannot be called.`;
17
+ case "approval":
18
+ return `${meta.tool} requires human approval.`;
19
+ }
20
+ }
21
+ return `Tool '${resolvedTool}' is not available in the current state.`;
22
+ }
23
+
6
24
  // src/advance.ts
7
25
  import { canFire, fire } from "@petriflow/engine";
8
26
  function isStructural(t) {
@@ -73,7 +91,7 @@ async function handleToolCall(event, ctx, net, state) {
73
91
  if (matching.length === 0) {
74
92
  return {
75
93
  block: true,
76
- reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`
94
+ reason: formatBlockReason(net, resolvedTool)
77
95
  };
78
96
  }
79
97
  const transition = matching[0];
@@ -88,14 +106,16 @@ async function handleToolCall(event, ctx, net, state) {
88
106
  }
89
107
  if (transition.type === "manual") {
90
108
  if (!ctx.hasUI) {
91
- return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };
109
+ const meta = net.ruleMetadata;
110
+ const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${resolvedTool} requires human approval.`;
111
+ return { block: true, reason };
92
112
  }
93
113
  const approved = await ctx.confirm(
94
114
  `Approve: ${transition.name}`,
95
115
  `Allow '${resolvedTool}' via transition '${transition.name}'?`
96
116
  );
97
117
  if (!approved) {
98
- return { block: true, reason: `Human rejected '${transition.name}'` };
118
+ return { block: true, reason: `${resolvedTool} was rejected by human review.` };
99
119
  }
100
120
  }
101
121
  if (transition.deferred) {
@@ -155,7 +175,7 @@ function classifyNets(nets, states, event) {
155
175
  return {
156
176
  ...base,
157
177
  kind: "blocked",
158
- reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`
178
+ reason: formatBlockReason(net, resolvedTool)
159
179
  };
160
180
  }
161
181
  return { ...base, kind: "gated", transition: matching[0] };
@@ -181,10 +201,9 @@ async function composedToolCall(getNets, getStates, event, ctx) {
181
201
  for (const v of gated) {
182
202
  if (v.transition.type === "manual") {
183
203
  if (!ctx.hasUI) {
184
- return {
185
- block: true,
186
- reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`
187
- };
204
+ const meta = v.net.ruleMetadata;
205
+ const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${v.resolvedTool} requires human approval.`;
206
+ return { block: true, reason };
188
207
  }
189
208
  const approved = await ctx.confirm(
190
209
  `Approve: ${v.transition.name} (${v.net.name})`,
@@ -193,7 +212,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
193
212
  if (!approved) {
194
213
  return {
195
214
  block: true,
196
- reason: `[${v.net.name}] Human rejected '${v.transition.name}'`
215
+ reason: `${v.resolvedTool} was rejected by human review.`
197
216
  };
198
217
  }
199
218
  }
@@ -232,6 +251,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
232
251
  }
233
252
 
234
253
  // src/manager.ts
254
+ import { canFire as canFire3, fire as fire4 } from "@petriflow/engine";
235
255
  function createGateManager(input, opts) {
236
256
  const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);
237
257
  if (opts) {
@@ -247,6 +267,40 @@ function createGateManager(input, opts) {
247
267
  }
248
268
  return manager;
249
269
  }
270
+ function normalizeEntries(entries) {
271
+ if (entries.length === 0) return [];
272
+ if (typeof entries[0] === "string") {
273
+ return entries.map((toolName) => ({ toolName, isError: false }));
274
+ }
275
+ return entries;
276
+ }
277
+ function replayNets(nets, states, entries) {
278
+ for (const entry of entries) {
279
+ if (entry.isError) continue;
280
+ for (let i = 0; i < nets.length; i++) {
281
+ const net = nets[i];
282
+ const state = states[i];
283
+ const resolved = resolveTool(net, { toolName: entry.toolName, input: entry.input ?? {} });
284
+ if (net.freeTools.includes(resolved)) continue;
285
+ const enabled = getEnabledToolTransitions(net, state.marking);
286
+ const matching = enabled.filter((t) => t.tools.includes(resolved));
287
+ if (matching.length === 0) continue;
288
+ const transition = matching[0];
289
+ if (canFire3(state.marking, transition)) {
290
+ state.marking = fire4(state.marking, transition);
291
+ if (transition.deferred && net.onDeferredResult) {
292
+ net.onDeferredResult(
293
+ { toolCallId: `replay-${i}`, input: entry.input ?? {}, isError: false },
294
+ resolved,
295
+ transition,
296
+ state
297
+ );
298
+ }
299
+ state.marking = autoAdvance(net, state.marking);
300
+ }
301
+ }
302
+ }
303
+ }
250
304
  function createArrayManager(nets) {
251
305
  const states = nets.map(
252
306
  (net) => createGateState(autoAdvance(net, { ...net.initialMarking }))
@@ -262,6 +316,9 @@ function createArrayManager(nets) {
262
316
  handleToolResult(event, nets[i], states[i]);
263
317
  }
264
318
  },
319
+ replay(entries) {
320
+ replayNets(nets, states, normalizeEntries(entries));
321
+ },
265
322
  addNet() {
266
323
  return { ok: false, message: "Static composition does not support dynamic nets" };
267
324
  },
@@ -303,6 +360,11 @@ function createRegistryManager(config) {
303
360
  handleToolResult(event, net, state);
304
361
  }
305
362
  },
363
+ replay(entries) {
364
+ const activeNets = getActiveNets();
365
+ const activeStates = getActiveStates();
366
+ replayNets(activeNets, activeStates, normalizeEntries(entries));
367
+ },
306
368
  addNet(name) {
307
369
  if (!registry.has(name)) {
308
370
  return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(", ")}` };
@@ -365,6 +427,7 @@ export {
365
427
  createGateManager,
366
428
  createGateState,
367
429
  defineSkillNet,
430
+ formatBlockReason,
368
431
  formatMarking,
369
432
  getEnabledToolTransitions,
370
433
  handleToolCall,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["import type { Marking } from \"@petriflow/engine\";\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `Human rejected '${transition.name}'` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n formatMarking,\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return {\n block: true,\n reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`,\n };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `[${v.net.name}] Human rejected '${v.transition.name}'`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport type { GateState } from \"./gate.js\";\nimport {\n createGateState,\n formatMarking,\n getEnabledToolTransitions,\n handleToolResult as handleToolResultSingle,\n} from \"./gate.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { composedToolCall } from \"./compose.js\";\nimport type { ComposeConfig } from \"./compose.js\";\n\nexport type GateManager = {\n handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;\n handleToolResult: (event: GateToolResult) => void;\n addNet: (name: string) => { ok: boolean; message: string };\n removeNet: (name: string) => { ok: boolean; message: string };\n getActiveNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string> }>;\n getAllNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string>; active: boolean }>;\n formatStatus: () => string;\n formatSystemPrompt: () => string;\n isDynamic: boolean;\n};\n\nexport type GateManagerOptions = {\n /** \"enforce\" blocks disallowed tools. \"shadow\" logs but never blocks. */\n mode: \"enforce\" | \"shadow\";\n /** Called after every gating decision. Use for logging, metrics, debugging. */\n onDecision?: (event: GateToolCall, decision: GateDecision) => void;\n};\n\nexport function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager {\n const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);\n\n if (opts) {\n const original = manager.handleToolCall;\n manager.handleToolCall = async (event, ctx) => {\n const decision = await original.call(manager, event, ctx);\n opts.onDecision?.(event, decision);\n if (opts.mode === \"shadow\" && decision?.block) {\n return undefined;\n }\n return decision;\n };\n }\n\n return manager;\n}\n\nfunction createArrayManager(nets: SkillNet<string>[]): GateManager {\n const states = nets.map((net) =>\n createGateState(autoAdvance(net, { ...net.initialMarking })),\n );\n\n const getNets = () => nets;\n const getStates = () => states;\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getNets, getStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (let i = 0; i < nets.length; i++) {\n handleToolResultSingle(event, nets[i]!, states[i]!);\n }\n },\n\n addNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n removeNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n getActiveNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]! }));\n },\n\n getAllNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]!, active: true }));\n },\n\n formatStatus() {\n return nets\n .map((n, i) => `${n.name}: ${formatMarking(states[i]!.marking)}`)\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(nets, states);\n },\n\n isDynamic: false,\n };\n}\n\nfunction createRegistryManager(config: ComposeConfig): GateManager {\n const registry = new Map<string, { net: SkillNet<string>; state: GateState<string> }>();\n for (const [name, net] of Object.entries(config.registry)) {\n registry.set(name, {\n net,\n state: createGateState(autoAdvance(net, { ...net.initialMarking })),\n });\n }\n\n const activeNames = new Set<string>(config.active ?? Object.keys(config.registry));\n\n const getActiveNets = () => [...activeNames].map((n) => registry.get(n)!.net);\n const getActiveStates = () => [...activeNames].map((n) => registry.get(n)!.state);\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getActiveNets, getActiveStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (const { net, state } of registry.values()) {\n handleToolResultSingle(event, net, state);\n }\n },\n\n addNet(name) {\n if (!registry.has(name)) {\n return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(\", \")}` };\n }\n if (activeNames.has(name)) {\n return { ok: false, message: `'${name}' is already active` };\n }\n activeNames.add(name);\n return { ok: true, message: `Activated '${name}'` };\n },\n\n removeNet(name) {\n if (!activeNames.has(name)) {\n return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(\", \")}` };\n }\n activeNames.delete(name);\n return { ok: true, message: `Deactivated '${name}' (state preserved)` };\n },\n\n getActiveNets() {\n return [...activeNames].map((name) => {\n const entry = registry.get(name)!;\n return { name, net: entry.net, state: entry.state };\n });\n },\n\n getAllNets() {\n return [...registry.entries()].map(([name, { net, state }]) => ({\n name,\n net,\n state,\n active: activeNames.has(name),\n }));\n },\n\n formatStatus() {\n return [...registry.entries()]\n .map(([name, { state }]) => {\n const status = activeNames.has(name) ? \"active\" : \"inactive\";\n return `${name} (${status}): ${formatMarking(state.marking)}`;\n })\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(getActiveNets(), getActiveStates());\n },\n\n isDynamic: true,\n };\n}\n\nfunction formatPromptForNets(nets: SkillNet<string>[], states: GateState<string>[]): string {\n const sections = nets.map((net, i) => {\n const enabled = getEnabledToolTransitions(net, states[i]!.marking);\n const toolList = enabled.flatMap((t) => t.tools ?? []);\n return `### ${net.name}\\nAvailable gated tools: ${toolList.join(\", \") || \"none\"}\\nFree tools: ${net.freeTools.join(\", \")}\\nState: ${formatMarking(states[i]!.marking)}`;\n });\n return `## Active Petri Nets (composed)\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"mappings":"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","names":["canFire","fire","canFire","fire","fire","fire"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/format.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["import type { Marking } from \"@petriflow/engine\";\n\n/** Structured metadata for generating user-facing block messages */\nexport type RuleMetadata =\n | { kind: \"sequence\"; prerequisite: string; dependent: string }\n | { kind: \"approval\"; tool: string }\n | { kind: \"block\"; tool: string }\n | { kind: \"limit\"; tool: string; limit: number; scope: \"session\" | string };\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n /** Structured rule metadata for generating constraint-stating block messages */\n ruleMetadata?: RuleMetadata;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import type { SkillNet } from \"./types.js\";\n\n/**\n * Generate a user-facing block reason from a net's rule metadata.\n *\n * With metadata (from the rules compiler), returns a constraint-stating message:\n * - sequence: \"deploy requires a successful call to test first.\"\n * - limit: \"deploy has reached its limit of 3 calls per session.\"\n * - block: \"rm is blocked and cannot be called.\"\n * - approval: \"deploy requires human approval.\"\n *\n * Without metadata (hand-built nets), falls back to a generic message.\n */\nexport function formatBlockReason(\n net: SkillNet<string>,\n resolvedTool: string,\n): string {\n const meta = net.ruleMetadata;\n\n if (meta) {\n switch (meta.kind) {\n case \"sequence\":\n return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;\n case \"limit\":\n return meta.scope === \"session\"\n ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.`\n : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;\n case \"block\":\n return `${meta.tool} is blocked and cannot be called.`;\n case \"approval\":\n return `${meta.tool} requires human approval.`;\n }\n }\n\n return `Tool '${resolvedTool}' is not available in the current state.`;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { formatBlockReason } from \"./format.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n const meta = net.ruleMetadata;\n const reason = meta?.kind === \"approval\"\n ? `${meta.tool} requires human approval.`\n : `${resolvedTool} requires human approval.`;\n return { block: true, reason };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `${resolvedTool} was rejected by human review.` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\nimport { formatBlockReason } from \"./format.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n const meta = v.net.ruleMetadata;\n const reason = meta?.kind === \"approval\"\n ? `${meta.tool} requires human approval.`\n : `${v.resolvedTool} requires human approval.`;\n return { block: true, reason };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `${v.resolvedTool} was rejected by human review.`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport type { GateState } from \"./gate.js\";\nimport {\n createGateState,\n formatMarking,\n getEnabledToolTransitions,\n handleToolResult as handleToolResultSingle,\n resolveTool,\n} from \"./gate.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { composedToolCall } from \"./compose.js\";\nimport type { ComposeConfig } from \"./compose.js\";\n\nexport type ReplayEntry = {\n toolName: string;\n input?: Record<string, unknown>;\n isError: boolean;\n};\n\nexport type GateManager = {\n handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;\n handleToolResult: (event: GateToolResult) => void;\n /**\n * Replay a sequence of completed tool results to advance net state.\n * Skips failed results (isError: true). Idempotent — if a transition\n * can't fire (already advanced past it), it is skipped silently.\n * Accepts an array of ReplayEntry objects or plain tool name strings\n * (treated as successful calls).\n */\n replay: (entries: ReplayEntry[] | string[]) => void;\n addNet: (name: string) => { ok: boolean; message: string };\n removeNet: (name: string) => { ok: boolean; message: string };\n getActiveNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string> }>;\n getAllNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string>; active: boolean }>;\n formatStatus: () => string;\n formatSystemPrompt: () => string;\n isDynamic: boolean;\n};\n\nexport type GateManagerOptions = {\n /** \"enforce\" blocks disallowed tools. \"shadow\" logs but never blocks. */\n mode: \"enforce\" | \"shadow\";\n /** Called after every gating decision. Use for logging, metrics, debugging. */\n onDecision?: (event: GateToolCall, decision: GateDecision) => void;\n};\n\nexport function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager {\n const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);\n\n if (opts) {\n const original = manager.handleToolCall;\n manager.handleToolCall = async (event, ctx) => {\n const decision = await original.call(manager, event, ctx);\n opts.onDecision?.(event, decision);\n if (opts.mode === \"shadow\" && decision?.block) {\n return undefined;\n }\n return decision;\n };\n }\n\n return manager;\n}\n\nfunction normalizeEntries(entries: ReplayEntry[] | string[]): ReplayEntry[] {\n if (entries.length === 0) return [];\n if (typeof entries[0] === \"string\") {\n return (entries as string[]).map((toolName) => ({ toolName, isError: false }));\n }\n return entries as ReplayEntry[];\n}\n\nfunction replayNets(\n nets: SkillNet<string>[],\n states: GateState<string>[],\n entries: ReplayEntry[],\n): void {\n for (const entry of entries) {\n if (entry.isError) continue;\n for (let i = 0; i < nets.length; i++) {\n const net = nets[i]!;\n const state = states[i]!;\n const resolved = resolveTool(net, { toolName: entry.toolName, input: entry.input ?? {} });\n\n if (net.freeTools.includes(resolved)) continue;\n\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolved));\n if (matching.length === 0) continue;\n\n const transition = matching[0]!;\n if (canFire(state.marking, transition)) {\n state.marking = fire(state.marking, transition);\n\n if (transition.deferred && net.onDeferredResult) {\n net.onDeferredResult(\n { toolCallId: `replay-${i}`, input: entry.input ?? {}, isError: false },\n resolved,\n transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n }\n }\n}\n\nfunction createArrayManager(nets: SkillNet<string>[]): GateManager {\n const states = nets.map((net) =>\n createGateState(autoAdvance(net, { ...net.initialMarking })),\n );\n\n const getNets = () => nets;\n const getStates = () => states;\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getNets, getStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (let i = 0; i < nets.length; i++) {\n handleToolResultSingle(event, nets[i]!, states[i]!);\n }\n },\n\n replay(entries) {\n replayNets(nets, states, normalizeEntries(entries));\n },\n\n addNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n removeNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n getActiveNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]! }));\n },\n\n getAllNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]!, active: true }));\n },\n\n formatStatus() {\n return nets\n .map((n, i) => `${n.name}: ${formatMarking(states[i]!.marking)}`)\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(nets, states);\n },\n\n isDynamic: false,\n };\n}\n\nfunction createRegistryManager(config: ComposeConfig): GateManager {\n const registry = new Map<string, { net: SkillNet<string>; state: GateState<string> }>();\n for (const [name, net] of Object.entries(config.registry)) {\n registry.set(name, {\n net,\n state: createGateState(autoAdvance(net, { ...net.initialMarking })),\n });\n }\n\n const activeNames = new Set<string>(config.active ?? Object.keys(config.registry));\n\n const getActiveNets = () => [...activeNames].map((n) => registry.get(n)!.net);\n const getActiveStates = () => [...activeNames].map((n) => registry.get(n)!.state);\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getActiveNets, getActiveStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (const { net, state } of registry.values()) {\n handleToolResultSingle(event, net, state);\n }\n },\n\n replay(entries) {\n const activeNets = getActiveNets();\n const activeStates = getActiveStates();\n replayNets(activeNets, activeStates, normalizeEntries(entries));\n },\n\n addNet(name) {\n if (!registry.has(name)) {\n return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(\", \")}` };\n }\n if (activeNames.has(name)) {\n return { ok: false, message: `'${name}' is already active` };\n }\n activeNames.add(name);\n return { ok: true, message: `Activated '${name}'` };\n },\n\n removeNet(name) {\n if (!activeNames.has(name)) {\n return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(\", \")}` };\n }\n activeNames.delete(name);\n return { ok: true, message: `Deactivated '${name}' (state preserved)` };\n },\n\n getActiveNets() {\n return [...activeNames].map((name) => {\n const entry = registry.get(name)!;\n return { name, net: entry.net, state: entry.state };\n });\n },\n\n getAllNets() {\n return [...registry.entries()].map(([name, { net, state }]) => ({\n name,\n net,\n state,\n active: activeNames.has(name),\n }));\n },\n\n formatStatus() {\n return [...registry.entries()]\n .map(([name, { state }]) => {\n const status = activeNames.has(name) ? \"active\" : \"inactive\";\n return `${name} (${status}): ${formatMarking(state.marking)}`;\n })\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(getActiveNets(), getActiveStates());\n },\n\n isDynamic: true,\n };\n}\n\nfunction formatPromptForNets(nets: SkillNet<string>[], states: GateState<string>[]): string {\n const sections = nets.map((net, i) => {\n const enabled = getEnabledToolTransitions(net, states[i]!.marking);\n const toolList = enabled.flatMap((t) => t.tools ?? []);\n return `### ${net.name}\\nAvailable gated tools: ${toolList.join(\", \") || \"none\"}\\nFree tools: ${net.freeTools.join(\", \")}\\nState: ${formatMarking(states[i]!.marking)}`;\n });\n return `## Active Petri Nets (composed)\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"mappings":"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","names":["canFire","fire","canFire","fire","fire","fire","canFire","fire","canFire","fire"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@petriflow/gate",
3
- "version": "0.1.3",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",