@metamynd/agentsafe-guard 0.6.3 → 0.6.4
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 +9 -1
- package/package.json +1 -1
- package/policy-core.mjs +8 -7
package/README.md
CHANGED
|
@@ -91,6 +91,14 @@ negative value could clear a spend cap for free, or erode a cumulative-spend tra
|
|
|
91
91
|
sums signed amounts over time. `amount-unknown` now fires on any non-finite, non-numeric,
|
|
92
92
|
**or negative** amount; a genuine `0` still passes.
|
|
93
93
|
|
|
94
|
+
**0.6.4 — a mandate's currency check no longer lets a PROHIBITION be dodged by relabeling
|
|
95
|
+
the currency.** A payAmount/cumulativeSpend constraint issued with a `unit` (currency) is
|
|
96
|
+
only satisfied in that currency — correct for a PERMISSION (fail closed to deny on a
|
|
97
|
+
mismatch), but a prohibition only fires when every one of its own constraints is satisfied,
|
|
98
|
+
so the identical "mismatch → not satisfied" rule let a prohibition like `payAmount gteq 1000
|
|
99
|
+
unit USD` be silently skipped by declaring any other currency, including a mere case
|
|
100
|
+
difference (`'usd'` vs `'USD'`). The currency comparison is also now case-insensitive.
|
|
101
|
+
|
|
94
102
|
```yaml
|
|
95
103
|
# .github/workflows/governance.yml
|
|
96
104
|
name: Governance
|
|
@@ -127,7 +135,7 @@ jobs:
|
|
|
127
135
|
runs-on: ubuntu-latest
|
|
128
136
|
steps:
|
|
129
137
|
- uses: actions/checkout@v5
|
|
130
|
-
- uses: Metamynd/agentsafe-guard/packages/agentsafe-guard@v0.6.
|
|
138
|
+
- uses: Metamynd/agentsafe-guard/packages/agentsafe-guard@v0.6.4
|
|
131
139
|
with:
|
|
132
140
|
config: ./agent.metamynd.json
|
|
133
141
|
require: merchants,perTxn
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamynd/agentsafe-guard",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Zero-dependency runtime governance for any Node AI agent \u2014 gate tool calls through MetaMynd/AgentSafe (allow / block / escalate) against the agent's mandate, enforced Standards, and SOPs. Ed25519-signed, deterministic, fail-closed.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./agentsafe-guard.mjs",
|
package/policy-core.mjs
CHANGED
|
@@ -351,13 +351,14 @@ function reasonFor(constraint) {
|
|
|
351
351
|
if (!constraint) return "CONSTRAINT_FAILED";
|
|
352
352
|
return REASON_BY_OPERAND[constraint.leftOperand] ?? `CONSTRAINT_FAILED:${constraint.leftOperand}`;
|
|
353
353
|
}
|
|
354
|
-
function constraintSatisfied(c, req) {
|
|
354
|
+
function constraintSatisfied(c, req, strict) {
|
|
355
355
|
const op = OPERATORS[c.operator];
|
|
356
356
|
if (!op) return false;
|
|
357
357
|
const left = Object.prototype.hasOwnProperty.call(req.values, c.leftOperand) ? req.values[c.leftOperand] : void 0;
|
|
358
|
-
if (!op(left, c.rightOperand)
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
if (!c.unit) return op(left, c.rightOperand);
|
|
359
|
+
const currency = req.values["mm:currency"];
|
|
360
|
+
const unitMatches = typeof currency === "string" && currency.toUpperCase() === c.unit.toUpperCase();
|
|
361
|
+
return unitMatches ? op(left, c.rightOperand) : !strict;
|
|
361
362
|
}
|
|
362
363
|
function targetOf(rule, mandate) {
|
|
363
364
|
return rule.target ?? mandate.target;
|
|
@@ -379,7 +380,7 @@ function evaluateMandate(mandate, req) {
|
|
|
379
380
|
}
|
|
380
381
|
for (const p of mandate.prohibition ?? []) {
|
|
381
382
|
if (targetOf(p, mandate) !== req.target) continue;
|
|
382
|
-
const fires = (p.constraint ?? []).every((c) => constraintSatisfied(c, req));
|
|
383
|
+
const fires = (p.constraint ?? []).every((c) => constraintSatisfied(c, req, false));
|
|
383
384
|
if (fires) {
|
|
384
385
|
return {
|
|
385
386
|
decision: p.enforcement ?? "block",
|
|
@@ -397,10 +398,10 @@ function evaluateMandate(mandate, req) {
|
|
|
397
398
|
};
|
|
398
399
|
}
|
|
399
400
|
for (const p of perms) {
|
|
400
|
-
const failing = (p.constraint ?? []).find((c) => !constraintSatisfied(c, req));
|
|
401
|
+
const failing = (p.constraint ?? []).find((c) => !constraintSatisfied(c, req, true));
|
|
401
402
|
if (!failing) return { decision: "allow", reasonCode: "AUTHORIZED" };
|
|
402
403
|
}
|
|
403
|
-
const firstFail = (perms[0].constraint ?? []).find((c) => !constraintSatisfied(c, req));
|
|
404
|
+
const firstFail = (perms[0].constraint ?? []).find((c) => !constraintSatisfied(c, req, true));
|
|
404
405
|
return {
|
|
405
406
|
decision: firstFail?.onFail ?? "block",
|
|
406
407
|
reasonCode: reasonFor(firstFail),
|