@gotgenes/pi-permission-system 20.9.1 → 20.10.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/CHANGELOG.md +7 -0
- package/dist/public.d.ts +25 -4
- package/package.json +1 -1
- package/src/authority/authorizer-chain.ts +9 -7
- package/src/authority/authorizer-selection.ts +1 -0
- package/src/authority/authorizer.ts +5 -2
- package/src/authority/delegation-envelope.ts +4 -3
- package/src/service.ts +20 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [20.10.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v20.9.1...pi-permission-system-v20.10.0) (2026-07-21)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **pi-permission-system:** thread a review-log seam into the authorizer chain ([b086474](https://github.com/gotgenes/pi-packages/commit/b086474e91c79b02632ee76bbaa3e72e39f29e40))
|
|
14
|
+
|
|
8
15
|
## [20.9.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v20.9.0...pi-permission-system-v20.9.1) (2026-07-20)
|
|
9
16
|
|
|
10
17
|
|
package/dist/public.d.ts
CHANGED
|
@@ -217,10 +217,12 @@ type AuthorizerVerdict = {
|
|
|
217
217
|
* decide it or defer to the next link (ADR 0007). The chain injects a narrow,
|
|
218
218
|
* session-scoped {@link PermissionQuery} at `authorize` time (§3), so a link
|
|
219
219
|
* queries the deterministic engine at gate parity rather than reaching for the
|
|
220
|
-
* cross-extension service via `Symbol.for()`.
|
|
220
|
+
* cross-extension service via `Symbol.for()`. It also injects an
|
|
221
|
+
* {@link AuthorizerLog} so a link can record its decision trail to the shared
|
|
222
|
+
* permission review log (same §3 injection pattern).
|
|
221
223
|
*/
|
|
222
224
|
interface Authorizer {
|
|
223
|
-
authorize(details: PromptPermissionDetails, query: PermissionQuery): Promise<AuthorizerVerdict>;
|
|
225
|
+
authorize(details: PromptPermissionDetails, query: PermissionQuery, log: AuthorizerLog): Promise<AuthorizerVerdict>;
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
/**
|
|
@@ -257,6 +259,22 @@ type ToolInputFormatter = (input: Record<string, unknown>) => string | undefined
|
|
|
257
259
|
* reference — this ensures resilience across `/reload` and load-order edge cases.
|
|
258
260
|
*/
|
|
259
261
|
|
|
262
|
+
/**
|
|
263
|
+
* The narrow review-log seam handed to a chain link at `authorize` time
|
|
264
|
+
* (ADR 0007 §3, same injection pattern as {@link PermissionQuery}).
|
|
265
|
+
*
|
|
266
|
+
* A link uses it to record a positive decision trail to the permission review
|
|
267
|
+
* log — `review` for the durable, default-on audit entry (one per handled
|
|
268
|
+
* ask), `debug` for verbose or short-circuit detail gated behind the
|
|
269
|
+
* `debugLog` toggle. The session's own logger is passed straight through, so a
|
|
270
|
+
* link's entries land in the same `pi-permission-system-permission-review.jsonl`
|
|
271
|
+
* as the gate decisions, keying to a gate entry by `requestId`.
|
|
272
|
+
*/
|
|
273
|
+
interface AuthorizerLog {
|
|
274
|
+
review(event: string, details?: Record<string, unknown>): void;
|
|
275
|
+
debug(event: string, details?: Record<string, unknown>): void;
|
|
276
|
+
}
|
|
277
|
+
|
|
260
278
|
/**
|
|
261
279
|
* The narrow, read-only projection of {@link PermissionsService}: answer a
|
|
262
280
|
* policy query for a surface, and report a tool-level state. This is the
|
|
@@ -348,7 +366,10 @@ interface PermissionsService extends PermissionQuery {
|
|
|
348
366
|
* throws. The returned disposer unregisters the link.
|
|
349
367
|
*
|
|
350
368
|
* @param name - Operator-facing link name referenced from `authorizerChain`.
|
|
351
|
-
* @param authorize - The link's decision callback
|
|
369
|
+
* @param authorize - The link's decision callback
|
|
370
|
+
* (`(details, query, log) => verdict`); `log` is an
|
|
371
|
+
* {@link AuthorizerLog} for recording a decision trail to
|
|
372
|
+
* the shared permission review log.
|
|
352
373
|
*/
|
|
353
374
|
registerAuthorizer(name: string, authorize: Authorizer["authorize"]): () => void;
|
|
354
375
|
}
|
|
@@ -383,4 +404,4 @@ declare function getPermissionsService(): PermissionsService | undefined;
|
|
|
383
404
|
declare function unpublishPermissionsService(service: PermissionsService): void;
|
|
384
405
|
|
|
385
406
|
export { PERMISSIONS_DECISION_CHANNEL, PERMISSIONS_READY_CHANNEL, PERMISSIONS_UI_PROMPT_CHANNEL, getPermissionsService, publishPermissionsService, unpublishPermissionsService };
|
|
386
|
-
export type { Authorizer, AuthorizerVerdict, ForwardedPromptContext, PermissionCheckResult, PermissionDecisionEvent, PermissionQuery, PermissionState, PermissionUiPromptEvent, PermissionUiPromptSource, PermissionsReadyEvent, PermissionsService, PromptPermissionDetails, ToolInputFormatter };
|
|
407
|
+
export type { Authorizer, AuthorizerLog, AuthorizerVerdict, ForwardedPromptContext, PermissionCheckResult, PermissionDecisionEvent, PermissionQuery, PermissionState, PermissionUiPromptEvent, PermissionUiPromptSource, PermissionsReadyEvent, PermissionsService, PromptPermissionDetails, ToolInputFormatter };
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PermissionQuery } from "#src/service";
|
|
1
|
+
import type { AuthorizerLog, PermissionQuery } from "#src/service";
|
|
2
2
|
import type {
|
|
3
3
|
Authorizer,
|
|
4
4
|
AuthorizerVerdict,
|
|
@@ -16,16 +16,18 @@ import { createDeniedPermissionDecision } from "./permission-dialog";
|
|
|
16
16
|
* (returns a full decision), so a deferring link cannot occupy the terminal
|
|
17
17
|
* slot.
|
|
18
18
|
*
|
|
19
|
-
* Each link is handed the session-scoped `query`
|
|
20
|
-
* §3) so it queries the deterministic engine at gate
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* Each link is handed the session-scoped `query` and the review-log `log` at
|
|
20
|
+
* `authorize` time (ADR 0007 §3) so it queries the deterministic engine at gate
|
|
21
|
+
* parity and records its decision trail; the terminal receives neither. With
|
|
22
|
+
* zero links the composed chain **is** the terminal instance (identity), so
|
|
23
|
+
* behavior is byte-identical to the pre-chain spine — the empty-links case that
|
|
24
|
+
* ships until a link registers.
|
|
24
25
|
*/
|
|
25
26
|
export function composeAuthorizerChain(
|
|
26
27
|
links: readonly Authorizer[],
|
|
27
28
|
terminal: TerminalAuthorizer,
|
|
28
29
|
query: PermissionQuery,
|
|
30
|
+
log: AuthorizerLog,
|
|
29
31
|
): TerminalAuthorizer {
|
|
30
32
|
if (links.length === 0) {
|
|
31
33
|
return terminal;
|
|
@@ -33,7 +35,7 @@ export function composeAuthorizerChain(
|
|
|
33
35
|
return {
|
|
34
36
|
async authorize(details) {
|
|
35
37
|
for (const link of links) {
|
|
36
|
-
const verdict = await link.authorize(details, query);
|
|
38
|
+
const verdict = await link.authorize(details, query, log);
|
|
37
39
|
const decision = decideFromVerdict(verdict);
|
|
38
40
|
if (decision) {
|
|
39
41
|
return decision;
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
} from "#src/authority/permission-prompt-component";
|
|
7
7
|
import type { SubagentSessionRegistry } from "#src/authority/subagent-registry";
|
|
8
8
|
import type { PermissionEventBus } from "#src/permission-events";
|
|
9
|
-
import type { PermissionQuery } from "#src/service";
|
|
9
|
+
import type { AuthorizerLog, PermissionQuery } from "#src/service";
|
|
10
10
|
import type { DebugReviewLogger } from "#src/session-logger";
|
|
11
11
|
import { ParentAuthorizer } from "./approval-escalator";
|
|
12
12
|
import { DenyingAuthorizer } from "./denying-authorizer";
|
|
@@ -29,12 +29,15 @@ export type AuthorizerVerdict =
|
|
|
29
29
|
* decide it or defer to the next link (ADR 0007). The chain injects a narrow,
|
|
30
30
|
* session-scoped {@link PermissionQuery} at `authorize` time (§3), so a link
|
|
31
31
|
* queries the deterministic engine at gate parity rather than reaching for the
|
|
32
|
-
* cross-extension service via `Symbol.for()`.
|
|
32
|
+
* cross-extension service via `Symbol.for()`. It also injects an
|
|
33
|
+
* {@link AuthorizerLog} so a link can record its decision trail to the shared
|
|
34
|
+
* permission review log (same §3 injection pattern).
|
|
33
35
|
*/
|
|
34
36
|
export interface Authorizer {
|
|
35
37
|
authorize(
|
|
36
38
|
details: PromptPermissionDetails,
|
|
37
39
|
query: PermissionQuery,
|
|
40
|
+
log: AuthorizerLog,
|
|
38
41
|
): Promise<AuthorizerVerdict>;
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -26,13 +26,14 @@ export const DELEGATION_EXCLUDED_SURFACES: ReadonlySet<string> = new Set([
|
|
|
26
26
|
/**
|
|
27
27
|
* Wrap a link's `authorize` so an `allow` on an excluded surface is capped to
|
|
28
28
|
* `defer`. All other verdicts, and `allow`s on non-excluded surfaces, pass
|
|
29
|
-
* through unchanged. `details
|
|
29
|
+
* through unchanged. `details`, the injected `query`, and the review-log `log`
|
|
30
|
+
* are forwarded as-is.
|
|
30
31
|
*/
|
|
31
32
|
export function encloseInDelegationEnvelope(
|
|
32
33
|
authorize: Authorizer["authorize"],
|
|
33
34
|
): Authorizer["authorize"] {
|
|
34
|
-
return async (details, query) => {
|
|
35
|
-
const verdict = await authorize(details, query);
|
|
35
|
+
return async (details, query, log) => {
|
|
36
|
+
const verdict = await authorize(details, query, log);
|
|
36
37
|
if (verdict.kind === "allow" && isExcludedSurface(details)) {
|
|
37
38
|
return { kind: "defer" };
|
|
38
39
|
}
|
package/src/service.ts
CHANGED
|
@@ -20,6 +20,22 @@ export type {
|
|
|
20
20
|
Authorizer,
|
|
21
21
|
AuthorizerVerdict,
|
|
22
22
|
} from "./authority/authorizer";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The narrow review-log seam handed to a chain link at `authorize` time
|
|
26
|
+
* (ADR 0007 §3, same injection pattern as {@link PermissionQuery}).
|
|
27
|
+
*
|
|
28
|
+
* A link uses it to record a positive decision trail to the permission review
|
|
29
|
+
* log — `review` for the durable, default-on audit entry (one per handled
|
|
30
|
+
* ask), `debug` for verbose or short-circuit detail gated behind the
|
|
31
|
+
* `debugLog` toggle. The session's own logger is passed straight through, so a
|
|
32
|
+
* link's entries land in the same `pi-permission-system-permission-review.jsonl`
|
|
33
|
+
* as the gate decisions, keying to a gate entry by `requestId`.
|
|
34
|
+
*/
|
|
35
|
+
export interface AuthorizerLog {
|
|
36
|
+
review(event: string, details?: Record<string, unknown>): void;
|
|
37
|
+
debug(event: string, details?: Record<string, unknown>): void;
|
|
38
|
+
}
|
|
23
39
|
export type { PromptPermissionDetails } from "./authority/permission-prompter";
|
|
24
40
|
export type {
|
|
25
41
|
ForwardedPromptContext,
|
|
@@ -143,7 +159,10 @@ export interface PermissionsService extends PermissionQuery {
|
|
|
143
159
|
* throws. The returned disposer unregisters the link.
|
|
144
160
|
*
|
|
145
161
|
* @param name - Operator-facing link name referenced from `authorizerChain`.
|
|
146
|
-
* @param authorize - The link's decision callback
|
|
162
|
+
* @param authorize - The link's decision callback
|
|
163
|
+
* (`(details, query, log) => verdict`); `log` is an
|
|
164
|
+
* {@link AuthorizerLog} for recording a decision trail to
|
|
165
|
+
* the shared permission review log.
|
|
147
166
|
*/
|
|
148
167
|
registerAuthorizer(
|
|
149
168
|
name: string,
|