@petriflow/gate 0.1.3 → 0.3.0
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 +30 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -1
- package/dist/index.d.ts +33 -1
- package/dist/index.js +29 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
createGateManager: () => createGateManager,
|
|
27
27
|
createGateState: () => createGateState,
|
|
28
28
|
defineSkillNet: () => defineSkillNet,
|
|
29
|
+
formatBlockReason: () => formatBlockReason,
|
|
29
30
|
formatMarking: () => formatMarking,
|
|
30
31
|
getEnabledToolTransitions: () => getEnabledToolTransitions,
|
|
31
32
|
handleToolCall: () => handleToolCall,
|
|
@@ -39,6 +40,24 @@ function defineSkillNet(net) {
|
|
|
39
40
|
return net;
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
// src/format.ts
|
|
44
|
+
function formatBlockReason(net, resolvedTool) {
|
|
45
|
+
const meta = net.ruleMetadata;
|
|
46
|
+
if (meta) {
|
|
47
|
+
switch (meta.kind) {
|
|
48
|
+
case "sequence":
|
|
49
|
+
return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;
|
|
50
|
+
case "limit":
|
|
51
|
+
return meta.scope === "session" ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.` : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;
|
|
52
|
+
case "block":
|
|
53
|
+
return `${meta.tool} is blocked and cannot be called.`;
|
|
54
|
+
case "approval":
|
|
55
|
+
return `${meta.tool} requires human approval.`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return `Tool '${resolvedTool}' is not available in the current state.`;
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
// src/advance.ts
|
|
43
62
|
var import_engine = require("@petriflow/engine");
|
|
44
63
|
function isStructural(t) {
|
|
@@ -109,7 +128,7 @@ async function handleToolCall(event, ctx, net, state) {
|
|
|
109
128
|
if (matching.length === 0) {
|
|
110
129
|
return {
|
|
111
130
|
block: true,
|
|
112
|
-
reason:
|
|
131
|
+
reason: formatBlockReason(net, resolvedTool)
|
|
113
132
|
};
|
|
114
133
|
}
|
|
115
134
|
const transition = matching[0];
|
|
@@ -124,14 +143,16 @@ async function handleToolCall(event, ctx, net, state) {
|
|
|
124
143
|
}
|
|
125
144
|
if (transition.type === "manual") {
|
|
126
145
|
if (!ctx.hasUI) {
|
|
127
|
-
|
|
146
|
+
const meta = net.ruleMetadata;
|
|
147
|
+
const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${resolvedTool} requires human approval.`;
|
|
148
|
+
return { block: true, reason };
|
|
128
149
|
}
|
|
129
150
|
const approved = await ctx.confirm(
|
|
130
151
|
`Approve: ${transition.name}`,
|
|
131
152
|
`Allow '${resolvedTool}' via transition '${transition.name}'?`
|
|
132
153
|
);
|
|
133
154
|
if (!approved) {
|
|
134
|
-
return { block: true, reason:
|
|
155
|
+
return { block: true, reason: `${resolvedTool} was rejected by human review.` };
|
|
135
156
|
}
|
|
136
157
|
}
|
|
137
158
|
if (transition.deferred) {
|
|
@@ -191,7 +212,7 @@ function classifyNets(nets, states, event) {
|
|
|
191
212
|
return {
|
|
192
213
|
...base,
|
|
193
214
|
kind: "blocked",
|
|
194
|
-
reason:
|
|
215
|
+
reason: formatBlockReason(net, resolvedTool)
|
|
195
216
|
};
|
|
196
217
|
}
|
|
197
218
|
return { ...base, kind: "gated", transition: matching[0] };
|
|
@@ -217,10 +238,9 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
217
238
|
for (const v of gated) {
|
|
218
239
|
if (v.transition.type === "manual") {
|
|
219
240
|
if (!ctx.hasUI) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
};
|
|
241
|
+
const meta = v.net.ruleMetadata;
|
|
242
|
+
const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${v.resolvedTool} requires human approval.`;
|
|
243
|
+
return { block: true, reason };
|
|
224
244
|
}
|
|
225
245
|
const approved = await ctx.confirm(
|
|
226
246
|
`Approve: ${v.transition.name} (${v.net.name})`,
|
|
@@ -229,7 +249,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
229
249
|
if (!approved) {
|
|
230
250
|
return {
|
|
231
251
|
block: true,
|
|
232
|
-
reason:
|
|
252
|
+
reason: `${v.resolvedTool} was rejected by human review.`
|
|
233
253
|
};
|
|
234
254
|
}
|
|
235
255
|
}
|
|
@@ -402,6 +422,7 @@ ${sections.join("\n\n")}`;
|
|
|
402
422
|
createGateManager,
|
|
403
423
|
createGateState,
|
|
404
424
|
defineSkillNet,
|
|
425
|
+
formatBlockReason,
|
|
405
426
|
formatMarking,
|
|
406
427
|
getEnabledToolTransitions,
|
|
407
428
|
handleToolCall,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["// Types\nexport { defineSkillNet } from \"./types.js\";\nexport type { SkillNet, GatedTransition, ToolEvent } from \"./types.js\";\n\n// Generic event types\nexport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\n\n// Auto-advance\nexport { autoAdvance } from \"./advance.js\";\n\n// Single-net gating\nexport {\n handleToolCall,\n handleToolResult,\n formatMarking,\n getEnabledToolTransitions,\n createGateState,\n resolveTool,\n} from \"./gate.js\";\nexport type { GateState } from \"./gate.js\";\n\n// Multi-net composition\nexport { classifyNets, composedToolCall } from \"./compose.js\";\nexport type { ComposeConfig, NetVerdict } from \"./compose.js\";\n\n// Manager\nexport { createGateManager } from \"./manager.js\";\nexport type { GateManager, GateManagerOptions } from \"./manager.js\";\n","import type { Marking } from \"@petriflow/engine\";\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `Human rejected '${transition.name}'` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n formatMarking,\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return {\n block: true,\n reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`,\n };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `[${v.net.name}] Human rejected '${v.transition.name}'`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport type { GateState } from \"./gate.js\";\nimport {\n createGateState,\n formatMarking,\n getEnabledToolTransitions,\n handleToolResult as handleToolResultSingle,\n} from \"./gate.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { composedToolCall } from \"./compose.js\";\nimport type { ComposeConfig } from \"./compose.js\";\n\nexport type GateManager = {\n handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;\n handleToolResult: (event: GateToolResult) => void;\n addNet: (name: string) => { ok: boolean; message: string };\n removeNet: (name: string) => { ok: boolean; message: string };\n getActiveNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string> }>;\n getAllNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string>; active: boolean }>;\n formatStatus: () => string;\n formatSystemPrompt: () => string;\n isDynamic: boolean;\n};\n\nexport type GateManagerOptions = {\n /** \"enforce\" blocks disallowed tools. \"shadow\" logs but never blocks. */\n mode: \"enforce\" | \"shadow\";\n /** Called after every gating decision. Use for logging, metrics, debugging. */\n onDecision?: (event: GateToolCall, decision: GateDecision) => void;\n};\n\nexport function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager {\n const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);\n\n if (opts) {\n const original = manager.handleToolCall;\n manager.handleToolCall = async (event, ctx) => {\n const decision = await original.call(manager, event, ctx);\n opts.onDecision?.(event, decision);\n if (opts.mode === \"shadow\" && decision?.block) {\n return undefined;\n }\n return decision;\n };\n }\n\n return manager;\n}\n\nfunction createArrayManager(nets: SkillNet<string>[]): GateManager {\n const states = nets.map((net) =>\n createGateState(autoAdvance(net, { ...net.initialMarking })),\n );\n\n const getNets = () => nets;\n const getStates = () => states;\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getNets, getStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (let i = 0; i < nets.length; i++) {\n handleToolResultSingle(event, nets[i]!, states[i]!);\n }\n },\n\n addNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n removeNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n getActiveNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]! }));\n },\n\n getAllNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]!, active: true }));\n },\n\n formatStatus() {\n return nets\n .map((n, i) => `${n.name}: ${formatMarking(states[i]!.marking)}`)\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(nets, states);\n },\n\n isDynamic: false,\n };\n}\n\nfunction createRegistryManager(config: ComposeConfig): GateManager {\n const registry = new Map<string, { net: SkillNet<string>; state: GateState<string> }>();\n for (const [name, net] of Object.entries(config.registry)) {\n registry.set(name, {\n net,\n state: createGateState(autoAdvance(net, { ...net.initialMarking })),\n });\n }\n\n const activeNames = new Set<string>(config.active ?? Object.keys(config.registry));\n\n const getActiveNets = () => [...activeNames].map((n) => registry.get(n)!.net);\n const getActiveStates = () => [...activeNames].map((n) => registry.get(n)!.state);\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getActiveNets, getActiveStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (const { net, state } of registry.values()) {\n handleToolResultSingle(event, net, state);\n }\n },\n\n addNet(name) {\n if (!registry.has(name)) {\n return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(\", \")}` };\n }\n if (activeNames.has(name)) {\n return { ok: false, message: `'${name}' is already active` };\n }\n activeNames.add(name);\n return { ok: true, message: `Activated '${name}'` };\n },\n\n removeNet(name) {\n if (!activeNames.has(name)) {\n return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(\", \")}` };\n }\n activeNames.delete(name);\n return { ok: true, message: `Deactivated '${name}' (state preserved)` };\n },\n\n getActiveNets() {\n return [...activeNames].map((name) => {\n const entry = registry.get(name)!;\n return { name, net: entry.net, state: entry.state };\n });\n },\n\n getAllNets() {\n return [...registry.entries()].map(([name, { net, state }]) => ({\n name,\n net,\n state,\n active: activeNames.has(name),\n }));\n },\n\n formatStatus() {\n return [...registry.entries()]\n .map(([name, { state }]) => {\n const status = activeNames.has(name) ? \"active\" : \"inactive\";\n return `${name} (${status}): ${formatMarking(state.marking)}`;\n })\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(getActiveNets(), getActiveStates());\n },\n\n isDynamic: true,\n };\n}\n\nfunction formatPromptForNets(nets: SkillNet<string>[], states: GateState<string>[]): string {\n const sections = nets.map((net, i) => {\n const enabled = getEnabledToolTransitions(net, states[i]!.marking);\n const toolList = enabled.flatMap((t) => t.tools ?? []);\n return `### ${net.name}\\nAvailable gated tools: ${toolList.join(\", \") || \"none\"}\\nFree tools: ${net.freeTools.join(\", \")}\\nState: ${formatMarking(states[i]!.marking)}`;\n });\n return `## Active Petri Nets (composed)\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"mappings":"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","names":["import_engine","import_engine"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/format.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["// Types\nexport { defineSkillNet } from \"./types.js\";\nexport type { SkillNet, GatedTransition, ToolEvent, RuleMetadata } from \"./types.js\";\n\n// Block reason formatting\nexport { formatBlockReason } from \"./format.js\";\n\n// Generic event types\nexport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\n\n// Auto-advance\nexport { autoAdvance } from \"./advance.js\";\n\n// Single-net gating\nexport {\n handleToolCall,\n handleToolResult,\n formatMarking,\n getEnabledToolTransitions,\n createGateState,\n resolveTool,\n} from \"./gate.js\";\nexport type { GateState } from \"./gate.js\";\n\n// Multi-net composition\nexport { classifyNets, composedToolCall } from \"./compose.js\";\nexport type { ComposeConfig, NetVerdict } from \"./compose.js\";\n\n// Manager\nexport { createGateManager } from \"./manager.js\";\nexport type { GateManager, GateManagerOptions } 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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { Marking } from '@petriflow/engine';
|
|
2
2
|
|
|
3
|
+
/** Structured metadata for generating user-facing block messages */
|
|
4
|
+
type RuleMetadata = {
|
|
5
|
+
kind: "sequence";
|
|
6
|
+
prerequisite: string;
|
|
7
|
+
dependent: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "approval";
|
|
10
|
+
tool: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "block";
|
|
13
|
+
tool: string;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "limit";
|
|
16
|
+
tool: string;
|
|
17
|
+
limit: number;
|
|
18
|
+
scope: "session" | string;
|
|
19
|
+
};
|
|
3
20
|
/** A transition that optionally gates tool access */
|
|
4
21
|
type GatedTransition<Place extends string> = {
|
|
5
22
|
name: string;
|
|
@@ -66,10 +83,25 @@ type SkillNet<Place extends string> = {
|
|
|
66
83
|
marking: Marking<Place>;
|
|
67
84
|
meta: Record<string, unknown>;
|
|
68
85
|
}): void;
|
|
86
|
+
/** Structured rule metadata for generating constraint-stating block messages */
|
|
87
|
+
ruleMetadata?: RuleMetadata;
|
|
69
88
|
};
|
|
70
89
|
/** Type-safe helper — validates places/marking at the type level */
|
|
71
90
|
declare function defineSkillNet<Place extends string>(net: SkillNet<Place>): SkillNet<Place>;
|
|
72
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Generate a user-facing block reason from a net's rule metadata.
|
|
94
|
+
*
|
|
95
|
+
* With metadata (from the rules compiler), returns a constraint-stating message:
|
|
96
|
+
* - sequence: "deploy requires a successful call to test first."
|
|
97
|
+
* - limit: "deploy has reached its limit of 3 calls per session."
|
|
98
|
+
* - block: "rm is blocked and cannot be called."
|
|
99
|
+
* - approval: "deploy requires human approval."
|
|
100
|
+
*
|
|
101
|
+
* Without metadata (hand-built nets), falls back to a generic message.
|
|
102
|
+
*/
|
|
103
|
+
declare function formatBlockReason(net: SkillNet<string>, resolvedTool: string): string;
|
|
104
|
+
|
|
73
105
|
/** Generic tool call event — framework-agnostic */
|
|
74
106
|
type GateToolCall = {
|
|
75
107
|
toolCallId: string;
|
|
@@ -206,4 +238,4 @@ type GateManagerOptions = {
|
|
|
206
238
|
};
|
|
207
239
|
declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
|
|
208
240
|
|
|
209
|
-
export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatMarking, getEnabledToolTransitions, handleToolCall, handleToolResult, resolveTool };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { Marking } from '@petriflow/engine';
|
|
2
2
|
|
|
3
|
+
/** Structured metadata for generating user-facing block messages */
|
|
4
|
+
type RuleMetadata = {
|
|
5
|
+
kind: "sequence";
|
|
6
|
+
prerequisite: string;
|
|
7
|
+
dependent: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "approval";
|
|
10
|
+
tool: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "block";
|
|
13
|
+
tool: string;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "limit";
|
|
16
|
+
tool: string;
|
|
17
|
+
limit: number;
|
|
18
|
+
scope: "session" | string;
|
|
19
|
+
};
|
|
3
20
|
/** A transition that optionally gates tool access */
|
|
4
21
|
type GatedTransition<Place extends string> = {
|
|
5
22
|
name: string;
|
|
@@ -66,10 +83,25 @@ type SkillNet<Place extends string> = {
|
|
|
66
83
|
marking: Marking<Place>;
|
|
67
84
|
meta: Record<string, unknown>;
|
|
68
85
|
}): void;
|
|
86
|
+
/** Structured rule metadata for generating constraint-stating block messages */
|
|
87
|
+
ruleMetadata?: RuleMetadata;
|
|
69
88
|
};
|
|
70
89
|
/** Type-safe helper — validates places/marking at the type level */
|
|
71
90
|
declare function defineSkillNet<Place extends string>(net: SkillNet<Place>): SkillNet<Place>;
|
|
72
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Generate a user-facing block reason from a net's rule metadata.
|
|
94
|
+
*
|
|
95
|
+
* With metadata (from the rules compiler), returns a constraint-stating message:
|
|
96
|
+
* - sequence: "deploy requires a successful call to test first."
|
|
97
|
+
* - limit: "deploy has reached its limit of 3 calls per session."
|
|
98
|
+
* - block: "rm is blocked and cannot be called."
|
|
99
|
+
* - approval: "deploy requires human approval."
|
|
100
|
+
*
|
|
101
|
+
* Without metadata (hand-built nets), falls back to a generic message.
|
|
102
|
+
*/
|
|
103
|
+
declare function formatBlockReason(net: SkillNet<string>, resolvedTool: string): string;
|
|
104
|
+
|
|
73
105
|
/** Generic tool call event — framework-agnostic */
|
|
74
106
|
type GateToolCall = {
|
|
75
107
|
toolCallId: string;
|
|
@@ -206,4 +238,4 @@ type GateManagerOptions = {
|
|
|
206
238
|
};
|
|
207
239
|
declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
|
|
208
240
|
|
|
209
|
-
export { type ComposeConfig, type GateContext, type GateDecision, type GateManager, type GateManagerOptions, type GateState, type GateToolCall, type GateToolResult, type GatedTransition, type NetVerdict, type SkillNet, type ToolEvent, autoAdvance, classifyNets, composedToolCall, createGateManager, createGateState, defineSkillNet, formatMarking, getEnabledToolTransitions, handleToolCall, handleToolResult, resolveTool };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,24 @@ function defineSkillNet(net) {
|
|
|
3
3
|
return net;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
+
// src/format.ts
|
|
7
|
+
function formatBlockReason(net, resolvedTool) {
|
|
8
|
+
const meta = net.ruleMetadata;
|
|
9
|
+
if (meta) {
|
|
10
|
+
switch (meta.kind) {
|
|
11
|
+
case "sequence":
|
|
12
|
+
return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;
|
|
13
|
+
case "limit":
|
|
14
|
+
return meta.scope === "session" ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.` : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;
|
|
15
|
+
case "block":
|
|
16
|
+
return `${meta.tool} is blocked and cannot be called.`;
|
|
17
|
+
case "approval":
|
|
18
|
+
return `${meta.tool} requires human approval.`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return `Tool '${resolvedTool}' is not available in the current state.`;
|
|
22
|
+
}
|
|
23
|
+
|
|
6
24
|
// src/advance.ts
|
|
7
25
|
import { canFire, fire } from "@petriflow/engine";
|
|
8
26
|
function isStructural(t) {
|
|
@@ -73,7 +91,7 @@ async function handleToolCall(event, ctx, net, state) {
|
|
|
73
91
|
if (matching.length === 0) {
|
|
74
92
|
return {
|
|
75
93
|
block: true,
|
|
76
|
-
reason:
|
|
94
|
+
reason: formatBlockReason(net, resolvedTool)
|
|
77
95
|
};
|
|
78
96
|
}
|
|
79
97
|
const transition = matching[0];
|
|
@@ -88,14 +106,16 @@ async function handleToolCall(event, ctx, net, state) {
|
|
|
88
106
|
}
|
|
89
107
|
if (transition.type === "manual") {
|
|
90
108
|
if (!ctx.hasUI) {
|
|
91
|
-
|
|
109
|
+
const meta = net.ruleMetadata;
|
|
110
|
+
const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${resolvedTool} requires human approval.`;
|
|
111
|
+
return { block: true, reason };
|
|
92
112
|
}
|
|
93
113
|
const approved = await ctx.confirm(
|
|
94
114
|
`Approve: ${transition.name}`,
|
|
95
115
|
`Allow '${resolvedTool}' via transition '${transition.name}'?`
|
|
96
116
|
);
|
|
97
117
|
if (!approved) {
|
|
98
|
-
return { block: true, reason:
|
|
118
|
+
return { block: true, reason: `${resolvedTool} was rejected by human review.` };
|
|
99
119
|
}
|
|
100
120
|
}
|
|
101
121
|
if (transition.deferred) {
|
|
@@ -155,7 +175,7 @@ function classifyNets(nets, states, event) {
|
|
|
155
175
|
return {
|
|
156
176
|
...base,
|
|
157
177
|
kind: "blocked",
|
|
158
|
-
reason:
|
|
178
|
+
reason: formatBlockReason(net, resolvedTool)
|
|
159
179
|
};
|
|
160
180
|
}
|
|
161
181
|
return { ...base, kind: "gated", transition: matching[0] };
|
|
@@ -181,10 +201,9 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
181
201
|
for (const v of gated) {
|
|
182
202
|
if (v.transition.type === "manual") {
|
|
183
203
|
if (!ctx.hasUI) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
};
|
|
204
|
+
const meta = v.net.ruleMetadata;
|
|
205
|
+
const reason = meta?.kind === "approval" ? `${meta.tool} requires human approval.` : `${v.resolvedTool} requires human approval.`;
|
|
206
|
+
return { block: true, reason };
|
|
188
207
|
}
|
|
189
208
|
const approved = await ctx.confirm(
|
|
190
209
|
`Approve: ${v.transition.name} (${v.net.name})`,
|
|
@@ -193,7 +212,7 @@ async function composedToolCall(getNets, getStates, event, ctx) {
|
|
|
193
212
|
if (!approved) {
|
|
194
213
|
return {
|
|
195
214
|
block: true,
|
|
196
|
-
reason:
|
|
215
|
+
reason: `${v.resolvedTool} was rejected by human review.`
|
|
197
216
|
};
|
|
198
217
|
}
|
|
199
218
|
}
|
|
@@ -365,6 +384,7 @@ export {
|
|
|
365
384
|
createGateManager,
|
|
366
385
|
createGateState,
|
|
367
386
|
defineSkillNet,
|
|
387
|
+
formatBlockReason,
|
|
368
388
|
formatMarking,
|
|
369
389
|
getEnabledToolTransitions,
|
|
370
390
|
handleToolCall,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["import type { Marking } from \"@petriflow/engine\";\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `Human rejected '${transition.name}'` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n formatMarking,\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n return {\n block: true,\n reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`,\n };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `[${v.net.name}] Human rejected '${v.transition.name}'`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport type { GateState } from \"./gate.js\";\nimport {\n createGateState,\n formatMarking,\n getEnabledToolTransitions,\n handleToolResult as handleToolResultSingle,\n} from \"./gate.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { composedToolCall } from \"./compose.js\";\nimport type { ComposeConfig } from \"./compose.js\";\n\nexport type GateManager = {\n handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;\n handleToolResult: (event: GateToolResult) => void;\n addNet: (name: string) => { ok: boolean; message: string };\n removeNet: (name: string) => { ok: boolean; message: string };\n getActiveNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string> }>;\n getAllNets: () => Array<{ name: string; net: SkillNet<string>; state: GateState<string>; active: boolean }>;\n formatStatus: () => string;\n formatSystemPrompt: () => string;\n isDynamic: boolean;\n};\n\nexport type GateManagerOptions = {\n /** \"enforce\" blocks disallowed tools. \"shadow\" logs but never blocks. */\n mode: \"enforce\" | \"shadow\";\n /** Called after every gating decision. Use for logging, metrics, debugging. */\n onDecision?: (event: GateToolCall, decision: GateDecision) => void;\n};\n\nexport function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager {\n const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);\n\n if (opts) {\n const original = manager.handleToolCall;\n manager.handleToolCall = async (event, ctx) => {\n const decision = await original.call(manager, event, ctx);\n opts.onDecision?.(event, decision);\n if (opts.mode === \"shadow\" && decision?.block) {\n return undefined;\n }\n return decision;\n };\n }\n\n return manager;\n}\n\nfunction createArrayManager(nets: SkillNet<string>[]): GateManager {\n const states = nets.map((net) =>\n createGateState(autoAdvance(net, { ...net.initialMarking })),\n );\n\n const getNets = () => nets;\n const getStates = () => states;\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getNets, getStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (let i = 0; i < nets.length; i++) {\n handleToolResultSingle(event, nets[i]!, states[i]!);\n }\n },\n\n addNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n removeNet() {\n return { ok: false, message: \"Static composition does not support dynamic nets\" };\n },\n\n getActiveNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]! }));\n },\n\n getAllNets() {\n return nets.map((net, i) => ({ name: net.name, net, state: states[i]!, active: true }));\n },\n\n formatStatus() {\n return nets\n .map((n, i) => `${n.name}: ${formatMarking(states[i]!.marking)}`)\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(nets, states);\n },\n\n isDynamic: false,\n };\n}\n\nfunction createRegistryManager(config: ComposeConfig): GateManager {\n const registry = new Map<string, { net: SkillNet<string>; state: GateState<string> }>();\n for (const [name, net] of Object.entries(config.registry)) {\n registry.set(name, {\n net,\n state: createGateState(autoAdvance(net, { ...net.initialMarking })),\n });\n }\n\n const activeNames = new Set<string>(config.active ?? Object.keys(config.registry));\n\n const getActiveNets = () => [...activeNames].map((n) => registry.get(n)!.net);\n const getActiveStates = () => [...activeNames].map((n) => registry.get(n)!.state);\n\n return {\n handleToolCall(event, ctx) {\n return composedToolCall(getActiveNets, getActiveStates, event, ctx);\n },\n\n handleToolResult(event) {\n for (const { net, state } of registry.values()) {\n handleToolResultSingle(event, net, state);\n }\n },\n\n addNet(name) {\n if (!registry.has(name)) {\n return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(\", \")}` };\n }\n if (activeNames.has(name)) {\n return { ok: false, message: `'${name}' is already active` };\n }\n activeNames.add(name);\n return { ok: true, message: `Activated '${name}'` };\n },\n\n removeNet(name) {\n if (!activeNames.has(name)) {\n return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(\", \")}` };\n }\n activeNames.delete(name);\n return { ok: true, message: `Deactivated '${name}' (state preserved)` };\n },\n\n getActiveNets() {\n return [...activeNames].map((name) => {\n const entry = registry.get(name)!;\n return { name, net: entry.net, state: entry.state };\n });\n },\n\n getAllNets() {\n return [...registry.entries()].map(([name, { net, state }]) => ({\n name,\n net,\n state,\n active: activeNames.has(name),\n }));\n },\n\n formatStatus() {\n return [...registry.entries()]\n .map(([name, { state }]) => {\n const status = activeNames.has(name) ? \"active\" : \"inactive\";\n return `${name} (${status}): ${formatMarking(state.marking)}`;\n })\n .join(\"\\n\");\n },\n\n formatSystemPrompt() {\n return formatPromptForNets(getActiveNets(), getActiveStates());\n },\n\n isDynamic: true,\n };\n}\n\nfunction formatPromptForNets(nets: SkillNet<string>[], states: GateState<string>[]): string {\n const sections = nets.map((net, i) => {\n const enabled = getEnabledToolTransitions(net, states[i]!.marking);\n const toolList = enabled.flatMap((t) => t.tools ?? []);\n return `### ${net.name}\\nAvailable gated tools: ${toolList.join(\", \") || \"none\"}\\nFree tools: ${net.freeTools.join(\", \")}\\nState: ${formatMarking(states[i]!.marking)}`;\n });\n return `## Active Petri Nets (composed)\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"mappings":"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","names":["canFire","fire","canFire","fire","fire","fire"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/format.ts","../src/advance.ts","../src/gate.ts","../src/compose.ts","../src/manager.ts"],"sourcesContent":["import type { Marking } from \"@petriflow/engine\";\n\n/** Structured metadata for generating user-facing block messages */\nexport type RuleMetadata =\n | { kind: \"sequence\"; prerequisite: string; dependent: string }\n | { kind: \"approval\"; tool: string }\n | { kind: \"block\"; tool: string }\n | { kind: \"limit\"; tool: string; limit: number; scope: \"session\" | string };\n\n/** A transition that optionally gates tool access */\nexport type GatedTransition<Place extends string> = {\n name: string;\n type: \"auto\" | \"manual\";\n inputs: Place[];\n outputs: Place[];\n guard?: string | null;\n tools?: string[];\n /**\n * When true, the transition allows the tool call immediately but\n * only fires (consumes/produces tokens) when the tool_result\n * comes back successfully (isError === false).\n * Use this for transitions where the tool must succeed before\n * the net advances (e.g. backup must succeed before delete unlocks).\n */\n deferred?: boolean;\n};\n\n/** Minimal tool event shape for toolMapper */\nexport type ToolEvent = { toolName: string; input: Record<string, unknown> };\n\n/** A Petri net that gates a skill's tool access */\nexport type SkillNet<Place extends string> = {\n name: string;\n places: Place[];\n transitions: GatedTransition<Place>[];\n initialMarking: Marking<Place>;\n terminalPlaces: Place[];\n freeTools: string[];\n /**\n * Maps a tool call to a virtual tool name before gating.\n * Use this to split one tool (e.g. \"bash\") into multiple gated\n * variants (e.g. \"bash\", \"git-commit\", \"git-push\") based on input.\n * If not provided, event.toolName is used as-is.\n */\n toolMapper?: (event: ToolEvent) => string;\n /**\n * Additional validation before a gated tool call is allowed.\n * Called after the net confirms a matching transition exists.\n * Return { block, reason } to reject, or void to allow.\n * Use this for domain-specific checks (e.g. path coverage).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n validateToolCall?(\n event: ToolEvent,\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): { block: true; reason: string } | void;\n /**\n * Called when a deferred transition's tool_result arrives.\n * Use this to record metadata (e.g. backed-up paths).\n *\n * Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.\n */\n onDeferredResult?(\n event: { toolCallId: string; input: Record<string, unknown>; isError: boolean },\n resolvedTool: string,\n transition: GatedTransition<Place>,\n state: { marking: Marking<Place>; meta: Record<string, unknown> },\n ): void;\n /** Structured rule metadata for generating constraint-stating block messages */\n ruleMetadata?: RuleMetadata;\n};\n\n/** Type-safe helper — validates places/marking at the type level */\nexport function defineSkillNet<Place extends string>(\n net: SkillNet<Place>,\n): SkillNet<Place> {\n return net;\n}\n","import type { SkillNet } from \"./types.js\";\n\n/**\n * Generate a user-facing block reason from a net's rule metadata.\n *\n * With metadata (from the rules compiler), returns a constraint-stating message:\n * - sequence: \"deploy requires a successful call to test first.\"\n * - limit: \"deploy has reached its limit of 3 calls per session.\"\n * - block: \"rm is blocked and cannot be called.\"\n * - approval: \"deploy requires human approval.\"\n *\n * Without metadata (hand-built nets), falls back to a generic message.\n */\nexport function formatBlockReason(\n net: SkillNet<string>,\n resolvedTool: string,\n): string {\n const meta = net.ruleMetadata;\n\n if (meta) {\n switch (meta.kind) {\n case \"sequence\":\n return `${meta.dependent} requires a successful call to ${meta.prerequisite} first.`;\n case \"limit\":\n return meta.scope === \"session\"\n ? `${meta.tool} has reached its limit of ${meta.limit} calls per session.`\n : `${meta.tool} has reached its limit of ${meta.limit} calls per ${meta.scope}.`;\n case \"block\":\n return `${meta.tool} is blocked and cannot be called.`;\n case \"approval\":\n return `${meta.tool} requires human approval.`;\n }\n }\n\n return `Tool '${resolvedTool}' is not available in the current state.`;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\n\n/** A structural transition: type=auto, no tools property */\nfunction isStructural<P extends string>(t: GatedTransition<P>): boolean {\n return t.type === \"auto\" && (t.tools === undefined || t.tools.length === 0);\n}\n\n/**\n * Check if two transitions compete for the same input token.\n * Two transitions conflict if they share an input place and the\n * marking doesn't have enough tokens for both.\n */\nfunction hasInputConflict<P extends string>(\n a: GatedTransition<P>,\n b: GatedTransition<P>,\n marking: Marking<P>,\n): boolean {\n for (const place of a.inputs) {\n if (b.inputs.includes(place)) {\n // Count how many tokens each needs from this place\n const aNeeds = a.inputs.filter((p) => p === place).length;\n const bNeeds = b.inputs.filter((p) => p === place).length;\n if ((marking[place] ?? 0) < aNeeds + bNeeds) return true;\n }\n }\n return false;\n}\n\n/**\n * Auto-advance: fire all enabled structural transitions (type=auto,\n * no tools) in a loop until quiescent.\n *\n * When multiple structural transitions compete for the same input\n * token, none of them fire (avoids ambiguous choices).\n */\nexport function autoAdvance<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): Marking<P> {\n let current = { ...marking };\n\n for (;;) {\n const structural = net.transitions.filter(\n (t) => isStructural(t) && canFire(current, t),\n );\n if (structural.length === 0) break;\n\n // Filter out transitions that conflict with another enabled one\n const unambiguous = structural.filter((t) =>\n structural.every((other) => other === t || !hasInputConflict(t, other, current)),\n );\n if (unambiguous.length === 0) break;\n\n for (const t of unambiguous) {\n // Re-check enablement — earlier firings in this batch may have consumed tokens\n if (canFire(current, t)) {\n current = fire(current, t);\n }\n }\n }\n\n return current;\n}\n","import { canFire, fire } from \"@petriflow/engine\";\nimport type { Marking } from \"@petriflow/engine\";\nimport type { GateToolCall, GateToolResult, GateContext, GateDecision } from \"./events.js\";\nimport type { GatedTransition, SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport { formatBlockReason } from \"./format.js\";\n\n/** Resolve the virtual tool name for a tool call event */\nexport function resolveTool<P extends string>(\n net: SkillNet<P>,\n event: { toolName: string; input: Record<string, unknown> },\n): string {\n if (net.toolMapper) {\n return net.toolMapper({\n toolName: event.toolName,\n input: event.input,\n });\n }\n return event.toolName;\n}\n\n/** Return transitions that are structurally enabled and have a tools list */\nfunction enabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return net.transitions.filter(\n (t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t),\n );\n}\n\n/** Public: get tool transitions the agent can currently use */\nexport function getEnabledToolTransitions<P extends string>(\n net: SkillNet<P>,\n marking: Marking<P>,\n): GatedTransition<P>[] {\n return enabledToolTransitions(net, marking);\n}\n\n/** Format marking for display */\nexport function formatMarking<P extends string>(marking: Marking<P>): string {\n return Object.entries(marking)\n .filter(([, v]) => (v as number) > 0)\n .map(([k, v]) => `${k}:${v}`)\n .join(\", \");\n}\n\n/** A pending deferred transition awaiting tool_result */\ntype PendingDeferred<P extends string> = {\n toolCallId: string;\n transition: GatedTransition<P>;\n resolvedTool: string;\n};\n\nexport type GateState<P extends string> = {\n marking: Marking<P>;\n /** Skill-specific metadata (e.g. backed-up paths) */\n meta: Record<string, unknown>;\n /** Deferred transitions waiting for tool_result */\n pending: Map<string, PendingDeferred<P>>;\n};\n\nexport function createGateState<P extends string>(marking: Marking<P>): GateState<P> {\n return { marking, meta: {}, pending: new Map() };\n}\n\n/**\n * Core gating logic for a tool_call event.\n * Mutates state.marking when a non-deferred transition fires.\n * For deferred transitions, records pending and fires on tool_result.\n */\nexport async function handleToolCall<P extends string>(\n event: GateToolCall,\n ctx: GateContext,\n net: SkillNet<P>,\n state: GateState<P>,\n): Promise<GateDecision> {\n const resolvedTool = resolveTool(net, event);\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return undefined;\n }\n\n const enabled = enabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n block: true,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\n };\n }\n\n const transition = matching[0]!;\n\n // Skill-specific validation (e.g. path coverage)\n if (net.validateToolCall) {\n const rejection = net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n resolvedTool,\n transition,\n state,\n );\n if (rejection) return rejection;\n }\n\n if (transition.type === \"manual\") {\n if (!ctx.hasUI) {\n const meta = net.ruleMetadata;\n const reason = meta?.kind === \"approval\"\n ? `${meta.tool} requires human approval.`\n : `${resolvedTool} requires human approval.`;\n return { block: true, reason };\n }\n const approved = await ctx.confirm(\n `Approve: ${transition.name}`,\n `Allow '${resolvedTool}' via transition '${transition.name}'?`,\n );\n if (!approved) {\n return { block: true, reason: `${resolvedTool} was rejected by human review.` };\n }\n }\n\n if (transition.deferred) {\n // Allow the tool call but don't fire yet — wait for tool_result\n state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });\n return undefined;\n }\n\n // Fire immediately\n state.marking = fire(state.marking, transition);\n state.marking = autoAdvance(net, state.marking);\n\n return undefined;\n}\n\n/**\n * Handle a tool_result event. Fires deferred transitions on success.\n * Returns void (tool_result handler doesn't block).\n */\nexport function handleToolResult<P extends string>(\n event: GateToolResult,\n net: SkillNet<P>,\n state: GateState<P>,\n): void {\n const pending = state.pending.get(event.toolCallId);\n if (!pending) return;\n\n state.pending.delete(event.toolCallId);\n\n if (event.isError) {\n // Tool failed — don't fire the transition, marking unchanged\n return;\n }\n\n // Tool succeeded — fire the deferred transition\n if (canFire(state.marking, pending.transition)) {\n state.marking = fire(state.marking, pending.transition);\n\n // Notify the skill of the successful deferred result\n if (net.onDeferredResult) {\n net.onDeferredResult(\n {\n toolCallId: event.toolCallId,\n input: event.input,\n isError: event.isError,\n },\n pending.resolvedTool,\n pending.transition,\n state,\n );\n }\n\n state.marking = autoAdvance(net, state.marking);\n }\n}\n","import { fire } from \"@petriflow/engine\";\nimport type { GateToolCall, GateContext, GateDecision } from \"./events.js\";\nimport type { SkillNet } from \"./types.js\";\nimport { autoAdvance } from \"./advance.js\";\nimport {\n getEnabledToolTransitions,\n resolveTool,\n} from \"./gate.js\";\nimport type { GateState } from \"./gate.js\";\nimport { formatBlockReason } from \"./format.js\";\n\n/** Classification of a net's opinion on a tool call */\nexport type NetVerdict<P extends string> = {\n net: SkillNet<P>;\n state: GateState<P>;\n resolvedTool: string;\n} & (\n | { kind: \"free\" }\n | { kind: \"abstain\" }\n | { kind: \"blocked\"; reason: string }\n | { kind: \"gated\"; transition: SkillNet<P>[\"transitions\"][number] }\n);\n\n/** Registry-based config for dynamic net management */\nexport type ComposeConfig = {\n registry: Record<string, SkillNet<string>>;\n active?: string[];\n};\n\n/**\n * Check if a net has jurisdiction over a tool — i.e. the tool appears\n * in at least one transition's tools list (enabled or not).\n */\nfunction hasJurisdiction<P extends string>(\n net: SkillNet<P>,\n resolvedTool: string,\n): boolean {\n return net.transitions.some(\n (t) => t.tools !== undefined && t.tools.includes(resolvedTool),\n );\n}\n\n/**\n * Phase 1 — Structural check (non-mutating).\n * Classify each net as free, gated, blocked, or abstain.\n */\nexport function classifyNets<P extends string>(\n nets: SkillNet<P>[],\n states: GateState<P>[],\n event: { toolName: string; input: Record<string, unknown> },\n): NetVerdict<P>[] {\n return nets.map((net, i) => {\n const state = states[i]!;\n const resolvedTool = resolveTool(net, event);\n const base = { net, state, resolvedTool };\n\n // Free tools always pass\n if (net.freeTools.includes(resolvedTool)) {\n return { ...base, kind: \"free\" as const };\n }\n\n // No jurisdiction → abstain\n if (!hasJurisdiction(net, resolvedTool)) {\n return { ...base, kind: \"abstain\" as const };\n }\n\n // Has jurisdiction — check enabled transitions\n const enabled = getEnabledToolTransitions(net, state.marking);\n const matching = enabled.filter((t) => t.tools!.includes(resolvedTool));\n\n if (matching.length === 0) {\n return {\n ...base,\n kind: \"blocked\" as const,\n reason: formatBlockReason(net as SkillNet<string>, resolvedTool),\n };\n }\n\n return { ...base, kind: \"gated\" as const, transition: matching[0]! };\n });\n}\n\n/**\n * 4-phase tool call handler for composed nets.\n */\nexport async function composedToolCall(\n getNets: () => SkillNet<string>[],\n getStates: () => GateState<string>[],\n event: GateToolCall,\n ctx: GateContext,\n): Promise<GateDecision> {\n const nets = getNets();\n const states = getStates();\n\n // --- Phase 1: Structural check ---\n const verdicts = classifyNets(nets, states, {\n toolName: event.toolName,\n input: event.input,\n });\n\n // If any net blocks, reject immediately\n const blocked = verdicts.find((v) => v.kind === \"blocked\");\n if (blocked) {\n return { block: true, reason: blocked.reason };\n }\n\n const gated = verdicts.filter(\n (v): v is Extract<NetVerdict<string>, { kind: \"gated\" }> => v.kind === \"gated\",\n );\n\n // No gated nets → all free/abstain → allow\n if (gated.length === 0) {\n return undefined;\n }\n\n // --- Phase 2: Manual approvals ---\n for (const v of gated) {\n if (v.transition.type === \"manual\") {\n if (!ctx.hasUI) {\n const meta = v.net.ruleMetadata;\n const reason = meta?.kind === \"approval\"\n ? `${meta.tool} requires human approval.`\n : `${v.resolvedTool} requires human approval.`;\n return { block: true, reason };\n }\n const approved = await ctx.confirm(\n `Approve: ${v.transition.name} (${v.net.name})`,\n `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`,\n );\n if (!approved) {\n return {\n block: true,\n reason: `${v.resolvedTool} was rejected by human review.`,\n };\n }\n }\n }\n\n // --- Phase 3: Semantic validation with meta rollback ---\n // Snapshot all meta for rollback\n const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));\n\n for (let i = 0; i < gated.length; i++) {\n const v = gated[i]!;\n if (v.net.validateToolCall) {\n const rejection = v.net.validateToolCall(\n { toolName: event.toolName, input: event.input },\n v.resolvedTool,\n v.transition,\n v.state,\n );\n if (rejection) {\n // Rollback all meta that may have been mutated by earlier validates\n for (let j = 0; j < i; j++) {\n gated[j]!.state.meta = metaSnapshots[j]!;\n }\n return rejection;\n }\n }\n }\n\n // --- Phase 4: Commit ---\n for (const v of gated) {\n if (v.transition.deferred) {\n v.state.pending.set(event.toolCallId, {\n toolCallId: event.toolCallId,\n transition: v.transition,\n resolvedTool: v.resolvedTool,\n });\n } else {\n v.state.marking = fire(v.state.marking, v.transition);\n v.state.marking = autoAdvance(v.net, v.state.marking);\n }\n }\n\n return undefined;\n}\n","import 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"]}
|