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