nucleus-core-ts 0.9.718 → 0.9.719

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.
@@ -95,8 +95,15 @@ function matchesGlob(path, patterns) {
95
95
  reason: 'godmin',
96
96
  action
97
97
  };
98
- const suppressed = suppressedClaims && suppressedClaims.length ? new Set(suppressedClaims) : null;
99
- const effectiveClaims = suppressed ? claims.filter((c)=>!suppressed.has(c)) : claims;
98
+ // A ClaimGuard's `invalidates` entries are PREFIX-aware, mirroring how claim grants work
99
+ // (and how firedGuards naming below matches): suppressing `llm` (or `llm.prediction`) removes
100
+ // every claim AT or UNDER it. This lets a guard invalidate a whole capability with one short
101
+ // prefix instead of enumerating each discovered leaf — an exact leaf is just the zero-descendant
102
+ // case. (Without this, an operator/AI-proposed `invalidates:["llm"]` would silently no-op, since
103
+ // discovered claims are leaves and none equals the bare prefix.)
104
+ const suppressed = suppressedClaims && suppressedClaims.length ? suppressedClaims : null;
105
+ const isSuppressed = (c)=>!!suppressed && suppressed.some((s)=>c === s || c.startsWith(`${s}.`));
106
+ const effectiveClaims = suppressed ? claims.filter((c)=>!isSuppressed(c)) : claims;
100
107
  if (claimSatisfies(effectiveClaims, action)) return {
101
108
  decision: 'allow',
102
109
  reason: 'claim',
@@ -249,6 +249,34 @@ describe('evaluateAccess', ()=>{
249
249
  reason: 'claim'
250
250
  });
251
251
  });
252
+ it('suppresses by PREFIX — invalidating "agent" removes every agent.* leaf', ()=>{
253
+ // A guard whose invalidates is a short prefix must suppress the leaf the user actually
254
+ // holds (discovered claims are leaves). Regression: exact-only subtraction would ALLOW.
255
+ const d = evaluateAccess({
256
+ ...base,
257
+ method: 'GET',
258
+ path: '/api/v1/templates',
259
+ claims: [
260
+ 'agent.templates.list'
261
+ ],
262
+ suppressedClaims: [
263
+ 'agent'
264
+ ],
265
+ firedGuards: [
266
+ {
267
+ action: 'maintenance_mode',
268
+ invalidates: [
269
+ 'agent'
270
+ ]
271
+ }
272
+ ]
273
+ });
274
+ expect(d).toMatchObject({
275
+ decision: 'deny',
276
+ reason: 'claim_guard',
277
+ guard: 'maintenance_mode'
278
+ });
279
+ });
252
280
  it('suppression never elevates godmin denial (godmin bypasses by role)', ()=>{
253
281
  const d = evaluateAccess({
254
282
  ...base,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nucleus-core-ts",
3
- "version": "0.9.718",
3
+ "version": "0.9.719",
4
4
  "description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
5
5
  "author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",