@petriflow/gate 0.3.0 → 0.3.2
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 +67 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +68 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -75,6 +75,8 @@ function hasInputConflict(a, b, marking) {
|
|
|
75
75
|
}
|
|
76
76
|
function autoAdvance(net, marking) {
|
|
77
77
|
let current = { ...marking };
|
|
78
|
+
const seen = /* @__PURE__ */ new Set();
|
|
79
|
+
seen.add(JSON.stringify(current));
|
|
78
80
|
for (; ; ) {
|
|
79
81
|
const structural = net.transitions.filter(
|
|
80
82
|
(t) => isStructural(t) && (0, import_engine.canFire)(current, t)
|
|
@@ -89,6 +91,9 @@ function autoAdvance(net, marking) {
|
|
|
89
91
|
current = (0, import_engine.fire)(current, t);
|
|
90
92
|
}
|
|
91
93
|
}
|
|
94
|
+
const key = JSON.stringify(current);
|
|
95
|
+
if (seen.has(key)) break;
|
|
96
|
+
seen.add(key);
|
|
92
97
|
}
|
|
93
98
|
return current;
|
|
94
99
|
}
|
|
@@ -155,6 +160,12 @@ async function handleToolCall(event, ctx, net, state) {
|
|
|
155
160
|
return { block: true, reason: `${resolvedTool} was rejected by human review.` };
|
|
156
161
|
}
|
|
157
162
|
}
|
|
163
|
+
if (!(0, import_engine2.canFire)(state.marking, transition)) {
|
|
164
|
+
return {
|
|
165
|
+
block: true,
|
|
166
|
+
reason: formatBlockReason(net, resolvedTool)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
158
169
|
if (transition.deferred) {
|
|
159
170
|
state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });
|
|
160
171
|
return void 0;
|
|
@@ -265,13 +276,21 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
265
276
|
v.state
|
|
266
277
|
);
|
|
267
278
|
if (rejection) {
|
|
268
|
-
for (let j = 0; j
|
|
279
|
+
for (let j = 0; j <= i; j++) {
|
|
269
280
|
gated[j].state.meta = metaSnapshots[j];
|
|
270
281
|
}
|
|
271
282
|
return rejection;
|
|
272
283
|
}
|
|
273
284
|
}
|
|
274
285
|
}
|
|
286
|
+
for (const v of gated) {
|
|
287
|
+
if (!v.transition.deferred && !(0, import_engine3.canFire)(v.state.marking, v.transition)) {
|
|
288
|
+
return {
|
|
289
|
+
block: true,
|
|
290
|
+
reason: `Tool '${v.resolvedTool}' is no longer available (state changed).`
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
275
294
|
for (const v of gated) {
|
|
276
295
|
if (v.transition.deferred) {
|
|
277
296
|
v.state.pending.set(event.toolCallId, {
|
|
@@ -288,6 +307,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
288
307
|
}
|
|
289
308
|
|
|
290
309
|
// src/manager.ts
|
|
310
|
+
var import_engine4 = require("@petriflow/engine");
|
|
291
311
|
function createGateManager(input, opts) {
|
|
292
312
|
const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);
|
|
293
313
|
if (opts) {
|
|
@@ -303,6 +323,41 @@ function createGateManager(input, opts) {
|
|
|
303
323
|
}
|
|
304
324
|
return manager;
|
|
305
325
|
}
|
|
326
|
+
function normalizeEntries(entries) {
|
|
327
|
+
if (entries.length === 0) return [];
|
|
328
|
+
if (typeof entries[0] === "string") {
|
|
329
|
+
return entries.map((toolName) => ({ toolName, isError: false }));
|
|
330
|
+
}
|
|
331
|
+
return entries;
|
|
332
|
+
}
|
|
333
|
+
function replayNets(nets, states, entries) {
|
|
334
|
+
for (let ei = 0; ei < entries.length; ei++) {
|
|
335
|
+
const entry = entries[ei];
|
|
336
|
+
if (entry.isError) continue;
|
|
337
|
+
for (let i = 0; i < nets.length; i++) {
|
|
338
|
+
const net = nets[i];
|
|
339
|
+
const state = states[i];
|
|
340
|
+
const resolved = resolveTool(net, { toolName: entry.toolName, input: entry.input ?? {} });
|
|
341
|
+
if (net.freeTools.includes(resolved)) continue;
|
|
342
|
+
const enabled = getEnabledToolTransitions(net, state.marking);
|
|
343
|
+
const matching = enabled.filter((t) => t.tools.includes(resolved));
|
|
344
|
+
if (matching.length === 0) continue;
|
|
345
|
+
const transition = matching[0];
|
|
346
|
+
if ((0, import_engine4.canFire)(state.marking, transition)) {
|
|
347
|
+
state.marking = (0, import_engine4.fire)(state.marking, transition);
|
|
348
|
+
if (transition.deferred && net.onDeferredResult) {
|
|
349
|
+
net.onDeferredResult(
|
|
350
|
+
{ toolCallId: `replay-${ei}-${i}`, input: entry.input ?? {}, isError: false },
|
|
351
|
+
resolved,
|
|
352
|
+
transition,
|
|
353
|
+
state
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
state.marking = autoAdvance(net, state.marking);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
306
361
|
function createArrayManager(nets) {
|
|
307
362
|
const states = nets.map(
|
|
308
363
|
(net) => createGateState(autoAdvance(net, { ...net.initialMarking }))
|
|
@@ -318,6 +373,9 @@ function createArrayManager(nets) {
|
|
|
318
373
|
handleToolResult(event, nets[i], states[i]);
|
|
319
374
|
}
|
|
320
375
|
},
|
|
376
|
+
replay(entries) {
|
|
377
|
+
replayNets(nets, states, normalizeEntries(entries));
|
|
378
|
+
},
|
|
321
379
|
addNet() {
|
|
322
380
|
return { ok: false, message: "Static composition does not support dynamic nets" };
|
|
323
381
|
},
|
|
@@ -347,7 +405,9 @@ function createRegistryManager(config) {
|
|
|
347
405
|
state: createGateState(autoAdvance(net, { ...net.initialMarking }))
|
|
348
406
|
});
|
|
349
407
|
}
|
|
350
|
-
const activeNames = new Set(
|
|
408
|
+
const activeNames = new Set(
|
|
409
|
+
(config.active ?? Object.keys(config.registry)).filter((n) => registry.has(n))
|
|
410
|
+
);
|
|
351
411
|
const getActiveNets = () => [...activeNames].map((n) => registry.get(n).net);
|
|
352
412
|
const getActiveStates = () => [...activeNames].map((n) => registry.get(n).state);
|
|
353
413
|
return {
|
|
@@ -359,6 +419,11 @@ function createRegistryManager(config) {
|
|
|
359
419
|
handleToolResult(event, net, state);
|
|
360
420
|
}
|
|
361
421
|
},
|
|
422
|
+
replay(entries) {
|
|
423
|
+
const activeNets = getActiveNets();
|
|
424
|
+
const activeStates = getActiveStates();
|
|
425
|
+
replayNets(activeNets, activeStates, normalizeEntries(entries));
|
|
426
|
+
},
|
|
362
427
|
addNet(name) {
|
|
363
428
|
if (!registry.has(name)) {
|
|
364
429
|
return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(", ")}` };
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 } 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 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 const seen = new Set<string>();\n seen.add(JSON.stringify(current));\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 // Break if we've returned to a previously-seen marking (cycle detection)\n const key = JSON.stringify(current);\n if (seen.has(key)) break;\n seen.add(key);\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 // Re-check enablement — marking may have changed during await ctx.confirm()\n if (!canFire(state.marking, transition)) {\n return {\n block: true,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\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 { canFire, 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 (including the rejector's own)\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 // Re-validate enablement — marking may have changed during awaits\n for (const v of gated) {\n if (!v.transition.deferred && !canFire(v.state.marking, v.transition)) {\n return {\n block: true,\n reason: `Tool '${v.resolvedTool}' is no longer available (state changed).`,\n };\n }\n }\n\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 (let ei = 0; ei < entries.length; ei++) {\n const entry = entries[ei]!;\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-${ei}-${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>(\n (config.active ?? Object.keys(config.registry)).filter((n) => registry.has(n)),\n );\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":["import_engine","import_engine","import_engine"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -204,9 +204,22 @@ declare function classifyNets<P extends string>(nets: SkillNet<P>[], states: Gat
|
|
|
204
204
|
*/
|
|
205
205
|
declare function composedToolCall(getNets: () => SkillNet<string>[], getStates: () => GateState<string>[], event: GateToolCall, ctx: GateContext): Promise<GateDecision>;
|
|
206
206
|
|
|
207
|
+
type ReplayEntry = {
|
|
208
|
+
toolName: string;
|
|
209
|
+
input?: Record<string, unknown>;
|
|
210
|
+
isError: boolean;
|
|
211
|
+
};
|
|
207
212
|
type GateManager = {
|
|
208
213
|
handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;
|
|
209
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;
|
|
210
223
|
addNet: (name: string) => {
|
|
211
224
|
ok: boolean;
|
|
212
225
|
message: string;
|
|
@@ -238,4 +251,4 @@ type GateManagerOptions = {
|
|
|
238
251
|
};
|
|
239
252
|
declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
|
|
240
253
|
|
|
241
|
-
export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type RuleMetadata, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatBlockReason, 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
|
@@ -204,9 +204,22 @@ declare function classifyNets<P extends string>(nets: SkillNet<P>[], states: Gat
|
|
|
204
204
|
*/
|
|
205
205
|
declare function composedToolCall(getNets: () => SkillNet<string>[], getStates: () => GateState<string>[], event: GateToolCall, ctx: GateContext): Promise<GateDecision>;
|
|
206
206
|
|
|
207
|
+
type ReplayEntry = {
|
|
208
|
+
toolName: string;
|
|
209
|
+
input?: Record<string, unknown>;
|
|
210
|
+
isError: boolean;
|
|
211
|
+
};
|
|
207
212
|
type GateManager = {
|
|
208
213
|
handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;
|
|
209
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;
|
|
210
223
|
addNet: (name: string) => {
|
|
211
224
|
ok: boolean;
|
|
212
225
|
message: string;
|
|
@@ -238,4 +251,4 @@ type GateManagerOptions = {
|
|
|
238
251
|
};
|
|
239
252
|
declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
|
|
240
253
|
|
|
241
|
-
export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type RuleMetadata, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatBlockReason, 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
|
@@ -38,6 +38,8 @@ function hasInputConflict(a, b, marking) {
|
|
|
38
38
|
}
|
|
39
39
|
function autoAdvance(net, marking) {
|
|
40
40
|
let current = { ...marking };
|
|
41
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42
|
+
seen.add(JSON.stringify(current));
|
|
41
43
|
for (; ; ) {
|
|
42
44
|
const structural = net.transitions.filter(
|
|
43
45
|
(t) => isStructural(t) && canFire(current, t)
|
|
@@ -52,6 +54,9 @@ function autoAdvance(net, marking) {
|
|
|
52
54
|
current = fire(current, t);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
57
|
+
const key = JSON.stringify(current);
|
|
58
|
+
if (seen.has(key)) break;
|
|
59
|
+
seen.add(key);
|
|
55
60
|
}
|
|
56
61
|
return current;
|
|
57
62
|
}
|
|
@@ -118,6 +123,12 @@ async function handleToolCall(event, ctx, net, state) {
|
|
|
118
123
|
return { block: true, reason: `${resolvedTool} was rejected by human review.` };
|
|
119
124
|
}
|
|
120
125
|
}
|
|
126
|
+
if (!canFire2(state.marking, transition)) {
|
|
127
|
+
return {
|
|
128
|
+
block: true,
|
|
129
|
+
reason: formatBlockReason(net, resolvedTool)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
121
132
|
if (transition.deferred) {
|
|
122
133
|
state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });
|
|
123
134
|
return void 0;
|
|
@@ -152,7 +163,7 @@ function handleToolResult(event, net, state) {
|
|
|
152
163
|
}
|
|
153
164
|
|
|
154
165
|
// src/compose.ts
|
|
155
|
-
import { fire as fire3 } from "@petriflow/engine";
|
|
166
|
+
import { canFire as canFire3, fire as fire3 } from "@petriflow/engine";
|
|
156
167
|
function hasJurisdiction(net, resolvedTool) {
|
|
157
168
|
return net.transitions.some(
|
|
158
169
|
(t) => t.tools !== void 0 && t.tools.includes(resolvedTool)
|
|
@@ -228,13 +239,21 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
228
239
|
v.state
|
|
229
240
|
);
|
|
230
241
|
if (rejection) {
|
|
231
|
-
for (let j = 0; j
|
|
242
|
+
for (let j = 0; j <= i; j++) {
|
|
232
243
|
gated[j].state.meta = metaSnapshots[j];
|
|
233
244
|
}
|
|
234
245
|
return rejection;
|
|
235
246
|
}
|
|
236
247
|
}
|
|
237
248
|
}
|
|
249
|
+
for (const v of gated) {
|
|
250
|
+
if (!v.transition.deferred && !canFire3(v.state.marking, v.transition)) {
|
|
251
|
+
return {
|
|
252
|
+
block: true,
|
|
253
|
+
reason: `Tool '${v.resolvedTool}' is no longer available (state changed).`
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
238
257
|
for (const v of gated) {
|
|
239
258
|
if (v.transition.deferred) {
|
|
240
259
|
v.state.pending.set(event.toolCallId, {
|
|
@@ -251,6 +270,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
251
270
|
}
|
|
252
271
|
|
|
253
272
|
// src/manager.ts
|
|
273
|
+
import { canFire as canFire4, fire as fire4 } from "@petriflow/engine";
|
|
254
274
|
function createGateManager(input, opts) {
|
|
255
275
|
const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);
|
|
256
276
|
if (opts) {
|
|
@@ -266,6 +286,41 @@ function createGateManager(input, opts) {
|
|
|
266
286
|
}
|
|
267
287
|
return manager;
|
|
268
288
|
}
|
|
289
|
+
function normalizeEntries(entries) {
|
|
290
|
+
if (entries.length === 0) return [];
|
|
291
|
+
if (typeof entries[0] === "string") {
|
|
292
|
+
return entries.map((toolName) => ({ toolName, isError: false }));
|
|
293
|
+
}
|
|
294
|
+
return entries;
|
|
295
|
+
}
|
|
296
|
+
function replayNets(nets, states, entries) {
|
|
297
|
+
for (let ei = 0; ei < entries.length; ei++) {
|
|
298
|
+
const entry = entries[ei];
|
|
299
|
+
if (entry.isError) continue;
|
|
300
|
+
for (let i = 0; i < nets.length; i++) {
|
|
301
|
+
const net = nets[i];
|
|
302
|
+
const state = states[i];
|
|
303
|
+
const resolved = resolveTool(net, { toolName: entry.toolName, input: entry.input ?? {} });
|
|
304
|
+
if (net.freeTools.includes(resolved)) continue;
|
|
305
|
+
const enabled = getEnabledToolTransitions(net, state.marking);
|
|
306
|
+
const matching = enabled.filter((t) => t.tools.includes(resolved));
|
|
307
|
+
if (matching.length === 0) continue;
|
|
308
|
+
const transition = matching[0];
|
|
309
|
+
if (canFire4(state.marking, transition)) {
|
|
310
|
+
state.marking = fire4(state.marking, transition);
|
|
311
|
+
if (transition.deferred && net.onDeferredResult) {
|
|
312
|
+
net.onDeferredResult(
|
|
313
|
+
{ toolCallId: `replay-${ei}-${i}`, input: entry.input ?? {}, isError: false },
|
|
314
|
+
resolved,
|
|
315
|
+
transition,
|
|
316
|
+
state
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
state.marking = autoAdvance(net, state.marking);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
269
324
|
function createArrayManager(nets) {
|
|
270
325
|
const states = nets.map(
|
|
271
326
|
(net) => createGateState(autoAdvance(net, { ...net.initialMarking }))
|
|
@@ -281,6 +336,9 @@ function createArrayManager(nets) {
|
|
|
281
336
|
handleToolResult(event, nets[i], states[i]);
|
|
282
337
|
}
|
|
283
338
|
},
|
|
339
|
+
replay(entries) {
|
|
340
|
+
replayNets(nets, states, normalizeEntries(entries));
|
|
341
|
+
},
|
|
284
342
|
addNet() {
|
|
285
343
|
return { ok: false, message: "Static composition does not support dynamic nets" };
|
|
286
344
|
},
|
|
@@ -310,7 +368,9 @@ function createRegistryManager(config) {
|
|
|
310
368
|
state: createGateState(autoAdvance(net, { ...net.initialMarking }))
|
|
311
369
|
});
|
|
312
370
|
}
|
|
313
|
-
const activeNames = new Set(
|
|
371
|
+
const activeNames = new Set(
|
|
372
|
+
(config.active ?? Object.keys(config.registry)).filter((n) => registry.has(n))
|
|
373
|
+
);
|
|
314
374
|
const getActiveNets = () => [...activeNames].map((n) => registry.get(n).net);
|
|
315
375
|
const getActiveStates = () => [...activeNames].map((n) => registry.get(n).state);
|
|
316
376
|
return {
|
|
@@ -322,6 +382,11 @@ function createRegistryManager(config) {
|
|
|
322
382
|
handleToolResult(event, net, state);
|
|
323
383
|
}
|
|
324
384
|
},
|
|
385
|
+
replay(entries) {
|
|
386
|
+
const activeNets = getActiveNets();
|
|
387
|
+
const activeStates = getActiveStates();
|
|
388
|
+
replayNets(activeNets, activeStates, normalizeEntries(entries));
|
|
389
|
+
},
|
|
325
390
|
addNet(name) {
|
|
326
391
|
if (!registry.has(name)) {
|
|
327
392
|
return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(", ")}` };
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 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 const seen = new Set<string>();\n seen.add(JSON.stringify(current));\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 // Break if we've returned to a previously-seen marking (cycle detection)\n const key = JSON.stringify(current);\n if (seen.has(key)) break;\n seen.add(key);\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 // Re-check enablement — marking may have changed during await ctx.confirm()\n if (!canFire(state.marking, transition)) {\n return {\n block: true,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\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 { canFire, 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 (including the rejector's own)\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 // Re-validate enablement — marking may have changed during awaits\n for (const v of gated) {\n if (!v.transition.deferred && !canFire(v.state.marking, v.transition)) {\n return {\n block: true,\n reason: `Tool '${v.resolvedTool}' is no longer available (state changed).`,\n };\n }\n }\n\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 (let ei = 0; ei < entries.length; ei++) {\n const entry = entries[ei]!;\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-${ei}-${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>(\n (config.active ?? Object.keys(config.registry)).filter((n) => registry.has(n)),\n );\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","canFire","fire","canFire","fire","canFire","fire","canFire","fire"]}
|