@gotgenes/pi-permission-system 25.3.0 → 26.0.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 +48 -0
- package/README.md +14 -12
- package/config/config.example.json +1 -2
- package/dist/public.d.ts +37 -10
- package/docs/configuration.md +23 -19
- package/docs/cross-extension-api.md +43 -12
- package/docs/migration/0745-prompt-payload-contracts.md +68 -0
- package/docs/migration/0746-review-log-fields.md +69 -0
- package/docs/troubleshooting.md +2 -1
- package/package.json +1 -1
- package/schemas/permissions.schema.json +14 -4
- package/src/access-intent/tool-kind.ts +1 -1
- package/src/authority/approval-escalator.ts +32 -5
- package/src/authority/authorizer.ts +3 -3
- package/src/authority/forwarded-request-server.ts +0 -2
- package/src/authority/forwarding-io.ts +7 -5
- package/src/authority/permission-forwarding.ts +12 -3
- package/src/authority/permission-prompter.ts +5 -4
- package/src/config-loader.ts +31 -0
- package/src/config-schema.ts +13 -4
- package/src/extension-config.ts +8 -9
- package/src/handlers/gates/bash-external-directory.ts +1 -12
- package/src/handlers/gates/bash-path.ts +1 -10
- package/src/handlers/gates/descriptor.ts +26 -6
- package/src/handlers/gates/external-directory.ts +1 -13
- package/src/handlers/gates/helpers.ts +6 -7
- package/src/handlers/gates/path.ts +1 -9
- package/src/handlers/gates/runner.ts +62 -31
- package/src/handlers/gates/skill-input-gate-pipeline.ts +3 -14
- package/src/handlers/gates/skill-input.ts +1 -10
- package/src/handlers/gates/skill-read.ts +2 -11
- package/src/handlers/gates/tool-call-gate-pipeline.ts +1 -5
- package/src/handlers/gates/tool.ts +1 -11
- package/src/handlers/tool-call-boundary.ts +30 -7
- package/src/log-field-cap.ts +82 -0
- package/src/logging.ts +24 -3
- package/src/permission-events.ts +21 -2
- package/src/permission-prompts.ts +4 -3
- package/src/permission-request-id.ts +17 -0
- package/src/permission-session.ts +1 -1
- package/src/permission-ui-prompt.ts +4 -2
- package/src/presentation/agent-renderer.ts +215 -0
- package/src/presentation/dialog-renderer.ts +8 -64
- package/src/presentation/fact-vocabulary.ts +103 -0
- package/src/presentation/forwarded-ask-payload.ts +42 -17
- package/src/presentation/path-ask-payload.ts +8 -1
- package/src/presentation/prompt-payload.ts +165 -4
- package/src/presentation/review-log-renderer.ts +51 -0
- package/src/service.ts +11 -0
- package/src/tool-input-preview.ts +0 -1
- package/src/tool-preview-formatter.ts +18 -33
- package/src/denial-messages.ts +0 -269
- package/src/presentation/legacy-message.ts +0 -117
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { matchQualifier, resolvesToSuffix } from "#src/denial-messages";
|
|
2
|
-
import {
|
|
3
|
-
allEvidence,
|
|
4
|
-
findEvidence,
|
|
5
|
-
type PromptPayload,
|
|
6
|
-
} from "#src/presentation/prompt-payload";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Render the flat `message` string every consumer still reads.
|
|
10
|
-
*
|
|
11
|
-
* Transitional, and deliberately the *only* place that string is produced: it
|
|
12
|
-
* reads nothing but the payload, so the existing prompt-text tests are the
|
|
13
|
-
* proof that the payload carries everything the six former assemblers said.
|
|
14
|
-
*
|
|
15
|
-
* The bounded renderers replace it consumer by consumer — the dialog and the
|
|
16
|
-
* fallback first, then the wire and the broadcast, then the review log — and
|
|
17
|
-
* this module goes when the last `message` reader does. Its coupling to the
|
|
18
|
-
* evidence labels the builders emit is the price of that byte-for-byte
|
|
19
|
-
* equivalence, and it is why it is scoped to the transition.
|
|
20
|
-
*/
|
|
21
|
-
export function renderLegacyMessage(payload: PromptPayload): string {
|
|
22
|
-
const { request } = payload;
|
|
23
|
-
const subject = request.requester.agentName
|
|
24
|
-
? `Agent '${request.requester.agentName}'`
|
|
25
|
-
: "Current agent";
|
|
26
|
-
|
|
27
|
-
switch (payload.kind) {
|
|
28
|
-
case "bash":
|
|
29
|
-
return `${subject} requested bash command '${request.value}'${bashQualifier(payload)}${fullCommandSuffix(payload)}. Allow this command?`;
|
|
30
|
-
case "mcp":
|
|
31
|
-
return `${subject} requested MCP target '${request.value}'${patternSuffix(payload)}${inputSuffix(payload)}. Allow this call?`;
|
|
32
|
-
case "tool":
|
|
33
|
-
return `${subject} requested tool '${request.value}'${patternSuffix(payload)}${inputSuffix(payload)}. Allow this call?`;
|
|
34
|
-
case "path":
|
|
35
|
-
return `${subject} requested tool '${request.toolName}' for path '${request.value}'. Allow this path access?`;
|
|
36
|
-
case "external_directory":
|
|
37
|
-
return `${subject} requested tool '${request.toolName}' for path '${request.value}'${resolvedSuffix(payload)} outside working directory '${workingDirectory(payload)}'. Allow this external directory access?`;
|
|
38
|
-
case "bash_external_directory":
|
|
39
|
-
return `${subject} requested bash command '${request.value}' which references path(s) outside working directory '${workingDirectory(payload)}': ${externalPathList(payload)}. Allow this external directory access?`;
|
|
40
|
-
case "skill":
|
|
41
|
-
return `${subject} requested skill '${request.value}'. Allow loading this skill?`;
|
|
42
|
-
case "skill_read":
|
|
43
|
-
return `${subject} requested access to skill '${request.value}' via '${textOf(payload, "read path")}'. Allow this read?`;
|
|
44
|
-
case "forwarded":
|
|
45
|
-
return renderForwarded(payload);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ── Per-kind fragments ──────────────────────────────────────────────────────
|
|
50
|
-
|
|
51
|
-
/** The bash parenthetical: the matched rule plus, when nested, its context. */
|
|
52
|
-
function bashQualifier(payload: PromptPayload): string {
|
|
53
|
-
const qualifier = matchQualifier(
|
|
54
|
-
payload.request.matchedPattern ?? undefined,
|
|
55
|
-
payload.request.commandContext ?? undefined,
|
|
56
|
-
);
|
|
57
|
-
return qualifier ? ` ${qualifier}` : "";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** ` (matched '<pattern>')` for the non-bash surfaces. */
|
|
61
|
-
function patternSuffix(payload: PromptPayload): string {
|
|
62
|
-
const { matchedPattern } = payload.request;
|
|
63
|
-
return matchedPattern ? ` (matched '${matchedPattern}')` : "";
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** The enclosing command, when the gated unit is only part of it. */
|
|
67
|
-
function fullCommandSuffix(payload: PromptPayload): string {
|
|
68
|
-
const full = findEvidence(payload, "full command");
|
|
69
|
-
return full ? ` (full command: '${full.text}')` : "";
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** The per-tool input preview, when the formatter produced one. */
|
|
73
|
-
function inputSuffix(payload: PromptPayload): string {
|
|
74
|
-
const preview = findEvidence(payload, "input");
|
|
75
|
-
return preview ? ` ${preview.text}` : "";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** ` (resolves to '<canonical>')` when the alias names a distinct location. */
|
|
79
|
-
function resolvedSuffix(payload: PromptPayload): string {
|
|
80
|
-
return resolvesToSuffix(findEvidence(payload, "resolves to")?.text);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** The comma-joined external paths, each with its canonical alias. */
|
|
84
|
-
function externalPathList(payload: PromptPayload): string {
|
|
85
|
-
return allEvidence(payload, "external path")
|
|
86
|
-
.map(
|
|
87
|
-
(entry) => `${entry.text}${resolvesToSuffix(entry.detail ?? undefined)}`,
|
|
88
|
-
)
|
|
89
|
-
.join(", ");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function workingDirectory(payload: PromptPayload): string {
|
|
93
|
-
return textOf(payload, "working directory");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* The child's ask, prefixed with its provenance.
|
|
98
|
-
*
|
|
99
|
-
* Until the payload replaces `message` on the wire, a forwarded request carries
|
|
100
|
-
* the child's pre-rendered sentence, which arrives as a single evidence entry.
|
|
101
|
-
*/
|
|
102
|
-
function renderForwarded(payload: PromptPayload): string {
|
|
103
|
-
const { requester } = payload.request;
|
|
104
|
-
return [
|
|
105
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- || intentional: a version-skewed request carries "" rather than null
|
|
106
|
-
`Subagent '${requester.agentName || "unknown"}' requested permission.`,
|
|
107
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- || intentional: a version-skewed request carries "" rather than null
|
|
108
|
-
`Session ID: ${requester.sessionId || "unknown"}`,
|
|
109
|
-
"",
|
|
110
|
-
textOf(payload, "requested"),
|
|
111
|
-
].join("\n");
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** The text of an evidence entry the render requires, or the empty string. */
|
|
115
|
-
function textOf(payload: PromptPayload, label: string): string {
|
|
116
|
-
return findEvidence(payload, label)?.text ?? "";
|
|
117
|
-
}
|