@petriflow/gate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -0
- package/dist/advance.d.ts +11 -0
- package/dist/advance.d.ts.map +1 -0
- package/dist/advance.js +49 -0
- package/dist/advance.js.map +1 -0
- package/dist/compose.d.ts +37 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +112 -0
- package/dist/compose.js.map +1 -0
- package/dist/events.d.ts +24 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/gate.d.ts +39 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +103 -0
- package/dist/gate.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +38 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +128 -0
- package/dist/manager.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# @petriflow/gate
|
|
2
|
+
|
|
3
|
+
Framework-agnostic Petri net gating for AI agent tool access control. Define safety constraints as Petri nets — tools are only allowed when an enabled transition permits them.
|
|
4
|
+
|
|
5
|
+
Built on [`@petriflow/engine`](../engine). Used by [`@petriflow/pi-extension`](../pi-extension) (pi-mono) and [`@petriflow/openclaw`](../openclaw) (OpenClaw).
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
LLM agents need guardrails, but hardcoded allow/deny lists are too rigid and per-call confirmation is too noisy. Petri nets let you express **stateful** safety constraints: "allow delete only after a successful backup", "allow push only after commit", "allow sending a message only after reading the channel".
|
|
10
|
+
|
|
11
|
+
This package provides the core gating logic with no framework dependencies — adapter packages wire it into specific agent runtimes.
|
|
12
|
+
|
|
13
|
+
## Defining a skill net
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineSkillNet } from "@petriflow/gate";
|
|
17
|
+
|
|
18
|
+
const toolApproval = defineSkillNet({
|
|
19
|
+
name: "tool-approval",
|
|
20
|
+
places: ["idle", "ready"],
|
|
21
|
+
terminalPlaces: [],
|
|
22
|
+
freeTools: ["ls", "read", "grep", "find"], // Always allowed
|
|
23
|
+
initialMarking: { idle: 1, ready: 0 },
|
|
24
|
+
transitions: [
|
|
25
|
+
{ name: "start", type: "auto", inputs: ["idle"], outputs: ["ready"] },
|
|
26
|
+
{ name: "execShell", type: "manual", inputs: ["ready"], outputs: ["ready"], tools: ["bash"] },
|
|
27
|
+
{ name: "execWrite", type: "manual", inputs: ["ready"], outputs: ["ready"], tools: ["write", "edit"] },
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Key concepts
|
|
33
|
+
|
|
34
|
+
### Transition types
|
|
35
|
+
|
|
36
|
+
- **`auto`** — fires immediately when the tool is called and the transition is enabled
|
|
37
|
+
- **`manual`** — requires human approval via `ctx.confirm()` before firing
|
|
38
|
+
|
|
39
|
+
### Free tools
|
|
40
|
+
|
|
41
|
+
Tools listed in `freeTools` are always allowed regardless of net state. Use this for read-only, side-effect-free tools.
|
|
42
|
+
|
|
43
|
+
### Tool mapping
|
|
44
|
+
|
|
45
|
+
Split one physical tool into multiple virtual tools based on input content:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
const net = defineSkillNet({
|
|
49
|
+
// ...
|
|
50
|
+
toolMapper: (event) => {
|
|
51
|
+
if (event.toolName !== "bash") return event.toolName;
|
|
52
|
+
const cmd = event.input.command as string;
|
|
53
|
+
if (/\bgit\s+commit\b/.test(cmd)) return "git-commit";
|
|
54
|
+
if (/\bgit\s+push\b/.test(cmd)) return "git-push";
|
|
55
|
+
return "bash";
|
|
56
|
+
},
|
|
57
|
+
freeTools: ["bash"], // Plain bash is free
|
|
58
|
+
transitions: [
|
|
59
|
+
{ name: "commit", type: "manual", inputs: ["working"], outputs: ["committed"], tools: ["git-commit"] },
|
|
60
|
+
{ name: "push", type: "manual", inputs: ["committed"], outputs: ["working"], tools: ["git-push"] },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Deferred transitions
|
|
66
|
+
|
|
67
|
+
Allow the tool call immediately but only advance the net when the tool succeeds:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
{
|
|
71
|
+
name: "backup",
|
|
72
|
+
type: "auto",
|
|
73
|
+
inputs: ["ready"],
|
|
74
|
+
outputs: ["backedUp"],
|
|
75
|
+
tools: ["backup"],
|
|
76
|
+
deferred: true, // Fires on successful tool_result, not tool_call
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
If the tool fails (`isError: true`), the transition doesn't fire and the marking stays unchanged.
|
|
81
|
+
|
|
82
|
+
### Semantic validation
|
|
83
|
+
|
|
84
|
+
Add domain-specific checks beyond what net structure alone enforces:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const net = defineSkillNet({
|
|
88
|
+
// ...
|
|
89
|
+
validateToolCall: (event, resolvedTool, transition, state) => {
|
|
90
|
+
if (resolvedTool === "destructive") {
|
|
91
|
+
const target = extractTarget(event.input);
|
|
92
|
+
const covered = state.meta.backedUpPaths.some(p => covers(p, target));
|
|
93
|
+
if (!covered) return { block: true, reason: `Target '${target}' not backed up` };
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
onDeferredResult: (event, resolvedTool, transition, state) => {
|
|
97
|
+
// Record metadata when a deferred transition resolves
|
|
98
|
+
state.meta.backedUpPaths.push(extractPath(event.input));
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Using the gate
|
|
104
|
+
|
|
105
|
+
### Single net (low-level)
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { handleToolCall, handleToolResult, createGateState, autoAdvance } from "@petriflow/gate";
|
|
109
|
+
|
|
110
|
+
const state = createGateState(autoAdvance(net, { ...net.initialMarking }));
|
|
111
|
+
|
|
112
|
+
const decision = await handleToolCall(
|
|
113
|
+
{ toolCallId: "1", toolName: "bash", input: { command: "rm -rf build/" } },
|
|
114
|
+
{ hasUI: true, confirm: async (title, msg) => window.confirm(msg) },
|
|
115
|
+
net,
|
|
116
|
+
state,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
if (decision?.block) {
|
|
120
|
+
console.log(`Blocked: ${decision.reason}`);
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Multi-net composition (GateManager)
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { createGateManager } from "@petriflow/gate";
|
|
128
|
+
|
|
129
|
+
// Static — all nets always active
|
|
130
|
+
const manager = createGateManager([netA, netB]);
|
|
131
|
+
|
|
132
|
+
// Registry — dynamic activation/deactivation
|
|
133
|
+
const manager = createGateManager({
|
|
134
|
+
registry: { netA, netB, netC },
|
|
135
|
+
active: ["netA"],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const decision = await manager.handleToolCall(event, ctx);
|
|
139
|
+
manager.handleToolResult(resultEvent);
|
|
140
|
+
manager.addNet("netB"); // Registry mode only
|
|
141
|
+
manager.removeNet("netA"); // Registry mode only
|
|
142
|
+
manager.formatStatus(); // "netA (active): ready:1\nnetB (inactive): idle:1"
|
|
143
|
+
manager.formatSystemPrompt(); // Markdown for LLM context
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Composition semantics
|
|
147
|
+
|
|
148
|
+
When multiple nets are composed, each net independently classifies a tool call:
|
|
149
|
+
|
|
150
|
+
| Verdict | Meaning |
|
|
151
|
+
|---|---|
|
|
152
|
+
| **free** | Tool is in the net's `freeTools` — always allowed |
|
|
153
|
+
| **abstain** | Tool doesn't appear in any of the net's transitions — no opinion |
|
|
154
|
+
| **gated** | An enabled transition covers this tool — allowed (pending approval/validation) |
|
|
155
|
+
| **blocked** | The net has jurisdiction but no enabled transition — rejected |
|
|
156
|
+
|
|
157
|
+
One **blocked** verdict from any net rejects the call. If no net blocks, **gated** nets fire their transitions. If all nets are **free** or **abstain**, the call passes through.
|
|
158
|
+
|
|
159
|
+
## API
|
|
160
|
+
|
|
161
|
+
| Export | Description |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `defineSkillNet(config)` | Type-safe skill net constructor |
|
|
164
|
+
| `createGateManager(input)` | Multi-net manager (array or registry config) |
|
|
165
|
+
| `handleToolCall(event, ctx, net, state)` | Single-net tool call gating |
|
|
166
|
+
| `handleToolResult(event, net, state)` | Single-net deferred resolution |
|
|
167
|
+
| `autoAdvance(net, marking)` | Fire structural (non-tool) auto transitions |
|
|
168
|
+
| `createGateState(marking)` | Initialize gate state with marking |
|
|
169
|
+
| `classifyNets(nets, states, event)` | Phase 1 structural check (non-mutating) |
|
|
170
|
+
| `composedToolCall(getNets, getStates, event, ctx)` | Full 4-phase composed gating |
|
|
171
|
+
| `formatMarking(marking)` | Format marking for display (`"ready:1, working:0"`) |
|
|
172
|
+
| `getEnabledToolTransitions(net, marking)` | List currently available tool transitions |
|
|
173
|
+
| `resolveTool(net, event)` | Apply tool mapper |
|
|
174
|
+
|
|
175
|
+
## Tests
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
bun test packages/gate
|
|
179
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Marking } from "@petriflow/engine";
|
|
2
|
+
import type { SkillNet } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Auto-advance: fire all enabled structural transitions (type=auto,
|
|
5
|
+
* no tools) in a loop until quiescent.
|
|
6
|
+
*
|
|
7
|
+
* When multiple structural transitions compete for the same input
|
|
8
|
+
* token, none of them fire (avoids ambiguous choices).
|
|
9
|
+
*/
|
|
10
|
+
export declare function autoAdvance<P extends string>(net: SkillNet<P>, marking: Marking<P>): Marking<P>;
|
|
11
|
+
//# sourceMappingURL=advance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advance.d.ts","sourceRoot":"","sources":["../src/advance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,YAAY,CAAC;AA4B5D;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAChB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,CAAC,CAAC,CAwBZ"}
|
package/dist/advance.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { canFire, fire } from "@petriflow/engine";
|
|
2
|
+
/** A structural transition: type=auto, no tools property */
|
|
3
|
+
function isStructural(t) {
|
|
4
|
+
return t.type === "auto" && (t.tools === undefined || t.tools.length === 0);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Check if two transitions compete for the same input token.
|
|
8
|
+
* Two transitions conflict if they share an input place and the
|
|
9
|
+
* marking doesn't have enough tokens for both.
|
|
10
|
+
*/
|
|
11
|
+
function hasInputConflict(a, b, marking) {
|
|
12
|
+
for (const place of a.inputs) {
|
|
13
|
+
if (b.inputs.includes(place)) {
|
|
14
|
+
// Count how many tokens each needs from this place
|
|
15
|
+
const aNeeds = a.inputs.filter((p) => p === place).length;
|
|
16
|
+
const bNeeds = b.inputs.filter((p) => p === place).length;
|
|
17
|
+
if ((marking[place] ?? 0) < aNeeds + bNeeds)
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Auto-advance: fire all enabled structural transitions (type=auto,
|
|
25
|
+
* no tools) in a loop until quiescent.
|
|
26
|
+
*
|
|
27
|
+
* When multiple structural transitions compete for the same input
|
|
28
|
+
* token, none of them fire (avoids ambiguous choices).
|
|
29
|
+
*/
|
|
30
|
+
export function autoAdvance(net, marking) {
|
|
31
|
+
let current = { ...marking };
|
|
32
|
+
for (;;) {
|
|
33
|
+
const structural = net.transitions.filter((t) => isStructural(t) && canFire(current, t));
|
|
34
|
+
if (structural.length === 0)
|
|
35
|
+
break;
|
|
36
|
+
// Filter out transitions that conflict with another enabled one
|
|
37
|
+
const unambiguous = structural.filter((t) => structural.every((other) => other === t || !hasInputConflict(t, other, current)));
|
|
38
|
+
if (unambiguous.length === 0)
|
|
39
|
+
break;
|
|
40
|
+
for (const t of unambiguous) {
|
|
41
|
+
// Re-check enablement — earlier firings in this batch may have consumed tokens
|
|
42
|
+
if (canFire(current, t)) {
|
|
43
|
+
current = fire(current, t);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=advance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advance.js","sourceRoot":"","sources":["../src/advance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAIlD,4DAA4D;AAC5D,SAAS,YAAY,CAAmB,CAAqB;IAC3D,OAAO,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,CAAqB,EACrB,CAAqB,EACrB,OAAmB;IAEnB,KAAK,MAAM,KAAK,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,mDAAmD;YACnD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC;YAC1D,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,CAAC;YAC1D,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG,MAAM;gBAAE,OAAO,IAAI,CAAC;QAC3D,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,GAAgB,EAChB,OAAmB;IAEnB,IAAI,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAE7B,SAAS,CAAC;QACR,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CACvC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAC9C,CAAC;QACF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAEnC,gEAAgE;QAChE,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1C,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CACjF,CAAC;QACF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAEpC,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;YAC5B,+EAA+E;YAC/E,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;gBACxB,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { GateToolCall, GateContext, GateDecision } from "./events.js";
|
|
2
|
+
import type { SkillNet } from "./types.js";
|
|
3
|
+
import type { GateState } from "./gate.js";
|
|
4
|
+
/** Classification of a net's opinion on a tool call */
|
|
5
|
+
export type NetVerdict<P extends string> = {
|
|
6
|
+
net: SkillNet<P>;
|
|
7
|
+
state: GateState<P>;
|
|
8
|
+
resolvedTool: string;
|
|
9
|
+
} & ({
|
|
10
|
+
kind: "free";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "abstain";
|
|
13
|
+
} | {
|
|
14
|
+
kind: "blocked";
|
|
15
|
+
reason: string;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "gated";
|
|
18
|
+
transition: SkillNet<P>["transitions"][number];
|
|
19
|
+
});
|
|
20
|
+
/** Registry-based config for dynamic net management */
|
|
21
|
+
export type ComposeConfig = {
|
|
22
|
+
registry: Record<string, SkillNet<string>>;
|
|
23
|
+
active?: string[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Phase 1 — Structural check (non-mutating).
|
|
27
|
+
* Classify each net as free, gated, blocked, or abstain.
|
|
28
|
+
*/
|
|
29
|
+
export declare function classifyNets<P extends string>(nets: SkillNet<P>[], states: GateState<P>[], event: {
|
|
30
|
+
toolName: string;
|
|
31
|
+
input: Record<string, unknown>;
|
|
32
|
+
}): NetVerdict<P>[];
|
|
33
|
+
/**
|
|
34
|
+
* 4-phase tool call handler for composed nets.
|
|
35
|
+
*/
|
|
36
|
+
export declare function composedToolCall(getNets: () => SkillNet<string>[], getStates: () => GateState<string>[], event: GateToolCall, ctx: GateContext): Promise<GateDecision>;
|
|
37
|
+
//# sourceMappingURL=compose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,uDAAuD;AACvD,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI;IACzC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,CACA;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;CAAE,CACpE,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAeF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,EACnB,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,EACtB,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC1D,UAAU,CAAC,CAAC,CAAC,EAAE,CA8BjB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,EACjC,SAAS,EAAE,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,EACpC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,YAAY,CAAC,CAqFvB"}
|
package/dist/compose.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { fire } from "@petriflow/engine";
|
|
2
|
+
import { autoAdvance } from "./advance.js";
|
|
3
|
+
import { formatMarking, getEnabledToolTransitions, resolveTool, } from "./gate.js";
|
|
4
|
+
/**
|
|
5
|
+
* Check if a net has jurisdiction over a tool — i.e. the tool appears
|
|
6
|
+
* in at least one transition's tools list (enabled or not).
|
|
7
|
+
*/
|
|
8
|
+
function hasJurisdiction(net, resolvedTool) {
|
|
9
|
+
return net.transitions.some((t) => t.tools !== undefined && t.tools.includes(resolvedTool));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Phase 1 — Structural check (non-mutating).
|
|
13
|
+
* Classify each net as free, gated, blocked, or abstain.
|
|
14
|
+
*/
|
|
15
|
+
export function classifyNets(nets, states, event) {
|
|
16
|
+
return nets.map((net, i) => {
|
|
17
|
+
const state = states[i];
|
|
18
|
+
const resolvedTool = resolveTool(net, event);
|
|
19
|
+
const base = { net, state, resolvedTool };
|
|
20
|
+
// Free tools always pass
|
|
21
|
+
if (net.freeTools.includes(resolvedTool)) {
|
|
22
|
+
return { ...base, kind: "free" };
|
|
23
|
+
}
|
|
24
|
+
// No jurisdiction → abstain
|
|
25
|
+
if (!hasJurisdiction(net, resolvedTool)) {
|
|
26
|
+
return { ...base, kind: "abstain" };
|
|
27
|
+
}
|
|
28
|
+
// Has jurisdiction — check enabled transitions
|
|
29
|
+
const enabled = getEnabledToolTransitions(net, state.marking);
|
|
30
|
+
const matching = enabled.filter((t) => t.tools.includes(resolvedTool));
|
|
31
|
+
if (matching.length === 0) {
|
|
32
|
+
return {
|
|
33
|
+
...base,
|
|
34
|
+
kind: "blocked",
|
|
35
|
+
reason: `[${net.name}] Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return { ...base, kind: "gated", transition: matching[0] };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 4-phase tool call handler for composed nets.
|
|
43
|
+
*/
|
|
44
|
+
export async function composedToolCall(getNets, getStates, event, ctx) {
|
|
45
|
+
const nets = getNets();
|
|
46
|
+
const states = getStates();
|
|
47
|
+
// --- Phase 1: Structural check ---
|
|
48
|
+
const verdicts = classifyNets(nets, states, {
|
|
49
|
+
toolName: event.toolName,
|
|
50
|
+
input: event.input,
|
|
51
|
+
});
|
|
52
|
+
// If any net blocks, reject immediately
|
|
53
|
+
const blocked = verdicts.find((v) => v.kind === "blocked");
|
|
54
|
+
if (blocked) {
|
|
55
|
+
return { block: true, reason: blocked.reason };
|
|
56
|
+
}
|
|
57
|
+
const gated = verdicts.filter((v) => v.kind === "gated");
|
|
58
|
+
// No gated nets → all free/abstain → allow
|
|
59
|
+
if (gated.length === 0) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
// --- Phase 2: Manual approvals ---
|
|
63
|
+
for (const v of gated) {
|
|
64
|
+
if (v.transition.type === "manual") {
|
|
65
|
+
if (!ctx.hasUI) {
|
|
66
|
+
return {
|
|
67
|
+
block: true,
|
|
68
|
+
reason: `[${v.net.name}] Manual transition '${v.transition.name}' requires UI approval`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const approved = await ctx.confirm(`Approve: ${v.transition.name} (${v.net.name})`, `Allow '${v.resolvedTool}' via transition '${v.transition.name}' in net '${v.net.name}'?`);
|
|
72
|
+
if (!approved) {
|
|
73
|
+
return {
|
|
74
|
+
block: true,
|
|
75
|
+
reason: `[${v.net.name}] Human rejected '${v.transition.name}'`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// --- Phase 3: Semantic validation with meta rollback ---
|
|
81
|
+
// Snapshot all meta for rollback
|
|
82
|
+
const metaSnapshots = gated.map((v) => structuredClone(v.state.meta));
|
|
83
|
+
for (let i = 0; i < gated.length; i++) {
|
|
84
|
+
const v = gated[i];
|
|
85
|
+
if (v.net.validateToolCall) {
|
|
86
|
+
const rejection = v.net.validateToolCall({ toolName: event.toolName, input: event.input }, v.resolvedTool, v.transition, v.state);
|
|
87
|
+
if (rejection) {
|
|
88
|
+
// Rollback all meta that may have been mutated by earlier validates
|
|
89
|
+
for (let j = 0; j < i; j++) {
|
|
90
|
+
gated[j].state.meta = metaSnapshots[j];
|
|
91
|
+
}
|
|
92
|
+
return rejection;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// --- Phase 4: Commit ---
|
|
97
|
+
for (const v of gated) {
|
|
98
|
+
if (v.transition.deferred) {
|
|
99
|
+
v.state.pending.set(event.toolCallId, {
|
|
100
|
+
toolCallId: event.toolCallId,
|
|
101
|
+
transition: v.transition,
|
|
102
|
+
resolvedTool: v.resolvedTool,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
v.state.marking = fire(v.state.marking, v.transition);
|
|
107
|
+
v.state.marking = autoAdvance(v.net, v.state.marking);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"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"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Generic tool call event — framework-agnostic */
|
|
2
|
+
export type GateToolCall = {
|
|
3
|
+
toolCallId: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
input: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
/** Generic tool result event — framework-agnostic */
|
|
8
|
+
export type GateToolResult = {
|
|
9
|
+
toolCallId: string;
|
|
10
|
+
toolName: string;
|
|
11
|
+
input: Record<string, unknown>;
|
|
12
|
+
isError: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** Generic context for gating decisions */
|
|
15
|
+
export type GateContext = {
|
|
16
|
+
hasUI: boolean;
|
|
17
|
+
confirm: (title: string, message: string) => Promise<boolean>;
|
|
18
|
+
};
|
|
19
|
+
/** A gating decision: block with reason, or undefined to allow */
|
|
20
|
+
export type GateDecision = {
|
|
21
|
+
block: true;
|
|
22
|
+
reason: string;
|
|
23
|
+
} | undefined;
|
|
24
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,qDAAqD;AACrD,MAAM,MAAM,cAAc,GAAG;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/D,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,YAAY,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":""}
|
package/dist/gate.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Marking } from "@petriflow/engine";
|
|
2
|
+
import type { GateToolCall, GateToolResult, GateContext, GateDecision } from "./events.js";
|
|
3
|
+
import type { GatedTransition, SkillNet } from "./types.js";
|
|
4
|
+
/** Resolve the virtual tool name for a tool call event */
|
|
5
|
+
export declare function resolveTool<P extends string>(net: SkillNet<P>, event: {
|
|
6
|
+
toolName: string;
|
|
7
|
+
input: Record<string, unknown>;
|
|
8
|
+
}): string;
|
|
9
|
+
/** Public: get tool transitions the agent can currently use */
|
|
10
|
+
export declare function getEnabledToolTransitions<P extends string>(net: SkillNet<P>, marking: Marking<P>): GatedTransition<P>[];
|
|
11
|
+
/** Format marking for display */
|
|
12
|
+
export declare function formatMarking<P extends string>(marking: Marking<P>): string;
|
|
13
|
+
/** A pending deferred transition awaiting tool_result */
|
|
14
|
+
type PendingDeferred<P extends string> = {
|
|
15
|
+
toolCallId: string;
|
|
16
|
+
transition: GatedTransition<P>;
|
|
17
|
+
resolvedTool: string;
|
|
18
|
+
};
|
|
19
|
+
export type GateState<P extends string> = {
|
|
20
|
+
marking: Marking<P>;
|
|
21
|
+
/** Skill-specific metadata (e.g. backed-up paths) */
|
|
22
|
+
meta: Record<string, unknown>;
|
|
23
|
+
/** Deferred transitions waiting for tool_result */
|
|
24
|
+
pending: Map<string, PendingDeferred<P>>;
|
|
25
|
+
};
|
|
26
|
+
export declare function createGateState<P extends string>(marking: Marking<P>): GateState<P>;
|
|
27
|
+
/**
|
|
28
|
+
* Core gating logic for a tool_call event.
|
|
29
|
+
* Mutates state.marking when a non-deferred transition fires.
|
|
30
|
+
* For deferred transitions, records pending and fires on tool_result.
|
|
31
|
+
*/
|
|
32
|
+
export declare function handleToolCall<P extends string>(event: GateToolCall, ctx: GateContext, net: SkillNet<P>, state: GateState<P>): Promise<GateDecision>;
|
|
33
|
+
/**
|
|
34
|
+
* Handle a tool_result event. Fires deferred transitions on success.
|
|
35
|
+
* Returns void (tool_result handler doesn't block).
|
|
36
|
+
*/
|
|
37
|
+
export declare function handleToolResult<P extends string>(event: GateToolResult, net: SkillNet<P>, state: GateState<P>): void;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG5D,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAC1C,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAChB,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC1D,MAAM,CAQR;AAYD,+DAA+D;AAC/D,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,MAAM,EACxD,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAChB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAClB,eAAe,CAAC,CAAC,CAAC,EAAE,CAEtB;AAED,iCAAiC;AACjC,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAK3E;AAED,yDAAyD;AACzD,KAAK,eAAe,CAAC,CAAC,SAAS,MAAM,IAAI;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI;IACxC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,mDAAmD;IACnD,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;CAC1C,CAAC;AAEF,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAEnF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,CAAC,SAAS,MAAM,EACnD,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAChB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,YAAY,CAAC,CAuDvB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAC/C,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAChB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAClB,IAAI,CA+BN"}
|
package/dist/gate.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { canFire, fire } from "@petriflow/engine";
|
|
2
|
+
import { autoAdvance } from "./advance.js";
|
|
3
|
+
/** Resolve the virtual tool name for a tool call event */
|
|
4
|
+
export function resolveTool(net, event) {
|
|
5
|
+
if (net.toolMapper) {
|
|
6
|
+
return net.toolMapper({
|
|
7
|
+
toolName: event.toolName,
|
|
8
|
+
input: event.input,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
return event.toolName;
|
|
12
|
+
}
|
|
13
|
+
/** Return transitions that are structurally enabled and have a tools list */
|
|
14
|
+
function enabledToolTransitions(net, marking) {
|
|
15
|
+
return net.transitions.filter((t) => t.tools !== undefined && t.tools.length > 0 && canFire(marking, t));
|
|
16
|
+
}
|
|
17
|
+
/** Public: get tool transitions the agent can currently use */
|
|
18
|
+
export function getEnabledToolTransitions(net, marking) {
|
|
19
|
+
return enabledToolTransitions(net, marking);
|
|
20
|
+
}
|
|
21
|
+
/** Format marking for display */
|
|
22
|
+
export function formatMarking(marking) {
|
|
23
|
+
return Object.entries(marking)
|
|
24
|
+
.filter(([, v]) => v > 0)
|
|
25
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
26
|
+
.join(", ");
|
|
27
|
+
}
|
|
28
|
+
export function createGateState(marking) {
|
|
29
|
+
return { marking, meta: {}, pending: new Map() };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Core gating logic for a tool_call event.
|
|
33
|
+
* Mutates state.marking when a non-deferred transition fires.
|
|
34
|
+
* For deferred transitions, records pending and fires on tool_result.
|
|
35
|
+
*/
|
|
36
|
+
export async function handleToolCall(event, ctx, net, state) {
|
|
37
|
+
const resolvedTool = resolveTool(net, event);
|
|
38
|
+
// Free tools always pass
|
|
39
|
+
if (net.freeTools.includes(resolvedTool)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const enabled = enabledToolTransitions(net, state.marking);
|
|
43
|
+
const matching = enabled.filter((t) => t.tools.includes(resolvedTool));
|
|
44
|
+
if (matching.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
block: true,
|
|
47
|
+
reason: `Tool '${resolvedTool}' not available in current state. Marking: ${formatMarking(state.marking)}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const transition = matching[0];
|
|
51
|
+
// Skill-specific validation (e.g. path coverage)
|
|
52
|
+
if (net.validateToolCall) {
|
|
53
|
+
const rejection = net.validateToolCall({ toolName: event.toolName, input: event.input }, resolvedTool, transition, state);
|
|
54
|
+
if (rejection)
|
|
55
|
+
return rejection;
|
|
56
|
+
}
|
|
57
|
+
if (transition.type === "manual") {
|
|
58
|
+
if (!ctx.hasUI) {
|
|
59
|
+
return { block: true, reason: `Manual transition '${transition.name}' requires UI approval` };
|
|
60
|
+
}
|
|
61
|
+
const approved = await ctx.confirm(`Approve: ${transition.name}`, `Allow '${resolvedTool}' via transition '${transition.name}'?`);
|
|
62
|
+
if (!approved) {
|
|
63
|
+
return { block: true, reason: `Human rejected '${transition.name}'` };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (transition.deferred) {
|
|
67
|
+
// Allow the tool call but don't fire yet — wait for tool_result
|
|
68
|
+
state.pending.set(event.toolCallId, { toolCallId: event.toolCallId, transition, resolvedTool });
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
// Fire immediately
|
|
72
|
+
state.marking = fire(state.marking, transition);
|
|
73
|
+
state.marking = autoAdvance(net, state.marking);
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Handle a tool_result event. Fires deferred transitions on success.
|
|
78
|
+
* Returns void (tool_result handler doesn't block).
|
|
79
|
+
*/
|
|
80
|
+
export function handleToolResult(event, net, state) {
|
|
81
|
+
const pending = state.pending.get(event.toolCallId);
|
|
82
|
+
if (!pending)
|
|
83
|
+
return;
|
|
84
|
+
state.pending.delete(event.toolCallId);
|
|
85
|
+
if (event.isError) {
|
|
86
|
+
// Tool failed — don't fire the transition, marking unchanged
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// Tool succeeded — fire the deferred transition
|
|
90
|
+
if (canFire(state.marking, pending.transition)) {
|
|
91
|
+
state.marking = fire(state.marking, pending.transition);
|
|
92
|
+
// Notify the skill of the successful deferred result
|
|
93
|
+
if (net.onDeferredResult) {
|
|
94
|
+
net.onDeferredResult({
|
|
95
|
+
toolCallId: event.toolCallId,
|
|
96
|
+
input: event.input,
|
|
97
|
+
isError: event.isError,
|
|
98
|
+
}, pending.resolvedTool, pending.transition, state);
|
|
99
|
+
}
|
|
100
|
+
state.marking = autoAdvance(net, state.marking);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=gate.js.map
|
package/dist/gate.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { defineSkillNet } from "./types.js";
|
|
2
|
+
export type { SkillNet, GatedTransition, ToolEvent } from "./types.js";
|
|
3
|
+
export type { GateToolCall, GateToolResult, GateContext, GateDecision } from "./events.js";
|
|
4
|
+
export { autoAdvance } from "./advance.js";
|
|
5
|
+
export { handleToolCall, handleToolResult, formatMarking, getEnabledToolTransitions, createGateState, resolveTool, } from "./gate.js";
|
|
6
|
+
export type { GateState } from "./gate.js";
|
|
7
|
+
export { classifyNets, composedToolCall } from "./compose.js";
|
|
8
|
+
export type { ComposeConfig, NetVerdict } from "./compose.js";
|
|
9
|
+
export { createGateManager } from "./manager.js";
|
|
10
|
+
export type { GateManager, GateManagerOptions } from "./manager.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3F,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,yBAAyB,EACzB,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export { defineSkillNet } from "./types.js";
|
|
3
|
+
// Auto-advance
|
|
4
|
+
export { autoAdvance } from "./advance.js";
|
|
5
|
+
// Single-net gating
|
|
6
|
+
export { handleToolCall, handleToolResult, formatMarking, getEnabledToolTransitions, createGateState, resolveTool, } from "./gate.js";
|
|
7
|
+
// Multi-net composition
|
|
8
|
+
export { classifyNets, composedToolCall } from "./compose.js";
|
|
9
|
+
// Manager
|
|
10
|
+
export { createGateManager } from "./manager.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAM5C,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,oBAAoB;AACpB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,yBAAyB,EACzB,eAAe,EACf,WAAW,GACZ,MAAM,WAAW,CAAC;AAGnB,wBAAwB;AACxB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAG9D,UAAU;AACV,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { GateToolCall, GateToolResult, GateContext, GateDecision } from "./events.js";
|
|
2
|
+
import type { SkillNet } from "./types.js";
|
|
3
|
+
import type { GateState } from "./gate.js";
|
|
4
|
+
import type { ComposeConfig } from "./compose.js";
|
|
5
|
+
export type GateManager = {
|
|
6
|
+
handleToolCall: (event: GateToolCall, ctx: GateContext) => Promise<GateDecision>;
|
|
7
|
+
handleToolResult: (event: GateToolResult) => void;
|
|
8
|
+
addNet: (name: string) => {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
removeNet: (name: string) => {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
getActiveNets: () => Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
net: SkillNet<string>;
|
|
19
|
+
state: GateState<string>;
|
|
20
|
+
}>;
|
|
21
|
+
getAllNets: () => Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
net: SkillNet<string>;
|
|
24
|
+
state: GateState<string>;
|
|
25
|
+
active: boolean;
|
|
26
|
+
}>;
|
|
27
|
+
formatStatus: () => string;
|
|
28
|
+
formatSystemPrompt: () => string;
|
|
29
|
+
isDynamic: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type GateManagerOptions = {
|
|
32
|
+
/** "enforce" blocks disallowed tools. "shadow" logs but never blocks. */
|
|
33
|
+
mode: "enforce" | "shadow";
|
|
34
|
+
/** Called after every gating decision. Use for logging, metrics, debugging. */
|
|
35
|
+
onDecision?: (event: GateToolCall, decision: GateDecision) => void;
|
|
36
|
+
};
|
|
37
|
+
export declare function createGateManager(input: SkillNet<string>[] | ComposeConfig, opts?: GateManagerOptions): GateManager;
|
|
38
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAS3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG;IACxB,cAAc,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACjF,gBAAgB,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9D,aAAa,EAAE,MAAM,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9F,UAAU,EAAE,MAAM,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5G,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,MAAM,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yEAAyE;IACzE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;CACpE,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,aAAa,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAgBnH"}
|
package/dist/manager.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createGateState, formatMarking, getEnabledToolTransitions, handleToolResult as handleToolResultSingle, } from "./gate.js";
|
|
2
|
+
import { autoAdvance } from "./advance.js";
|
|
3
|
+
import { composedToolCall } from "./compose.js";
|
|
4
|
+
export function createGateManager(input, opts) {
|
|
5
|
+
const manager = Array.isArray(input) ? createArrayManager(input) : createRegistryManager(input);
|
|
6
|
+
if (opts) {
|
|
7
|
+
const original = manager.handleToolCall;
|
|
8
|
+
manager.handleToolCall = async (event, ctx) => {
|
|
9
|
+
const decision = await original.call(manager, event, ctx);
|
|
10
|
+
opts.onDecision?.(event, decision);
|
|
11
|
+
if (opts.mode === "shadow" && decision?.block) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return decision;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return manager;
|
|
18
|
+
}
|
|
19
|
+
function createArrayManager(nets) {
|
|
20
|
+
const states = nets.map((net) => createGateState(autoAdvance(net, { ...net.initialMarking })));
|
|
21
|
+
const getNets = () => nets;
|
|
22
|
+
const getStates = () => states;
|
|
23
|
+
return {
|
|
24
|
+
handleToolCall(event, ctx) {
|
|
25
|
+
return composedToolCall(getNets, getStates, event, ctx);
|
|
26
|
+
},
|
|
27
|
+
handleToolResult(event) {
|
|
28
|
+
for (let i = 0; i < nets.length; i++) {
|
|
29
|
+
handleToolResultSingle(event, nets[i], states[i]);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
addNet() {
|
|
33
|
+
return { ok: false, message: "Static composition does not support dynamic nets" };
|
|
34
|
+
},
|
|
35
|
+
removeNet() {
|
|
36
|
+
return { ok: false, message: "Static composition does not support dynamic nets" };
|
|
37
|
+
},
|
|
38
|
+
getActiveNets() {
|
|
39
|
+
return nets.map((net, i) => ({ name: net.name, net, state: states[i] }));
|
|
40
|
+
},
|
|
41
|
+
getAllNets() {
|
|
42
|
+
return nets.map((net, i) => ({ name: net.name, net, state: states[i], active: true }));
|
|
43
|
+
},
|
|
44
|
+
formatStatus() {
|
|
45
|
+
return nets
|
|
46
|
+
.map((n, i) => `${n.name}: ${formatMarking(states[i].marking)}`)
|
|
47
|
+
.join("\n");
|
|
48
|
+
},
|
|
49
|
+
formatSystemPrompt() {
|
|
50
|
+
return formatPromptForNets(nets, states);
|
|
51
|
+
},
|
|
52
|
+
isDynamic: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createRegistryManager(config) {
|
|
56
|
+
const registry = new Map();
|
|
57
|
+
for (const [name, net] of Object.entries(config.registry)) {
|
|
58
|
+
registry.set(name, {
|
|
59
|
+
net,
|
|
60
|
+
state: createGateState(autoAdvance(net, { ...net.initialMarking })),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const activeNames = new Set(config.active ?? Object.keys(config.registry));
|
|
64
|
+
const getActiveNets = () => [...activeNames].map((n) => registry.get(n).net);
|
|
65
|
+
const getActiveStates = () => [...activeNames].map((n) => registry.get(n).state);
|
|
66
|
+
return {
|
|
67
|
+
handleToolCall(event, ctx) {
|
|
68
|
+
return composedToolCall(getActiveNets, getActiveStates, event, ctx);
|
|
69
|
+
},
|
|
70
|
+
handleToolResult(event) {
|
|
71
|
+
for (const { net, state } of registry.values()) {
|
|
72
|
+
handleToolResultSingle(event, net, state);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
addNet(name) {
|
|
76
|
+
if (!registry.has(name)) {
|
|
77
|
+
return { ok: false, message: `Unknown net '${name}'. Available: ${[...registry.keys()].join(", ")}` };
|
|
78
|
+
}
|
|
79
|
+
if (activeNames.has(name)) {
|
|
80
|
+
return { ok: false, message: `'${name}' is already active` };
|
|
81
|
+
}
|
|
82
|
+
activeNames.add(name);
|
|
83
|
+
return { ok: true, message: `Activated '${name}'` };
|
|
84
|
+
},
|
|
85
|
+
removeNet(name) {
|
|
86
|
+
if (!activeNames.has(name)) {
|
|
87
|
+
return { ok: false, message: `'${name}' is not active. Active: ${[...activeNames].join(", ")}` };
|
|
88
|
+
}
|
|
89
|
+
activeNames.delete(name);
|
|
90
|
+
return { ok: true, message: `Deactivated '${name}' (state preserved)` };
|
|
91
|
+
},
|
|
92
|
+
getActiveNets() {
|
|
93
|
+
return [...activeNames].map((name) => {
|
|
94
|
+
const entry = registry.get(name);
|
|
95
|
+
return { name, net: entry.net, state: entry.state };
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
getAllNets() {
|
|
99
|
+
return [...registry.entries()].map(([name, { net, state }]) => ({
|
|
100
|
+
name,
|
|
101
|
+
net,
|
|
102
|
+
state,
|
|
103
|
+
active: activeNames.has(name),
|
|
104
|
+
}));
|
|
105
|
+
},
|
|
106
|
+
formatStatus() {
|
|
107
|
+
return [...registry.entries()]
|
|
108
|
+
.map(([name, { state }]) => {
|
|
109
|
+
const status = activeNames.has(name) ? "active" : "inactive";
|
|
110
|
+
return `${name} (${status}): ${formatMarking(state.marking)}`;
|
|
111
|
+
})
|
|
112
|
+
.join("\n");
|
|
113
|
+
},
|
|
114
|
+
formatSystemPrompt() {
|
|
115
|
+
return formatPromptForNets(getActiveNets(), getActiveStates());
|
|
116
|
+
},
|
|
117
|
+
isDynamic: true,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function formatPromptForNets(nets, states) {
|
|
121
|
+
const sections = nets.map((net, i) => {
|
|
122
|
+
const enabled = getEnabledToolTransitions(net, states[i].marking);
|
|
123
|
+
const toolList = enabled.flatMap((t) => t.tools ?? []);
|
|
124
|
+
return `### ${net.name}\nAvailable gated tools: ${toolList.join(", ") || "none"}\nFree tools: ${net.freeTools.join(", ")}\nState: ${formatMarking(states[i].marking)}`;
|
|
125
|
+
});
|
|
126
|
+
return `## Active Petri Nets (composed)\n${sections.join("\n\n")}`;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,eAAe,EACf,aAAa,EACb,yBAAyB,EACzB,gBAAgB,IAAI,sBAAsB,GAC3C,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAsBhD,MAAM,UAAU,iBAAiB,CAAC,KAAyC,EAAE,IAAyB;IACpG,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAEhG,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC;QACxC,OAAO,CAAC,cAAc,GAAG,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC5C,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;YAC1D,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAAE,KAAK,EAAE,CAAC;gBAC9C,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,OAAO,QAAQ,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAwB;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC9B,eAAe,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,CAC7D,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC;IAC3B,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC;IAE/B,OAAO;QACL,cAAc,CAAC,KAAK,EAAE,GAAG;YACvB,OAAO,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;QAED,gBAAgB,CAAC,KAAK;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrC,sBAAsB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAE,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;QAED,MAAM;YACJ,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,kDAAkD,EAAE,CAAC;QACpF,CAAC;QAED,SAAS;YACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,kDAAkD,EAAE,CAAC;QACpF,CAAC;QAED,aAAa;YACX,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,UAAU;YACR,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1F,CAAC;QAED,YAAY;YACV,OAAO,IAAI;iBACR,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,EAAE,CAAC;iBAChE,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,kBAAkB;YAChB,OAAO,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,SAAS,EAAE,KAAK;KACjB,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAqB;IAClD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+D,CAAC;IACxF,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE;YACjB,GAAG;YACH,KAAK,EAAE,eAAe,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;SACpE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEnF,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,GAAG,CAAC,CAAC;IAC9E,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC;IAElF,OAAO;QACL,cAAc,CAAC,KAAK,EAAE,GAAG;YACvB,OAAO,gBAAgB,CAAC,aAAa,EAAE,eAAe,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QACtE,CAAC;QAED,gBAAgB,CAAC,KAAK;YACpB,KAAK,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC/C,sBAAsB,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,MAAM,CAAC,IAAI;YACT,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,gBAAgB,IAAI,iBAAiB,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACxG,CAAC;YACD,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,qBAAqB,EAAE,CAAC;YAC/D,CAAC;YACD,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACtB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,IAAI,GAAG,EAAE,CAAC;QACtD,CAAC;QAED,SAAS,CAAC,IAAI;YACZ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,4BAA4B,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACnG,CAAC;YACD,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,IAAI,qBAAqB,EAAE,CAAC;QAC1E,CAAC;QAED,aAAa;YACX,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;gBAClC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YACtD,CAAC,CAAC,CAAC;QACL,CAAC;QAED,UAAU;YACR,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9D,IAAI;gBACJ,GAAG;gBACH,KAAK;gBACL,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;aAC9B,CAAC,CAAC,CAAC;QACN,CAAC;QAED,YAAY;YACV,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;iBAC3B,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;gBACzB,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;gBAC7D,OAAO,GAAG,IAAI,KAAK,MAAM,MAAM,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAChE,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,kBAAkB;YAChB,OAAO,mBAAmB,CAAC,aAAa,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAwB,EAAE,MAA2B;IAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACnC,MAAM,OAAO,GAAG,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACvD,OAAO,OAAO,GAAG,CAAC,IAAI,4BAA4B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,iBAAiB,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,aAAa,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC1K,CAAC,CAAC,CAAC;IACH,OAAO,oCAAoC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;AACrE,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Marking } from "@petriflow/engine";
|
|
2
|
+
/** A transition that optionally gates tool access */
|
|
3
|
+
export type GatedTransition<Place extends string> = {
|
|
4
|
+
name: string;
|
|
5
|
+
type: "auto" | "manual";
|
|
6
|
+
inputs: Place[];
|
|
7
|
+
outputs: Place[];
|
|
8
|
+
guard?: string | null;
|
|
9
|
+
tools?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* When true, the transition allows the tool call immediately but
|
|
12
|
+
* only fires (consumes/produces tokens) when the tool_result
|
|
13
|
+
* comes back successfully (isError === false).
|
|
14
|
+
* Use this for transitions where the tool must succeed before
|
|
15
|
+
* the net advances (e.g. backup must succeed before delete unlocks).
|
|
16
|
+
*/
|
|
17
|
+
deferred?: boolean;
|
|
18
|
+
};
|
|
19
|
+
/** Minimal tool event shape for toolMapper */
|
|
20
|
+
export type ToolEvent = {
|
|
21
|
+
toolName: string;
|
|
22
|
+
input: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
/** A Petri net that gates a skill's tool access */
|
|
25
|
+
export type SkillNet<Place extends string> = {
|
|
26
|
+
name: string;
|
|
27
|
+
places: Place[];
|
|
28
|
+
transitions: GatedTransition<Place>[];
|
|
29
|
+
initialMarking: Marking<Place>;
|
|
30
|
+
terminalPlaces: Place[];
|
|
31
|
+
freeTools: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Maps a tool call to a virtual tool name before gating.
|
|
34
|
+
* Use this to split one tool (e.g. "bash") into multiple gated
|
|
35
|
+
* variants (e.g. "bash", "git-commit", "git-push") based on input.
|
|
36
|
+
* If not provided, event.toolName is used as-is.
|
|
37
|
+
*/
|
|
38
|
+
toolMapper?: (event: ToolEvent) => string;
|
|
39
|
+
/**
|
|
40
|
+
* Additional validation before a gated tool call is allowed.
|
|
41
|
+
* Called after the net confirms a matching transition exists.
|
|
42
|
+
* Return { block, reason } to reject, or void to allow.
|
|
43
|
+
* Use this for domain-specific checks (e.g. path coverage).
|
|
44
|
+
*
|
|
45
|
+
* Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.
|
|
46
|
+
*/
|
|
47
|
+
validateToolCall?(event: ToolEvent, resolvedTool: string, transition: GatedTransition<Place>, state: {
|
|
48
|
+
marking: Marking<Place>;
|
|
49
|
+
meta: Record<string, unknown>;
|
|
50
|
+
}): {
|
|
51
|
+
block: true;
|
|
52
|
+
reason: string;
|
|
53
|
+
} | void;
|
|
54
|
+
/**
|
|
55
|
+
* Called when a deferred transition's tool_result arrives.
|
|
56
|
+
* Use this to record metadata (e.g. backed-up paths).
|
|
57
|
+
*
|
|
58
|
+
* Method syntax is intentional — bivariant so SkillNet<Place> widens to SkillNet<string>.
|
|
59
|
+
*/
|
|
60
|
+
onDeferredResult?(event: {
|
|
61
|
+
toolCallId: string;
|
|
62
|
+
input: Record<string, unknown>;
|
|
63
|
+
isError: boolean;
|
|
64
|
+
}, resolvedTool: string, transition: GatedTransition<Place>, state: {
|
|
65
|
+
marking: Marking<Place>;
|
|
66
|
+
meta: Record<string, unknown>;
|
|
67
|
+
}): void;
|
|
68
|
+
};
|
|
69
|
+
/** Type-safe helper — validates places/marking at the type level */
|
|
70
|
+
export declare function defineSkillNet<Place extends string>(net: SkillNet<Place>): SkillNet<Place>;
|
|
71
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,qDAAqD;AACrD,MAAM,MAAM,eAAe,CAAC,KAAK,SAAS,MAAM,IAAI;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,8CAA8C;AAC9C,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC;AAE7E,mDAAmD;AACnD,MAAM,MAAM,QAAQ,CAAC,KAAK,SAAS,MAAM,IAAI;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,WAAW,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;IACtC,cAAc,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC/B,cAAc,EAAE,KAAK,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC;IAC1C;;;;;;;OAOG;IACH,gBAAgB,CAAC,CACf,KAAK,EAAE,SAAS,EAChB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,eAAe,CAAC,KAAK,CAAC,EAClC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAChE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C;;;;;OAKG;IACH,gBAAgB,CAAC,CACf,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAC/E,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,eAAe,CAAC,KAAK,CAAC,EAClC,KAAK,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAChE,IAAI,CAAC;CACT,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,KAAK,SAAS,MAAM,EACjD,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,GACnB,QAAQ,CAAC,KAAK,CAAC,CAEjB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAkEA,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAC5B,GAAoB;IAEpB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@petriflow/gate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": ["dist", "README.md"],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "bun test",
|
|
16
|
+
"build": "tsc -p tsconfig.build.json",
|
|
17
|
+
"check": "tsc --noEmit",
|
|
18
|
+
"lint": "oxlint src/"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@petriflow/engine": "workspace:*"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/joshuaisaact/petri-flow.git",
|
|
27
|
+
"directory": "packages/gate"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "^1",
|
|
34
|
+
"typescript": "^5.7"
|
|
35
|
+
}
|
|
36
|
+
}
|