@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.
Files changed (53) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +14 -12
  3. package/config/config.example.json +1 -2
  4. package/dist/public.d.ts +37 -10
  5. package/docs/configuration.md +23 -19
  6. package/docs/cross-extension-api.md +43 -12
  7. package/docs/migration/0745-prompt-payload-contracts.md +68 -0
  8. package/docs/migration/0746-review-log-fields.md +69 -0
  9. package/docs/troubleshooting.md +2 -1
  10. package/package.json +1 -1
  11. package/schemas/permissions.schema.json +14 -4
  12. package/src/access-intent/tool-kind.ts +1 -1
  13. package/src/authority/approval-escalator.ts +32 -5
  14. package/src/authority/authorizer.ts +3 -3
  15. package/src/authority/forwarded-request-server.ts +0 -2
  16. package/src/authority/forwarding-io.ts +7 -5
  17. package/src/authority/permission-forwarding.ts +12 -3
  18. package/src/authority/permission-prompter.ts +5 -4
  19. package/src/config-loader.ts +31 -0
  20. package/src/config-schema.ts +13 -4
  21. package/src/extension-config.ts +8 -9
  22. package/src/handlers/gates/bash-external-directory.ts +1 -12
  23. package/src/handlers/gates/bash-path.ts +1 -10
  24. package/src/handlers/gates/descriptor.ts +26 -6
  25. package/src/handlers/gates/external-directory.ts +1 -13
  26. package/src/handlers/gates/helpers.ts +6 -7
  27. package/src/handlers/gates/path.ts +1 -9
  28. package/src/handlers/gates/runner.ts +62 -31
  29. package/src/handlers/gates/skill-input-gate-pipeline.ts +3 -14
  30. package/src/handlers/gates/skill-input.ts +1 -10
  31. package/src/handlers/gates/skill-read.ts +2 -11
  32. package/src/handlers/gates/tool-call-gate-pipeline.ts +1 -5
  33. package/src/handlers/gates/tool.ts +1 -11
  34. package/src/handlers/tool-call-boundary.ts +30 -7
  35. package/src/log-field-cap.ts +82 -0
  36. package/src/logging.ts +24 -3
  37. package/src/permission-events.ts +21 -2
  38. package/src/permission-prompts.ts +4 -3
  39. package/src/permission-request-id.ts +17 -0
  40. package/src/permission-session.ts +1 -1
  41. package/src/permission-ui-prompt.ts +4 -2
  42. package/src/presentation/agent-renderer.ts +215 -0
  43. package/src/presentation/dialog-renderer.ts +8 -64
  44. package/src/presentation/fact-vocabulary.ts +103 -0
  45. package/src/presentation/forwarded-ask-payload.ts +42 -17
  46. package/src/presentation/path-ask-payload.ts +8 -1
  47. package/src/presentation/prompt-payload.ts +165 -4
  48. package/src/presentation/review-log-renderer.ts +51 -0
  49. package/src/service.ts +11 -0
  50. package/src/tool-input-preview.ts +0 -1
  51. package/src/tool-preview-formatter.ts +18 -33
  52. package/src/denial-messages.ts +0 -269
  53. 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
- }