@argszero/cordis-plugin-sandbox-grant-advisor 0.3.0 → 0.4.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 CHANGED
@@ -30,10 +30,12 @@ stuck.
30
30
 
31
31
  ### 1. Workspace provisioning — the Windows ACL failure (`acl-provisioning`)
32
32
 
33
- Four reports describe this exact line: [discussion #7538], [discussion #7622],
34
- [discussion #7646], [discussion #7720]. In each one every sandboxed command fails
35
- the same way, before it runs, and the error names neither the missing right nor
36
- a remedy.
33
+ Six reports describe this exact line: [discussion #7538], [discussion #7622],
34
+ [discussion #7646], [discussion #7720], [discussion #7750], [discussion #7735]
35
+ (the last two on data-volume workspaces, where *no* ACE names the caller at all —
36
+ the inherited `Authenticated Users: Modify` is the whole of their access). In each
37
+ one every sandboxed command fails the same way, before it runs, and the error names
38
+ neither the missing right nor a remedy.
37
39
 
38
40
  `#7720` is worth reading for where the failure lands: the grant is materialized
39
41
  at sandbox **initialization**, so this is not one refused operation but *every*
@@ -66,7 +68,26 @@ Two consequences follow from that one line:
66
68
  is the token-privilege form of the same idea, and it is the hypothesis the
67
69
  reports naturally reach for — `whoami /priv` cannot tell the two apart,
68
70
  because `WRITE_OWNER` is an object right and never appears in that table.
69
- Granting Full control to the workspace root needs **no** elevation.
71
+ Granting `WRITE_OWNER` on the workspace root needs **no** elevation. `#7735`
72
+ settles the gate with an isolation table on one machine and one unprivileged
73
+ account: the label write succeeds with Full control *or with Take-ownership
74
+ alone*, and fails with `ChangePermissions`, `ReadPermissions` or `Modify`
75
+ alone — so the gate is `WRITE_OWNER` and nothing else.
76
+ 3. **The remedy is the narrowest form of that right.** The advisory recommends
77
+ `icacls <dir> /grant "<user>:(OI)(CI)(WO)"` — `WO` *is* `WRITE_OWNER`, i.e.
78
+ literally the right the documented prerequisite names, so the one-liner grants
79
+ nothing the harness did not ask for — and offers Full control second, as the
80
+ same line with `F` in place of `(WO)`. Both assume the caller owns the
81
+ directory: owner-implicit rights cover the DACL half of the merged write.
82
+ 4. **The version boundary is stated, because the error cannot carry it.** Up to
83
+ `0.1.6-alpha.x` the backend's `SetNamedSecurityInfoW` wrote the DACL only
84
+ (flag 4) and a Modify-only workspace provisioned fine; the mandatory label —
85
+ and with it the SACL, flag 20 — arrives in `0.1.7-alpha.1`. So the same string
86
+ on an older line belongs to a different cause space, and the natural reach
87
+ (downgrade to the build that "worked") is refused with its reason: the label is
88
+ what confines deletes to the workspace, and reverting it reintroduces the
89
+ escape it closed. `#7750` asks for exactly this and explains why it is a
90
+ usability regression traded for a security fix.
70
91
 
71
92
  The grant is materialized lazily, on the first confined call, and **nothing is
72
93
  cached when it throws** — so the same failure repeats per command (850 calls
@@ -84,12 +105,17 @@ What was reported:
84
105
  Why it is refused while the directory looks writable: that call is a MERGED write ...
85
106
  ... the label half additionally needs WRITE_OWNER on the directory. ...
86
107
 
108
+ A version boundary worth knowing before reaching for an older build: the label half is new to this package.
109
+ Up to `0.1.6-alpha.x` the backend touched the DACL only (flag 4) ... `0.1.7-alpha.1` is where the mandatory
110
+ label — and with it the SACL, flag 20 — arrives. ... Rolling back is not the fix either: the label is what
111
+ confines deletes to the workspace, and reverting it reintroduces the escape it closed.
112
+
87
113
  Confirm the cause (unelevated) — `icacls` is a normal user command:
88
114
  icacls "D:\ws"
89
115
 
90
116
  Fix it (unelevated, one line) and then run the command again:
91
- PowerShell: icacls "D:\ws" /grant "$env:USERNAME:(OI)(CI)F"
92
- cmd: icacls "D:\ws" /grant "%USERNAME%:(OI)(CI)F"
117
+ PowerShell: icacls "D:\ws" /grant "$env:USERNAME:(OI)(CI)(WO)"
118
+ cmd: icacls "D:\ws" /grant "%USERNAME%:(OI)(CI)(WO)"
93
119
 
94
120
  What will NOT fix it — both look like the right move, and both were tried and reported:
95
121
  takeown /F "D:\ws" /R /D Y
@@ -295,6 +321,12 @@ than one that stays silent.
295
321
  replace it.** That guard keys on **call identity** (identical arguments
296
322
  retried); this one keys on the **environment signature**, which is how several
297
323
  *different* commands share one cause. Mounting both is sensible.
324
+ - **The plugin cannot see the launcher half of `#7735`.** The Low label's other
325
+ side effect — the shell's publisher confirmation before launching a
326
+ Low-integrity `.bat`/`.cmd`/`.exe` — never appears in a tool result, so it is
327
+ outside the seam this plugin subscribes to. The report and its proposed fix
328
+ stay with the maintainers; all this plugin can do is explain the provisioning
329
+ failure that shares its root.
298
330
  - **The real fix is upstream, in both families.** For the ACL failure,
299
331
  `grantWrite` already computes `hasExactGrant` / `hasExactDeny` /
300
332
  `hasExactLabel` and discards which one was false, so the diagnostic that turns
@@ -325,11 +357,19 @@ composition that does not mount the service degrades to silence instead of
325
357
  failing to load. See `src/mode.ts`.
326
358
 
327
359
  Probed at the newest build of every line the range admits — `0.1.2-rc.1`,
328
- `0.1.3-alpha.2`, `0.1.5-rc.3`, `0.1.6-alpha.2`, `0.1.7-rc.1` (the build the third
329
- report ran) — with `npm run test:probe-lines`, which derives those builds from
330
- this range, installs each one from the registry into a scratch tree and runs the
331
- suite against it. A line whose probe fails is removed from the range rather than
332
- left claimed.
360
+ `0.1.3-alpha.2`, `0.1.5-rc.3`, `0.1.6-alpha.2`, `0.1.7-rc.2` (the newest build of
361
+ the line the later Windows reports ran on) — with `npm run test:probe-lines`,
362
+ which derives those builds from this range, installs each one from the registry
363
+ into a scratch tree and runs the suite against it. `0.1.7-rc.1`, the build the
364
+ third report ran, is admitted by the same `||` segment and was probed while it was
365
+ the newest of that line.
366
+
367
+ The whole set is re-probed whenever this package's source changes rather than
368
+ carried over from an earlier version: the range is a claim about *this* build of
369
+ the plugin, so `0.4.0` re-ran all five lines above. A line whose probe fails is
370
+ removed from the range rather than left claimed. The scratch tree's resolved
371
+ versions are the ones to read back when a probe is quoted as evidence — the probe
372
+ script pins them by exact version, and `--keep` leaves the tree in place to check.
333
373
 
334
374
  ## Development
335
375
 
@@ -357,4 +397,6 @@ the current runtime cannot distinguish rather than counting it as a pass.
357
397
  [discussion #7622]: https://github.com/deepseek-ai/deepseek-harness/discussions/7622
358
398
  [discussion #7646]: https://github.com/deepseek-ai/deepseek-harness/discussions/7646
359
399
  [discussion #7720]: https://github.com/deepseek-ai/deepseek-harness/discussions/7720
400
+ [discussion #7750]: https://github.com/deepseek-ai/deepseek-harness/discussions/7750
401
+ [discussion #7735]: https://github.com/deepseek-ai/deepseek-harness/discussions/7735
360
402
  [discussion #7638]: https://github.com/deepseek-ai/deepseek-harness/discussions/7638
package/lib/advice.js CHANGED
@@ -13,6 +13,13 @@
13
13
  * the caller can grant itself with `icacls`, unelevated. It is **not**
14
14
  * `SeSecurityPrivilege`, the token privilege the reports naturally reach for;
15
15
  * `whoami /priv` cannot show the difference, and elevation is the wrong lever.
16
+ * The remedy is the **narrowest** form of that grant — `(WO)` alone, which is
17
+ * literally the right the backend's prerequisite names — with Full control
18
+ * offered as the broad alternative; and the advisory carries the **version
19
+ * boundary** the label introduced (`0.1.7-alpha.1`), because that is what
20
+ * separates "this is the label failure" from "this is something else", and
21
+ * because rolling back is the reach it invites while making the very problem
22
+ * it closed come back.
16
23
  * - **The persistent-shell failure** (`pty-startup`) is *not* fixable by the
17
24
  * caller — least of all by the model, which has no shell to run anything in.
18
25
  * So its advice says so and stops: the remedy is a user-side preset choice,
@@ -33,7 +40,7 @@
33
40
  */
34
41
  import { failureLine } from './signature.js';
35
42
  /** The upstream threads the ACL advisory is a stopgap for. */
36
- export const ACL_DISCUSSIONS = '#7538 / #7622 / #7646 / #7720';
43
+ export const ACL_DISCUSSIONS = '#7538 / #7622 / #7646 / #7720 / #7750 / #7735';
37
44
  /** The upstream thread the persistent-shell advisory is a stopgap for. */
38
45
  export const PTY_DISCUSSIONS = '#7638';
39
46
  /** The documented prerequisite, quoted from the backend's README. */
@@ -87,7 +94,10 @@ function diagnosis(failure) {
87
94
  'mandatory-integrity label go out as one `SetNamedSecurityInfoW`. The label lives in the SACL, and the',
88
95
  "owner's implicit rights cover only READ_CONTROL and WRITE_DAC, so the label half additionally needs",
89
96
  'WRITE_OWNER on the directory. A workspace created with `mkdir` normally inherits',
90
- '"Authenticated Users: Modify" (`0x1301bf`) from the drive root — and that mask has neither right.',
97
+ '"Authenticated Users: Modify" (`0x1301bf`) from the drive root — and that mask has neither right; on a data',
98
+ 'volume there may be no ACE naming you at all, so that inherited entry is the whole of your access. The two',
99
+ 'halves go out as one call, so a refused label discards the write grant with it, and the sandbox then',
100
+ 'refuses to start any command in the workspace instead of running it unconfined.',
91
101
  'This is a directory ACL fact, not a token privilege: `whoami /priv` will not show it, and',
92
102
  'SeSecurityPrivilege is the wrong lever here.',
93
103
  ].join('\n');
@@ -137,6 +147,28 @@ function nonFixes(failure, path) {
137
147
  '',
138
148
  ];
139
149
  }
150
+ /**
151
+ * When the label half arrived, and why an older build is not the remedy.
152
+ *
153
+ * Emitted for every class: the boundary is a fact about the package
154
+ * `sandbox-windows-acl` (whose `DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION`
155
+ * flag is what needs `WRITE_OWNER`), not about which of its two calls failed, so
156
+ * it is true wherever this module is willing to speak at all. It is also the one
157
+ * fact neither report could get from the error: the failure looks identical on a
158
+ * line where the label does not exist yet, and the build that introduced it is
159
+ * the natural thing to reach for and the wrong one to reach for.
160
+ * @returns the section's lines.
161
+ */
162
+ function versionBoundary() {
163
+ return [
164
+ 'A version boundary worth knowing before reaching for an older build: the label half is new to this package.',
165
+ 'Up to `0.1.6-alpha.x` the backend touched the DACL only (flag 4), so a Modify-only workspace provisioned',
166
+ 'fine; `0.1.7-alpha.1` is where the mandatory label — and with it the SACL, flag 20 — arrives. On a',
167
+ '`0.1.6-alpha.x`-or-older line this exact failure therefore belongs to a different cause space, while on any',
168
+ '`0.1.7-*` line it is this one. Rolling back is not the fix either: the label is what confines deletes to the',
169
+ 'workspace, and reverting it reintroduces the escape it closed.',
170
+ ].join('\n');
171
+ }
140
172
  /**
141
173
  * Build the advisory attached to the failing tool result.
142
174
  *
@@ -174,14 +206,25 @@ function aclAdvisory(failure, href) {
174
206
  '',
175
207
  diagnosis(failure),
176
208
  '',
209
+ versionBoundary(),
210
+ '',
177
211
  'Confirm the cause (unelevated) — `icacls` is a normal user command:',
178
212
  ` icacls "${path}"`,
179
- 'Look for an ACE that names YOUR OWN account (run `whoami` if unsure) with (F) / Full control.',
180
- 'If the strongest entry naming you is (M) / Modify, that is this failure.',
213
+ 'Look for an ACE that names YOUR OWN account (run `whoami` if unsure) with (F) / Full control or',
214
+ '(WO) / Write owner. If the strongest entry naming you is (M) / Modify — or no entry names you at all and',
215
+ 'your access comes from an inherited `Authenticated Users:(M)` — that is this failure.',
181
216
  '',
182
217
  'Fix it (unelevated, one line) and then run the command again:',
183
- ` PowerShell: icacls "${path}" /grant "$env:USERNAME:(OI)(CI)F"`,
184
- ` cmd: icacls "${path}" /grant "%USERNAME%:(OI)(CI)F"`,
218
+ ` PowerShell: icacls "${path}" /grant "$env:USERNAME:(OI)(CI)(WO)"`,
219
+ ` cmd: icacls "${path}" /grant "%USERNAME%:(OI)(CI)(WO)"`,
220
+ 'WRITE_OWNER is exactly the right the prerequisite names, so this grants nothing the harness did not ask for,',
221
+ 'and (OI)(CI) makes the ACE inheritable, so one command reaches the workspace\'s existing subdirectories.',
222
+ 'Full control works just as well — the same line with `F` in place of `(WO)`:',
223
+ ` icacls "${path}" /grant "$env:USERNAME:(OI)(CI)F"`,
224
+ 'Both assume you own the directory: owner-implicit rights cover the DACL half of the merged write, so',
225
+ 'WRITE_OWNER is the single missing piece. A directory owned by someone else is a bigger change than a',
226
+ 'one-liner — that is the harness\'s documented prerequisite, and it is why this failure is loud instead of',
227
+ 'silently skipped.',
185
228
  '',
186
229
  ...nonFixes(failure, path),
187
230
  'How to read this: the harness documents the prerequisite (' + PREREQUISITE + ') and this',
@@ -266,7 +309,7 @@ export function denialText(failure, observed, denial, maxDenials) {
266
309
  '',
267
310
  'Retrying cannot succeed — the sandbox cannot start a command until the directory grant applies.',
268
311
  'Stop, and either apply the fix or hand the problem to the user:',
269
- failure.path === undefined ? '' : ` icacls "${failure.path}" /grant "$env:USERNAME:(OI)(CI)F"`,
312
+ failure.path === undefined ? '' : ` icacls "${failure.path}" /grant "$env:USERNAME:(OI)(CI)(WO)"`,
270
313
  '',
271
314
  suffix > 0
272
315
  ? `This is automatic block ${String(denial)} of ${String(maxDenials)}; after that the call is allowed again.`
package/lib/signature.js CHANGED
@@ -30,6 +30,21 @@
30
30
  * the advisory can quote the exact line the model and the user are looking
31
31
  * at, and the path can be re-used in the fix command.
32
32
  *
33
+ * One signature covers two environments, and the text is identical in both, so
34
+ * the classifier cannot and must not try to tell them apart: a workspace the
35
+ * caller created with `mkdir` that inherits "Authenticated Users: Modify" from
36
+ * the drive root (`#7622`, `#7646`, `#7720`), and a directory on a data volume
37
+ * where **no ACE names the caller at all** so that the inherited entry is the
38
+ * whole of their access (`#7750` on D:/E:, `#7735`'s second defect). Both are
39
+ * the same gate — `WRITE_OWNER` on the directory — and the same one-line remedy
40
+ * satisfies both, which is why they share a class and the advisory names both
41
+ * shapes instead of guessing which one it is looking at. What the text *does*
42
+ * carry that the failure does not is the version boundary: the merged
43
+ * DACL + label write exists only from `0.1.7-alpha.1` on
44
+ * (`packages/sandbox/sandbox-windows-acl/src/acl.ts`, flag
45
+ * `DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION`), so on an older line
46
+ * the same string belongs to a different cause space.
47
+ *
33
48
  * ## The persistent-shell startup failure (`pty-startup`)
34
49
  *
35
50
  * `dsh-terminal-bash` throws `PTY shell exited during startup` when the shell
@@ -13,6 +13,13 @@
13
13
  * the caller can grant itself with `icacls`, unelevated. It is **not**
14
14
  * `SeSecurityPrivilege`, the token privilege the reports naturally reach for;
15
15
  * `whoami /priv` cannot show the difference, and elevation is the wrong lever.
16
+ * The remedy is the **narrowest** form of that grant — `(WO)` alone, which is
17
+ * literally the right the backend's prerequisite names — with Full control
18
+ * offered as the broad alternative; and the advisory carries the **version
19
+ * boundary** the label introduced (`0.1.7-alpha.1`), because that is what
20
+ * separates "this is the label failure" from "this is something else", and
21
+ * because rolling back is the reach it invites while making the very problem
22
+ * it closed come back.
16
23
  * - **The persistent-shell failure** (`pty-startup`) is *not* fixable by the
17
24
  * caller — least of all by the model, which has no shell to run anything in.
18
25
  * So its advice says so and stops: the remedy is a user-side preset choice,
@@ -34,7 +41,7 @@
34
41
  import type { ProvisioningFailure, RecognizedFailure } from './signature.js';
35
42
  import type { SandboxModeName } from './mode.js';
36
43
  /** The upstream threads the ACL advisory is a stopgap for. */
37
- export declare const ACL_DISCUSSIONS = "#7538 / #7622 / #7646 / #7720";
44
+ export declare const ACL_DISCUSSIONS = "#7538 / #7622 / #7646 / #7720 / #7750 / #7735";
38
45
  /** The upstream thread the persistent-shell advisory is a stopgap for. */
39
46
  export declare const PTY_DISCUSSIONS = "#7638";
40
47
  /** The documented prerequisite, quoted from the backend's README. */
@@ -30,6 +30,21 @@
30
30
  * the advisory can quote the exact line the model and the user are looking
31
31
  * at, and the path can be re-used in the fix command.
32
32
  *
33
+ * One signature covers two environments, and the text is identical in both, so
34
+ * the classifier cannot and must not try to tell them apart: a workspace the
35
+ * caller created with `mkdir` that inherits "Authenticated Users: Modify" from
36
+ * the drive root (`#7622`, `#7646`, `#7720`), and a directory on a data volume
37
+ * where **no ACE names the caller at all** so that the inherited entry is the
38
+ * whole of their access (`#7750` on D:/E:, `#7735`'s second defect). Both are
39
+ * the same gate — `WRITE_OWNER` on the directory — and the same one-line remedy
40
+ * satisfies both, which is why they share a class and the advisory names both
41
+ * shapes instead of guessing which one it is looking at. What the text *does*
42
+ * carry that the failure does not is the version boundary: the merged
43
+ * DACL + label write exists only from `0.1.7-alpha.1` on
44
+ * (`packages/sandbox/sandbox-windows-acl/src/acl.ts`, flag
45
+ * `DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION`), so on an older line
46
+ * the same string belongs to a different cause space.
47
+ *
33
48
  * ## The persistent-shell startup failure (`pty-startup`)
34
49
  *
35
50
  * `dsh-terminal-bash` throws `PTY shell exited during startup` when the shell
@@ -56,7 +71,13 @@
56
71
  */
57
72
  /** Which provisioning operation failed, and which diagnosis follows from it. */
58
73
  export type FailureClass =
59
- /** `SetNamedSecurityInfoW` returned `ERROR_ACCESS_DENIED` (5): the merged DACL + label write was refused. */
74
+ /**
75
+ * `SetNamedSecurityInfoW` returned `ERROR_ACCESS_DENIED` (5): the merged
76
+ * DACL + label write was refused. Both environments in the module doc land
77
+ * here — the `mkdir`-inherited Modify workspace and the data-volume directory
78
+ * with no ACE naming the caller — because the failure text cannot separate
79
+ * them and the remedy is the same `WRITE_OWNER` grant.
80
+ */
60
81
  'apply-denied'
61
82
  /** `SetNamedSecurityInfoW` failed with a Win32 code other than `ERROR_ACCESS_DENIED`. */
62
83
  | 'apply-other'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@argszero/cordis-plugin-sandbox-grant-advisor",
3
- "description": "Turns two sandbox environment failures that name neither their cause nor a remedy into a diagnosis with a path forward. Family 1, the Windows workspace ACL: four reports (#7538, #7622, #7646, #7720) of one signature — every sandboxed command fails before it runs with `SetNamedSecurityInfoW failed (Win32 5): grantWrite(<workspace>)`, because the merged DACL + mandatory-label write needs WRITE_OWNER on the directory (an object right the caller can self-grant), not SeSecurityPrivilege and not elevation; the host grant is materialized lazily and caches nothing on the failure path, so the same failure repeats per command. Family 2, the persistent shell (#7638): with the `minimal` preset under a confining sandbox mode every shell call dies instantly with `PTY shell exited during startup` because the terminal backend cannot create the pseudo-console inside the sandbox, retrying never helps, and `minimal` mounts no fallback shell tool. The plugin observes the public `tools/post-execute` waterfall, classifies both signatures narrowly (only the two `...NamedSecurityInfoW` operations; the PTY message matched on a whole line, never as a substring, and only under a mode the policy resolver reports as confining), and attaches ONE durable user-role advisory per agent per family through `additionalContexts`. The ACL advisory names the missing right, the discriminator, the unelevated `icacls` fix, and the two remedies that look right and are not (`takeown`, `icacls /reset`), each with the reason it fails; the PTY advisory names the failing combination, states the resolved mode, tells the model to stop rather than retry, and hands the user-side preset choice over — it never names a shell tool the failing composition does not mount. An optional, off-by-default `enforceAfter` refuses an identical ACL call this plugin has watched fail, bounded by `maxDenials`; the blocking half is ACL-only by design. It never edits an ACL, never elevates, and never changes a preset or a mode, and it complements repeat-guard-escalation, which keys on call identity rather than on the environment signature.",
4
- "version": "0.3.0",
3
+ "description": "Turns two sandbox environment failures that name neither their cause nor a remedy into a diagnosis with a path forward. Family 1, the Windows workspace ACL: six reports (#7538, #7622, #7646, #7720, #7750, #7735) of one signature — every sandboxed command fails before it runs with `SetNamedSecurityInfoW failed (Win32 5): grantWrite(<workspace>)`, because the merged DACL + mandatory-label write needs WRITE_OWNER on the directory (an object right the caller can self-grant), not SeSecurityPrivilege and not elevation; the host grant is materialized lazily and caches nothing on the failure path, so the same failure repeats per command. Family 2, the persistent shell (#7638): with the `minimal` preset under a confining sandbox mode every shell call dies instantly with `PTY shell exited during startup` because the terminal backend cannot create the pseudo-console inside the sandbox, retrying never helps, and `minimal` mounts no fallback shell tool. The plugin observes the public `tools/post-execute` waterfall, classifies both signatures narrowly (only the two `...NamedSecurityInfoW` operations; the PTY message matched on a whole line, never as a substring, and only under a mode the policy resolver reports as confining), and attaches ONE durable user-role advisory per agent per family through `additionalContexts`. The ACL advisory names the missing right, both environments the identical text can describe (an inherited Modify-only entry, and a data volume where no ACE names the caller at all), the version boundary that arrived with the mandatory label (0.1.7-alpha.1, flag 20, versus the DACL-only flag 4 up to 0.1.6-alpha.x) together with why downgrading is not the remedy, the discriminator, the unelevated `icacls ... :(OI)(CI)(WO)` fix — the narrowest form of the exact right the backend's prerequisite names, with Full control offered second — and the two remedies that look right and are not (`takeown`, `icacls /reset`), each with the reason it fails; the PTY advisory names the failing combination, states the resolved mode, tells the model to stop rather than retry, and hands the user-side preset choice over — it never names a shell tool the failing composition does not mount. An optional, off-by-default `enforceAfter` refuses an identical ACL call this plugin has watched fail, bounded by `maxDenials`; the blocking half is ACL-only by design. It never edits an ACL, never elevates, and never changes a preset or a mode, and it complements repeat-guard-escalation, which keys on call identity rather than on the environment signature.",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/types/index.d.ts",