@gotgenes/pi-permission-system 25.2.2 → 25.3.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 +15 -0
- package/README.md +1 -1
- package/config/config.example.json +3 -0
- package/dist/public.d.ts +156 -41
- package/docs/configuration.md +17 -2
- package/package.json +1 -1
- package/schemas/permissions.schema.json +16 -0
- package/src/access-intent/bash/command-enumeration.ts +45 -117
- package/src/access-intent/bash/wrapper-analysis.ts +335 -0
- package/src/authority/forwarded-request-server.ts +5 -14
- package/src/authority/local-user-authorizer.ts +2 -3
- package/src/authority/permission-prompt-component.ts +87 -47
- package/src/authority/permission-prompter.ts +9 -0
- package/src/config-loader.ts +2 -0
- package/src/config-schema.ts +14 -0
- package/src/extension-config.ts +10 -0
- package/src/handlers/gates/bash-command.ts +7 -2
- package/src/handlers/gates/bash-external-directory.ts +11 -6
- package/src/handlers/gates/bash-path.ts +10 -6
- package/src/handlers/gates/external-directory.ts +13 -8
- package/src/handlers/gates/path.ts +11 -14
- package/src/handlers/gates/skill-input.ts +5 -2
- package/src/handlers/gates/skill-read.ts +5 -6
- package/src/handlers/gates/tool.ts +10 -5
- package/src/index.ts +2 -0
- package/src/permission-prompts.ts +4 -72
- package/src/presentation/dialog-renderer.ts +404 -0
- package/src/presentation/forwarded-ask-payload.ts +45 -0
- package/src/presentation/legacy-message.ts +117 -0
- package/src/presentation/line-fitting.ts +27 -0
- package/src/presentation/path-ask-payload.ts +128 -0
- package/src/presentation/prompt-payload.ts +137 -0
- package/src/presentation/skill-ask-payload.ts +50 -0
- package/src/presentation/tool-ask-payload.ts +104 -0
- package/src/tool-preview-formatter.ts +1 -1
- package/src/types.ts +6 -0
- package/src/handlers/gates/external-directory-messages.ts +0 -28
package/src/config-schema.ts
CHANGED
|
@@ -194,6 +194,20 @@ export const unifiedConfigSchema = z
|
|
|
194
194
|
"How long a subagent waits for the parent session to answer a forwarded permission request, in milliseconds.\n\nOmit to use the default (`600000`, ten minutes). A child whose in-process parent is not draining its inbox at all gives up in a couple of seconds regardless of this value, so lower it only to bound how long you are willing to leave an *unanswered* prompt pending.",
|
|
195
195
|
default: 600000,
|
|
196
196
|
}),
|
|
197
|
+
promptMaxRows: z.number().int().min(1).optional().meta({
|
|
198
|
+
description:
|
|
199
|
+
"Maximum rows a permission prompt renders before eliding its evidence. Omit to use the default (24).",
|
|
200
|
+
markdownDescription:
|
|
201
|
+
"Maximum rows a permission prompt renders before eliding its evidence.\n\nOmit to use the default (24). The request's own facts — the requesting agent, the tool, the matched rule, the decision-relevant value — are never elided by this budget; what gives way is the supporting evidence, and `Ctrl+O` expands the prompt to the complete request.",
|
|
202
|
+
default: 24,
|
|
203
|
+
}),
|
|
204
|
+
promptFieldMaxWidth: z.number().int().min(1).optional().meta({
|
|
205
|
+
description:
|
|
206
|
+
"Maximum characters of any one field shown in a permission prompt. Omit to use the default (400).",
|
|
207
|
+
markdownDescription:
|
|
208
|
+
"Maximum characters of any one field shown in a permission prompt.\n\nOmit to use the default (400). This is what bounds a single pathological field — a long here-string command, say — that would otherwise fill the prompt through wrapping. A shortened field is marked with an ellipsis, and `Ctrl+O` shows it in full.",
|
|
209
|
+
default: 400,
|
|
210
|
+
}),
|
|
197
211
|
toolInputPreviewMaxLength: z.number().int().min(1).optional().meta({
|
|
198
212
|
description:
|
|
199
213
|
"Maximum character length of the inline-JSON tool-input preview shown in permission prompts. Omit to use the default (200). Set to a large value to disable truncation.",
|
package/src/extension-config.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface PermissionSystemExtensionConfig {
|
|
|
22
22
|
piInfrastructureReadPaths?: string[];
|
|
23
23
|
/** How long a subagent waits for the parent's answer to a forwarded ask, in ms. Defaults to 600000. */
|
|
24
24
|
forwardingTimeoutMs?: number;
|
|
25
|
+
/** Max rows a permission prompt renders before eliding its evidence. Defaults to 24. */
|
|
26
|
+
promptMaxRows?: number;
|
|
27
|
+
/** Max characters of any one field shown in a permission prompt. Defaults to 400. */
|
|
28
|
+
promptFieldMaxWidth?: number;
|
|
25
29
|
/** Max length of the inline-JSON input preview shown in permission prompts. Defaults to 200. */
|
|
26
30
|
toolInputPreviewMaxLength?: number;
|
|
27
31
|
/** Max length of inline pattern/path summaries (grep/find/ls) in permission prompts. Defaults to 80. */
|
|
@@ -76,6 +80,12 @@ export function normalizePermissionSystemConfig(
|
|
|
76
80
|
if (raw.forwardingTimeoutMs !== undefined) {
|
|
77
81
|
result.forwardingTimeoutMs = raw.forwardingTimeoutMs;
|
|
78
82
|
}
|
|
83
|
+
if (raw.promptMaxRows !== undefined) {
|
|
84
|
+
result.promptMaxRows = raw.promptMaxRows;
|
|
85
|
+
}
|
|
86
|
+
if (raw.promptFieldMaxWidth !== undefined) {
|
|
87
|
+
result.promptFieldMaxWidth = raw.promptFieldMaxWidth;
|
|
88
|
+
}
|
|
79
89
|
if (raw.toolInputPreviewMaxLength !== undefined) {
|
|
80
90
|
result.toolInputPreviewMaxLength = raw.toolInputPreviewMaxLength;
|
|
81
91
|
}
|
|
@@ -83,7 +83,7 @@ export function resolveBashCommandCheck(
|
|
|
83
83
|
input: { command: cmd.text },
|
|
84
84
|
agentName,
|
|
85
85
|
});
|
|
86
|
-
const
|
|
86
|
+
const floored =
|
|
87
87
|
cmd.wrapperKind && base.state === "allow"
|
|
88
88
|
? {
|
|
89
89
|
...base,
|
|
@@ -91,7 +91,12 @@ export function resolveBashCommandCheck(
|
|
|
91
91
|
matchedPattern: WRAPPER_SENTINEL[cmd.wrapperKind],
|
|
92
92
|
}
|
|
93
93
|
: base;
|
|
94
|
-
|
|
94
|
+
const result = cmd.context
|
|
95
|
+
? { ...floored, commandContext: cmd.context }
|
|
96
|
+
: floored;
|
|
97
|
+
return cmd.executedUnit === undefined
|
|
98
|
+
? result
|
|
99
|
+
: { ...result, executedUnit: cmd.executedUnit };
|
|
95
100
|
});
|
|
96
101
|
return (
|
|
97
102
|
pickMostRestrictive(results) ??
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { BashProgram } from "#src/access-intent/bash/program";
|
|
2
2
|
import type { ScopedPermissionResolver } from "#src/permission-resolver";
|
|
3
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
4
|
+
import { buildBashExternalDirectoryAskPayload } from "#src/presentation/path-ask-payload";
|
|
3
5
|
import { SessionApproval } from "#src/session-approval";
|
|
4
6
|
import { deriveApprovalPattern } from "#src/session-rules";
|
|
5
7
|
import type { GateResult } from "./descriptor";
|
|
6
|
-
import { formatBashExternalDirectoryAskPrompt } from "./external-directory-messages";
|
|
7
8
|
import { selectUncoveredExternalPaths } from "./external-directory-policy";
|
|
8
9
|
import { accessFactsFromPath } from "./helpers";
|
|
9
10
|
import type { ToolCallContext } from "./types";
|
|
@@ -76,12 +77,15 @@ export function describeBashExternalDirectoryGate(
|
|
|
76
77
|
resolvedPath: path.resolvedAlias(),
|
|
77
78
|
}));
|
|
78
79
|
|
|
79
|
-
const
|
|
80
|
+
const payload = buildBashExternalDirectoryAskPayload({
|
|
80
81
|
command,
|
|
81
|
-
disclosures,
|
|
82
|
-
tcc.cwd,
|
|
83
|
-
tcc.agentName
|
|
84
|
-
|
|
82
|
+
externalPaths: disclosures,
|
|
83
|
+
cwd: tcc.cwd,
|
|
84
|
+
agentName: tcc.agentName,
|
|
85
|
+
toolName: tcc.toolName,
|
|
86
|
+
matchedPattern: preCheck.matchedPattern,
|
|
87
|
+
});
|
|
88
|
+
const bashExtMessage = renderLegacyMessage(payload);
|
|
85
89
|
|
|
86
90
|
const patterns = uncoveredPaths.map((p) => deriveApprovalPattern(p));
|
|
87
91
|
|
|
@@ -100,6 +104,7 @@ export function describeBashExternalDirectoryGate(
|
|
|
100
104
|
source: "tool_call",
|
|
101
105
|
agentName: tcc.agentName,
|
|
102
106
|
message: bashExtMessage,
|
|
107
|
+
payload,
|
|
103
108
|
toolCallId: tcc.toolCallId,
|
|
104
109
|
toolName: tcc.toolName,
|
|
105
110
|
command,
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { AccessPath } from "#src/access-intent/access-path";
|
|
2
2
|
import type { BashProgram } from "#src/access-intent/bash/program";
|
|
3
3
|
import type { ScopedPermissionResolver } from "#src/permission-resolver";
|
|
4
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
5
|
+
import { buildPathAskPayload } from "#src/presentation/path-ask-payload";
|
|
4
6
|
import { SessionApproval } from "#src/session-approval";
|
|
5
7
|
import { deriveApprovalPattern } from "#src/session-rules";
|
|
6
8
|
import type { PermissionCheckResult } from "#src/types";
|
|
7
9
|
import { pickMostRestrictive } from "./candidate-check";
|
|
8
10
|
import type { GateResult } from "./descriptor";
|
|
9
11
|
import { accessFactsFromPath } from "./helpers";
|
|
10
|
-
import { formatPathAskPrompt } from "./path";
|
|
11
12
|
import type { ToolCallContext } from "./types";
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -113,11 +114,13 @@ export function describeBashPathGate(
|
|
|
113
114
|
// path), so it matches the values a later call produces. For an unknown base
|
|
114
115
|
// (`forLiteral`) `value()` is the raw token.
|
|
115
116
|
const pattern = deriveApprovalPattern(worstEntry.path.value());
|
|
116
|
-
const
|
|
117
|
-
tcc.toolName,
|
|
118
|
-
worstToken,
|
|
119
|
-
tcc.agentName
|
|
120
|
-
|
|
117
|
+
const payload = buildPathAskPayload({
|
|
118
|
+
toolName: tcc.toolName,
|
|
119
|
+
pathValue: worstToken,
|
|
120
|
+
agentName: tcc.agentName,
|
|
121
|
+
matchedPattern: worstCheck.matchedPattern,
|
|
122
|
+
});
|
|
123
|
+
const askMessage = renderLegacyMessage(payload);
|
|
121
124
|
|
|
122
125
|
return {
|
|
123
126
|
surface: "path",
|
|
@@ -133,6 +136,7 @@ export function describeBashPathGate(
|
|
|
133
136
|
source: "tool_call",
|
|
134
137
|
agentName: tcc.agentName,
|
|
135
138
|
message: askMessage,
|
|
139
|
+
payload,
|
|
136
140
|
toolCallId: tcc.toolCallId,
|
|
137
141
|
toolName: tcc.toolName,
|
|
138
142
|
command,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getToolInputPath } from "#src/access-intent/tool-input-path";
|
|
2
2
|
import type { PathNormalizer } from "#src/path-normalizer";
|
|
3
3
|
import type { ScopedPermissionResolver } from "#src/permission-resolver";
|
|
4
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
5
|
+
import { buildExternalDirectoryAskPayload } from "#src/presentation/path-ask-payload";
|
|
4
6
|
import { SessionApproval } from "#src/session-approval";
|
|
5
7
|
import { deriveApprovalPattern } from "#src/session-rules";
|
|
6
8
|
import type { ToolAccessExtractorLookup } from "#src/tool-access-extractor-registry";
|
|
7
9
|
import type { GateResult } from "./descriptor";
|
|
8
|
-
import { formatExternalDirectoryAskPrompt } from "./external-directory-messages";
|
|
9
10
|
import { resolveExternalDirectoryPolicy } from "./external-directory-policy";
|
|
10
11
|
import { accessFactsFromPath } from "./helpers";
|
|
11
12
|
import type { ToolCallContext } from "./types";
|
|
@@ -69,13 +70,6 @@ export function describeExternalDirectoryGate(
|
|
|
69
70
|
|
|
70
71
|
// ── Build descriptor for permission check ───────────────────────────────
|
|
71
72
|
const resolvedAlias = accessPath.resolvedAlias();
|
|
72
|
-
const extDirMessage = formatExternalDirectoryAskPrompt(
|
|
73
|
-
tcc.toolName,
|
|
74
|
-
externalDirectoryPath,
|
|
75
|
-
resolvedAlias,
|
|
76
|
-
tcc.cwd,
|
|
77
|
-
tcc.agentName ?? undefined,
|
|
78
|
-
);
|
|
79
73
|
|
|
80
74
|
// The runner consumes this preCheck and skips its own resolve.
|
|
81
75
|
const preCheck = resolveExternalDirectoryPolicy(
|
|
@@ -85,6 +79,16 @@ export function describeExternalDirectoryGate(
|
|
|
85
79
|
);
|
|
86
80
|
const pattern = deriveApprovalPattern(accessPath.value());
|
|
87
81
|
|
|
82
|
+
const payload = buildExternalDirectoryAskPayload({
|
|
83
|
+
toolName: tcc.toolName,
|
|
84
|
+
pathValue: externalDirectoryPath,
|
|
85
|
+
resolvedPath: resolvedAlias,
|
|
86
|
+
cwd: tcc.cwd,
|
|
87
|
+
agentName: tcc.agentName,
|
|
88
|
+
matchedPattern: preCheck.matchedPattern,
|
|
89
|
+
});
|
|
90
|
+
const extDirMessage = renderLegacyMessage(payload);
|
|
91
|
+
|
|
88
92
|
return {
|
|
89
93
|
surface: "external_directory",
|
|
90
94
|
input: {},
|
|
@@ -102,6 +106,7 @@ export function describeExternalDirectoryGate(
|
|
|
102
106
|
source: "tool_call",
|
|
103
107
|
agentName: tcc.agentName,
|
|
104
108
|
message: extDirMessage,
|
|
109
|
+
payload,
|
|
105
110
|
toolCallId: tcc.toolCallId,
|
|
106
111
|
toolName: tcc.toolName,
|
|
107
112
|
path: externalDirectoryPath,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getToolInputPath } from "#src/access-intent/tool-input-path";
|
|
2
2
|
import type { PathNormalizer } from "#src/path-normalizer";
|
|
3
3
|
import type { ScopedPermissionResolver } from "#src/permission-resolver";
|
|
4
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
5
|
+
import { buildPathAskPayload } from "#src/presentation/path-ask-payload";
|
|
4
6
|
import { SessionApproval } from "#src/session-approval";
|
|
5
7
|
import { deriveApprovalPattern } from "#src/session-rules";
|
|
6
8
|
import type { ToolAccessExtractorLookup } from "#src/tool-access-extractor-registry";
|
|
@@ -47,6 +49,13 @@ export function describePathGate(
|
|
|
47
49
|
// the policy values a later call produces.
|
|
48
50
|
const pattern = deriveApprovalPattern(accessPath.value());
|
|
49
51
|
|
|
52
|
+
const payload = buildPathAskPayload({
|
|
53
|
+
toolName: tcc.toolName,
|
|
54
|
+
pathValue: filePath,
|
|
55
|
+
agentName: tcc.agentName,
|
|
56
|
+
matchedPattern: check.matchedPattern,
|
|
57
|
+
});
|
|
58
|
+
|
|
50
59
|
const descriptor: GateDescriptor = {
|
|
51
60
|
surface: "path",
|
|
52
61
|
input: { path: filePath },
|
|
@@ -60,11 +69,8 @@ export function describePathGate(
|
|
|
60
69
|
promptDetails: {
|
|
61
70
|
source: "tool_call",
|
|
62
71
|
agentName: tcc.agentName,
|
|
63
|
-
message:
|
|
64
|
-
|
|
65
|
-
filePath,
|
|
66
|
-
tcc.agentName ?? undefined,
|
|
67
|
-
),
|
|
72
|
+
message: renderLegacyMessage(payload),
|
|
73
|
+
payload,
|
|
68
74
|
toolCallId: tcc.toolCallId,
|
|
69
75
|
toolName: tcc.toolName,
|
|
70
76
|
path: filePath,
|
|
@@ -86,12 +92,3 @@ export function describePathGate(
|
|
|
86
92
|
|
|
87
93
|
return descriptor;
|
|
88
94
|
}
|
|
89
|
-
|
|
90
|
-
export function formatPathAskPrompt(
|
|
91
|
-
toolName: string,
|
|
92
|
-
pathValue: string,
|
|
93
|
-
agentName?: string,
|
|
94
|
-
): string {
|
|
95
|
-
const subject = agentName ? `Agent '${agentName}'` : "Current agent";
|
|
96
|
-
return `${subject} requested tool '${toolName}' for path '${pathValue}'. Allow this path access?`;
|
|
97
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
2
|
+
import { buildSkillAskPayload } from "#src/presentation/skill-ask-payload";
|
|
2
3
|
import type { PermissionCheckResult } from "#src/types";
|
|
3
4
|
import type { GateDescriptor } from "./descriptor";
|
|
4
5
|
import { accessFactsFromValue } from "./helpers";
|
|
@@ -15,7 +16,8 @@ export function describeSkillInputGate(
|
|
|
15
16
|
agentName: string | null,
|
|
16
17
|
preCheck: PermissionCheckResult,
|
|
17
18
|
): GateDescriptor {
|
|
18
|
-
const
|
|
19
|
+
const payload = buildSkillAskPayload(skillName, agentName);
|
|
20
|
+
const message = renderLegacyMessage(payload);
|
|
19
21
|
return {
|
|
20
22
|
surface: "skill",
|
|
21
23
|
input: { name: skillName },
|
|
@@ -29,6 +31,7 @@ export function describeSkillInputGate(
|
|
|
29
31
|
source: "skill_input",
|
|
30
32
|
agentName,
|
|
31
33
|
message,
|
|
34
|
+
payload,
|
|
32
35
|
skillName,
|
|
33
36
|
accessIntent: accessFactsFromValue("skill", skillName),
|
|
34
37
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PathNormalizer } from "#src/path-normalizer";
|
|
2
|
-
import {
|
|
2
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
3
|
+
import { buildSkillPathAskPayload } from "#src/presentation/skill-ask-payload";
|
|
3
4
|
import type { SkillPromptEntry } from "#src/skill-prompt-sanitizer";
|
|
4
5
|
import { findSkillPathMatch } from "#src/skill-prompt-sanitizer";
|
|
5
6
|
import { toRecord } from "#src/value-guards";
|
|
@@ -42,11 +43,8 @@ export function describeSkillReadGate(
|
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
path,
|
|
48
|
-
tcc.agentName ?? undefined,
|
|
49
|
-
);
|
|
46
|
+
const payload = buildSkillPathAskPayload(matchedSkill, path, tcc.agentName);
|
|
47
|
+
const skillReadMessage = renderLegacyMessage(payload);
|
|
50
48
|
|
|
51
49
|
return {
|
|
52
50
|
surface: "skill",
|
|
@@ -61,6 +59,7 @@ export function describeSkillReadGate(
|
|
|
61
59
|
source: "skill_read",
|
|
62
60
|
agentName: tcc.agentName,
|
|
63
61
|
message: skillReadMessage,
|
|
62
|
+
payload,
|
|
64
63
|
toolCallId: tcc.toolCallId,
|
|
65
64
|
toolName: tcc.toolName,
|
|
66
65
|
skillName: matchedSkill.name,
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
type ShellInvocation,
|
|
7
7
|
} from "#src/access-intent/tool-kind";
|
|
8
8
|
import { suggestSessionPattern } from "#src/pattern-suggest";
|
|
9
|
-
import {
|
|
9
|
+
import { renderLegacyMessage } from "#src/presentation/legacy-message";
|
|
10
|
+
import { buildToolAskPayload } from "#src/presentation/tool-ask-payload";
|
|
10
11
|
import { SessionApproval } from "#src/session-approval";
|
|
11
12
|
import type { ToolPreviewFormatter } from "#src/tool-preview-formatter";
|
|
12
13
|
import type { PermissionCheckResult } from "#src/types";
|
|
@@ -72,12 +73,15 @@ export function describeToolGate(
|
|
|
72
73
|
deriveSuggestionValue(gateSurface, check, accessPath),
|
|
73
74
|
);
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
+
const payload = buildToolAskPayload({
|
|
76
77
|
check,
|
|
77
|
-
tcc.agentName
|
|
78
|
-
|
|
78
|
+
agentName: tcc.agentName,
|
|
79
|
+
surface: gateSurface,
|
|
80
|
+
invokedToolName: tcc.toolName,
|
|
81
|
+
input: tcc.input,
|
|
79
82
|
formatter,
|
|
80
|
-
);
|
|
83
|
+
});
|
|
84
|
+
const askMessage = renderLegacyMessage(payload);
|
|
81
85
|
|
|
82
86
|
const decisionValue = deriveDecisionValue(
|
|
83
87
|
gateSurface,
|
|
@@ -108,6 +112,7 @@ export function describeToolGate(
|
|
|
108
112
|
source: "tool_call",
|
|
109
113
|
agentName: tcc.agentName,
|
|
110
114
|
message: askMessage,
|
|
115
|
+
payload,
|
|
111
116
|
toolCallId: tcc.toolCallId,
|
|
112
117
|
toolName: tcc.toolName,
|
|
113
118
|
sessionLabel: suggestion.label,
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ import { PermissionManager } from "./permission-manager";
|
|
|
38
38
|
import { PermissionResolver } from "./permission-resolver";
|
|
39
39
|
import { PermissionSession } from "./permission-session";
|
|
40
40
|
import { LocalPermissionsService } from "./permissions-service";
|
|
41
|
+
import { resolveRenderBudget } from "./presentation/dialog-renderer";
|
|
41
42
|
import { PermissionServiceLifecycle } from "./service-lifecycle";
|
|
42
43
|
import { PermissionSessionLogger } from "./session-logger";
|
|
43
44
|
import { SessionRules } from "./session-rules";
|
|
@@ -115,6 +116,7 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
115
116
|
events: pi.events,
|
|
116
117
|
getPromptPreferences: () => ({
|
|
117
118
|
doublePressToConfirm: configStore.current().doublePressToConfirm,
|
|
119
|
+
budget: resolveRenderBudget(configStore.current()),
|
|
118
120
|
}),
|
|
119
121
|
requestPermissionDecision,
|
|
120
122
|
forwardingDir: paths.forwardingDir,
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { classifyToolKind
|
|
2
|
-
import { matchQualifier } from "./denial-messages";
|
|
3
|
-
import type { SkillPromptEntry } from "./skill-prompt-sanitizer";
|
|
4
|
-
import type { ToolPreviewFormatter } from "./tool-preview-formatter";
|
|
5
|
-
import type { PermissionCheckResult } from "./types";
|
|
6
|
-
import { getNonEmptyString, toRecord } from "./value-guards";
|
|
1
|
+
import { classifyToolKind } from "./access-intent/tool-kind";
|
|
7
2
|
|
|
8
|
-
// NOTE:
|
|
9
|
-
//
|
|
10
|
-
//
|
|
3
|
+
// NOTE: the ask prompts are now payload builders under src/presentation/;
|
|
4
|
+
// denial text lives in denial-messages.ts. This module retains only the
|
|
5
|
+
// pre-check reasons, which are agent-facing rather than user-facing.
|
|
11
6
|
|
|
12
7
|
export function formatMissingToolNameReason(): string {
|
|
13
8
|
return "Tool call was blocked because no tool name was provided. Use a registered tool name from pi.getAllTools().";
|
|
@@ -29,66 +24,3 @@ export function formatUnknownToolReason(
|
|
|
29
24
|
|
|
30
25
|
return `Tool '${toolName}' is not registered in this runtime and was blocked before permission checks.${mcpHint} Registered tools: ${availableList}.`;
|
|
31
26
|
}
|
|
32
|
-
|
|
33
|
-
export function formatAskPrompt(
|
|
34
|
-
result: PermissionCheckResult,
|
|
35
|
-
agentName?: string,
|
|
36
|
-
input?: unknown,
|
|
37
|
-
formatter?: ToolPreviewFormatter,
|
|
38
|
-
): string {
|
|
39
|
-
const subject = agentName ? `Agent '${agentName}'` : "Current agent";
|
|
40
|
-
|
|
41
|
-
if (classifyToolKind(result.toolName) === "bash") {
|
|
42
|
-
const subCommand = result.command ?? "";
|
|
43
|
-
const qualifier = matchQualifier(
|
|
44
|
-
result.matchedPattern,
|
|
45
|
-
result.commandContext,
|
|
46
|
-
);
|
|
47
|
-
const qualifierInfo = qualifier ? ` ${qualifier}` : "";
|
|
48
|
-
const fullCommand = getNonEmptyString(toRecord(input).command);
|
|
49
|
-
const fullCommandInfo =
|
|
50
|
-
fullCommand && fullCommand !== subCommand
|
|
51
|
-
? ` (full command: '${fullCommand}')`
|
|
52
|
-
: "";
|
|
53
|
-
return `${subject} requested bash command '${subCommand}'${qualifierInfo}${fullCommandInfo}. Allow this command?`;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (isMcpCheck(result) && result.target) {
|
|
57
|
-
const patternInfo = result.matchedPattern
|
|
58
|
-
? ` (matched '${result.matchedPattern}')`
|
|
59
|
-
: "";
|
|
60
|
-
const mcpPreview = formatter
|
|
61
|
-
? formatter.formatToolInputForPrompt("mcp", input)
|
|
62
|
-
: "";
|
|
63
|
-
const previewSuffix = mcpPreview ? ` ${mcpPreview}` : "";
|
|
64
|
-
return `${subject} requested MCP target '${result.target}'${patternInfo}${previewSuffix}. Allow this call?`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const patternInfo = result.matchedPattern
|
|
68
|
-
? ` (matched '${result.matchedPattern}')`
|
|
69
|
-
: "";
|
|
70
|
-
const inputPreview = formatter
|
|
71
|
-
? formatter.formatToolInputForPrompt(result.toolName, input)
|
|
72
|
-
: "";
|
|
73
|
-
const inputSuffix = inputPreview ? ` ${inputPreview}` : "";
|
|
74
|
-
return `${subject} requested tool '${result.toolName}'${patternInfo}${inputSuffix}. Allow this call?`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function formatSkillAskPrompt(
|
|
78
|
-
skillName: string,
|
|
79
|
-
agentName?: string,
|
|
80
|
-
): string {
|
|
81
|
-
const subject = agentName ? `Agent '${agentName}'` : "Current agent";
|
|
82
|
-
return `${subject} requested skill '${skillName}'. Allow loading this skill?`;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function formatSkillPathAskPrompt(
|
|
86
|
-
skill: SkillPromptEntry,
|
|
87
|
-
readPath: string,
|
|
88
|
-
agentName?: string,
|
|
89
|
-
): string {
|
|
90
|
-
const subject = agentName ? `Agent '${agentName}'` : "Current agent";
|
|
91
|
-
return `${subject} requested access to skill '${skill.name}' via '${readPath}'. Allow this read?`;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// formatSkillPathDenyReason has been moved to denial-messages.ts.
|