@metamynd/agentsafe-guard 0.6.3 → 0.6.6
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 +23 -2
- package/package.json +1 -1
- package/policy-core.mjs +8 -7
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ re-implement the gate:
|
|
|
25
25
|
([markdown](https://metamynd.ai/specs/magp-v1.0.md))
|
|
26
26
|
|
|
27
27
|
It defines agent identity, the canonical signed message (§8.3), the sixteen-stage order of
|
|
28
|
-
checks (§8.5), all
|
|
28
|
+
checks (§8.5), all 64 reason codes (Appendix A), delegation narrowing (§5.4), and evidence
|
|
29
29
|
you can verify offline without MetaMynd (§13.4). If you are writing a client in a language
|
|
30
30
|
other than JavaScript, read §8.3.3–8.3.5 first: key encoding, number stringification and
|
|
31
31
|
signed-vs-sent field identity each surface only as `SIGNATURE_INVALID`.
|
|
@@ -91,6 +91,27 @@ 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
|
+
|
|
102
|
+
**0.6.5 — `IDENTITY_KEY_MISMATCH`, a new reason code (now 63 total).** The backend gate
|
|
103
|
+
now cross-checks a resolved agent's registered public key against the key embedded in its
|
|
104
|
+
own self-certifying DID (`did:hedera` / `did:key`) before trusting a signature against it —
|
|
105
|
+
free, local, no network round-trip, since the DID already carries its own proof. Nothing in
|
|
106
|
+
this package's own evaluation changes; documented here because the count in this README and
|
|
107
|
+
the linked spec moved.
|
|
108
|
+
|
|
109
|
+
**0.6.6 — `SPEND_PATTERN_ANOMALY`, a new reason code (now 64 total).** The backend gate can
|
|
110
|
+
now escalate an otherwise-permitted decision whose amount deviates sharply from an agent's
|
|
111
|
+
OWN recorded spend history — a baseline the platform's trust-graph engine learns, not a
|
|
112
|
+
threshold a human pre-sets. Opt-in (`SPEND_ANOMALY_MODE=on`), off by default. Nothing in this
|
|
113
|
+
package's own evaluation changes; documented here because the count moved again.
|
|
114
|
+
|
|
94
115
|
```yaml
|
|
95
116
|
# .github/workflows/governance.yml
|
|
96
117
|
name: Governance
|
|
@@ -127,7 +148,7 @@ jobs:
|
|
|
127
148
|
runs-on: ubuntu-latest
|
|
128
149
|
steps:
|
|
129
150
|
- uses: actions/checkout@v5
|
|
130
|
-
- uses: Metamynd/agentsafe-guard/packages/agentsafe-guard@v0.6.
|
|
151
|
+
- uses: Metamynd/agentsafe-guard/packages/agentsafe-guard@v0.6.4
|
|
131
152
|
with:
|
|
132
153
|
config: ./agent.metamynd.json
|
|
133
154
|
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.6",
|
|
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),
|