@gotgenes/pi-permission-system 10.0.0 → 10.2.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 +33 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/agent-prep-session.ts +28 -0
- package/src/decision-reporter.ts +41 -0
- package/src/denial-messages.ts +11 -0
- package/src/forwarded-permissions/permission-forwarder.ts +549 -0
- package/src/forwarding-manager.ts +3 -7
- package/src/gate-handler-session.ts +13 -0
- package/src/gate-prompter.ts +14 -0
- package/src/handlers/before-agent-start.ts +2 -3
- package/src/handlers/gates/bash-command.ts +4 -18
- package/src/handlers/gates/bash-external-directory.ts +3 -15
- package/src/handlers/gates/bash-path.ts +3 -16
- package/src/handlers/gates/descriptor.ts +0 -28
- package/src/handlers/gates/path.ts +3 -15
- package/src/handlers/gates/runner.ts +142 -105
- package/src/handlers/gates/skill-input-gate-pipeline.ts +104 -0
- package/src/handlers/gates/skill-input.ts +44 -0
- package/src/handlers/gates/tool-call-gate-pipeline.ts +120 -0
- package/src/handlers/lifecycle.ts +9 -9
- package/src/handlers/permission-gate-handler.ts +34 -238
- package/src/index.ts +53 -69
- package/src/mcp-targets.ts +56 -46
- package/src/permission-manager.ts +69 -3
- package/src/permission-prompter.ts +7 -58
- package/src/permission-resolver.ts +17 -0
- package/src/permission-session.ts +83 -27
- package/src/permissions-service.ts +53 -0
- package/src/runtime.ts +1 -37
- package/src/service-lifecycle.ts +49 -0
- package/src/session-approval-recorder.ts +6 -0
- package/src/session-lifecycle-session.ts +24 -0
- package/src/tool-input-preview.ts +0 -62
- package/src/tool-input-prompt-formatters.ts +63 -0
- package/src/tool-preview-formatter.ts +6 -4
- package/test/decision-reporter.test.ts +112 -0
- package/test/denial-messages.test.ts +62 -0
- package/test/forwarding-manager.test.ts +26 -44
- package/test/handlers/before-agent-start.test.ts +45 -21
- package/test/handlers/external-directory-integration.test.ts +83 -114
- package/test/handlers/external-directory-session-dedup.test.ts +102 -55
- package/test/handlers/gates/bash-command.test.ts +49 -90
- package/test/handlers/gates/bash-external-directory.test.ts +54 -95
- package/test/handlers/gates/bash-path.test.ts +54 -157
- package/test/handlers/gates/path.test.ts +38 -105
- package/test/handlers/gates/runner.test.ts +151 -186
- package/test/handlers/gates/skill-input-gate-pipeline.test.ts +176 -0
- package/test/handlers/gates/skill-input.test.ts +128 -0
- package/test/handlers/gates/tool-call-gate-pipeline.test.ts +180 -0
- package/test/handlers/input.test.ts +1 -2
- package/test/handlers/lifecycle.test.ts +49 -33
- package/test/handlers/tool-call-events.test.ts +1 -1
- package/test/handlers/tool-call.test.ts +44 -153
- package/test/helpers/gate-fixtures.ts +212 -17
- package/test/helpers/handler-fixtures.ts +226 -29
- package/test/mcp-targets.test.ts +55 -0
- package/test/permission-forwarder.test.ts +295 -0
- package/test/permission-forwarding.test.ts +0 -282
- package/test/permission-manager-unified.test.ts +159 -1
- package/test/permission-prompter.test.ts +33 -44
- package/test/permission-session.test.ts +211 -105
- package/test/permissions-service.test.ts +151 -0
- package/test/runtime.test.ts +2 -86
- package/test/service-lifecycle.test.ts +162 -0
- package/test/tool-input-preview.test.ts +0 -111
- package/test/tool-input-prompt-formatters.test.ts +115 -0
- package/src/forwarded-permissions/polling.ts +0 -411
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
getActiveAgentName,
|
|
7
|
-
getActiveAgentNameFromSystemPrompt,
|
|
8
|
-
} from "#src/active-agent";
|
|
9
|
-
import { toRecord } from "#src/common";
|
|
10
|
-
import type {
|
|
11
|
-
PermissionPromptDecision,
|
|
12
|
-
RequestPermissionOptions,
|
|
13
|
-
} from "#src/permission-dialog";
|
|
14
|
-
import {
|
|
15
|
-
emitUiPromptEvent,
|
|
16
|
-
type PermissionEventBus,
|
|
17
|
-
} from "#src/permission-events";
|
|
18
|
-
import {
|
|
19
|
-
type ForwardedPermissionRequest,
|
|
20
|
-
type ForwardedPermissionResponse,
|
|
21
|
-
type ForwardedPromptDisplay,
|
|
22
|
-
isForwardedPermissionRequestForSession,
|
|
23
|
-
PERMISSION_FORWARDING_POLL_INTERVAL_MS,
|
|
24
|
-
PERMISSION_FORWARDING_TIMEOUT_MS,
|
|
25
|
-
resolvePermissionForwardingTargetSessionId,
|
|
26
|
-
SUBAGENT_PARENT_SESSION_ENV_CANDIDATES,
|
|
27
|
-
} from "#src/permission-forwarding";
|
|
28
|
-
import { buildForwardedUiPrompt } from "#src/permission-ui-prompt";
|
|
29
|
-
import { isSubagentExecutionContext } from "#src/subagent-context";
|
|
30
|
-
import type { SubagentSessionRegistry } from "#src/subagent-registry";
|
|
31
|
-
|
|
32
|
-
import {
|
|
33
|
-
cleanupPermissionForwardingLocationIfEmpty,
|
|
34
|
-
ensurePermissionForwardingLocation,
|
|
35
|
-
type ForwardedPermissionLogger,
|
|
36
|
-
getExistingPermissionForwardingLocation,
|
|
37
|
-
listRequestFiles,
|
|
38
|
-
logPermissionForwardingError,
|
|
39
|
-
logPermissionForwardingWarning,
|
|
40
|
-
readForwardedPermissionRequest,
|
|
41
|
-
readForwardedPermissionResponse,
|
|
42
|
-
safeDeleteFile,
|
|
43
|
-
sleep,
|
|
44
|
-
writeJsonFileAtomic,
|
|
45
|
-
} from "./io";
|
|
46
|
-
|
|
47
|
-
export interface PermissionForwardingDeps {
|
|
48
|
-
forwardingDir: string;
|
|
49
|
-
subagentSessionsDir: string;
|
|
50
|
-
/** In-process subagent session registry for detection and forwarding target resolution. */
|
|
51
|
-
registry?: SubagentSessionRegistry;
|
|
52
|
-
/** Event bus used for UI prompt broadcasts. */
|
|
53
|
-
events?: PermissionEventBus;
|
|
54
|
-
logger: ForwardedPermissionLogger;
|
|
55
|
-
writeReviewLog: (event: string, details: Record<string, unknown>) => void;
|
|
56
|
-
requestPermissionDecisionFromUi: (
|
|
57
|
-
ui: ExtensionContext["ui"],
|
|
58
|
-
title: string,
|
|
59
|
-
message: string,
|
|
60
|
-
options?: RequestPermissionOptions,
|
|
61
|
-
) => Promise<PermissionPromptDecision>;
|
|
62
|
-
shouldAutoApprove: () => boolean;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function getSessionId(ctx: ExtensionContext): string {
|
|
66
|
-
try {
|
|
67
|
-
const sessionId = ctx.sessionManager.getSessionId();
|
|
68
|
-
if (typeof sessionId === "string" && sessionId.trim()) {
|
|
69
|
-
return sessionId.trim();
|
|
70
|
-
}
|
|
71
|
-
} catch {}
|
|
72
|
-
|
|
73
|
-
return "unknown";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function getContextSystemPrompt(ctx: ExtensionContext): string | undefined {
|
|
77
|
-
const getSystemPrompt = toRecord(ctx).getSystemPrompt;
|
|
78
|
-
if (typeof getSystemPrompt !== "function") {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- getSystemPrompt is a Pi SDK accessor returning any
|
|
84
|
-
const systemPrompt = getSystemPrompt.call(ctx);
|
|
85
|
-
return typeof systemPrompt === "string" ? systemPrompt : undefined;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
// No deps available in this helper — warning silently dropped.
|
|
88
|
-
logPermissionForwardingWarning(
|
|
89
|
-
null,
|
|
90
|
-
"Failed to read context system prompt for forwarded permission metadata",
|
|
91
|
-
error,
|
|
92
|
-
);
|
|
93
|
-
return undefined;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function formatForwardedPermissionPrompt(
|
|
98
|
-
request: ForwardedPermissionRequest,
|
|
99
|
-
): string {
|
|
100
|
-
const agentName = request.requesterAgentName || "unknown";
|
|
101
|
-
const sessionId = request.requesterSessionId || "unknown";
|
|
102
|
-
return [
|
|
103
|
-
`Subagent '${agentName}' requested permission.`,
|
|
104
|
-
`Session ID: ${sessionId}`,
|
|
105
|
-
"",
|
|
106
|
-
request.message,
|
|
107
|
-
].join("\n");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export async function waitForForwardedPermissionApproval(
|
|
111
|
-
ctx: ExtensionContext,
|
|
112
|
-
message: string,
|
|
113
|
-
deps: PermissionForwardingDeps,
|
|
114
|
-
forwarded?: ForwardedPromptDisplay,
|
|
115
|
-
): Promise<PermissionPromptDecision> {
|
|
116
|
-
const requesterSessionId = getSessionId(ctx);
|
|
117
|
-
const targetSessionId = resolvePermissionForwardingTargetSessionId({
|
|
118
|
-
hasUI: ctx.hasUI,
|
|
119
|
-
isSubagent: isSubagentExecutionContext(
|
|
120
|
-
ctx,
|
|
121
|
-
deps.subagentSessionsDir,
|
|
122
|
-
deps.registry,
|
|
123
|
-
),
|
|
124
|
-
currentSessionId: requesterSessionId,
|
|
125
|
-
env: process.env,
|
|
126
|
-
sessionId: requesterSessionId,
|
|
127
|
-
registry: deps.registry,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
if (!targetSessionId) {
|
|
131
|
-
logPermissionForwardingError(
|
|
132
|
-
deps.logger,
|
|
133
|
-
`Permission forwarding target session could not be resolved. ` +
|
|
134
|
-
`Checked env vars: ${SUBAGENT_PARENT_SESSION_ENV_CANDIDATES.join(", ")}. ` +
|
|
135
|
-
`If you are using a subagent extension (nicobailon/pi-subagents, HazAT/pi-interactive-subagents, etc.), ` +
|
|
136
|
-
`ask its maintainer to set PI_SUBAGENT_PARENT_SESSION in the child process environment ` +
|
|
137
|
-
`(see https://github.com/gotgenes/pi-permission-system/issues/143).`,
|
|
138
|
-
);
|
|
139
|
-
return { approved: false, state: "denied" };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const location = ensurePermissionForwardingLocation(
|
|
143
|
-
deps.logger,
|
|
144
|
-
deps.forwardingDir,
|
|
145
|
-
targetSessionId,
|
|
146
|
-
);
|
|
147
|
-
if (!location) {
|
|
148
|
-
logPermissionForwardingError(
|
|
149
|
-
deps.logger,
|
|
150
|
-
`Permission forwarding is unavailable because session-scoped directories could not be prepared for '${targetSessionId}'`,
|
|
151
|
-
);
|
|
152
|
-
return { approved: false, state: "denied" };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}-${process.pid}`;
|
|
156
|
-
const requesterAgentName =
|
|
157
|
-
getActiveAgentName(ctx) ??
|
|
158
|
-
getActiveAgentNameFromSystemPrompt(getContextSystemPrompt(ctx)) ??
|
|
159
|
-
"unknown";
|
|
160
|
-
const request: ForwardedPermissionRequest = {
|
|
161
|
-
id: requestId,
|
|
162
|
-
createdAt: Date.now(),
|
|
163
|
-
requesterSessionId,
|
|
164
|
-
targetSessionId,
|
|
165
|
-
requesterAgentName,
|
|
166
|
-
message,
|
|
167
|
-
...(forwarded
|
|
168
|
-
? {
|
|
169
|
-
source: forwarded.source,
|
|
170
|
-
surface: forwarded.surface,
|
|
171
|
-
value: forwarded.value,
|
|
172
|
-
}
|
|
173
|
-
: {}),
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const requestPath = join(location.requestsDir, `${requestId}.json`);
|
|
177
|
-
const responsePath = join(location.responsesDir, `${requestId}.json`);
|
|
178
|
-
|
|
179
|
-
deps.writeReviewLog("forwarded_permission.request_created", {
|
|
180
|
-
requestId,
|
|
181
|
-
requesterAgentName,
|
|
182
|
-
requesterSessionId: request.requesterSessionId,
|
|
183
|
-
targetSessionId,
|
|
184
|
-
requestPath,
|
|
185
|
-
responsePath,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
writeJsonFileAtomic(deps.logger, requestPath, request);
|
|
190
|
-
} catch (error) {
|
|
191
|
-
logPermissionForwardingError(
|
|
192
|
-
deps.logger,
|
|
193
|
-
`Failed to write forwarded permission request '${requestPath}'`,
|
|
194
|
-
error,
|
|
195
|
-
);
|
|
196
|
-
return { approved: false, state: "denied" };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const deadline = Date.now() + PERMISSION_FORWARDING_TIMEOUT_MS;
|
|
200
|
-
while (Date.now() < deadline) {
|
|
201
|
-
if (existsSync(responsePath)) {
|
|
202
|
-
const response = readForwardedPermissionResponse(
|
|
203
|
-
deps.logger,
|
|
204
|
-
responsePath,
|
|
205
|
-
);
|
|
206
|
-
deps.writeReviewLog("forwarded_permission.response_received", {
|
|
207
|
-
requestId,
|
|
208
|
-
approved: response?.approved ?? null,
|
|
209
|
-
state: response?.state ?? null,
|
|
210
|
-
denialReason: response?.denialReason ?? null,
|
|
211
|
-
responderSessionId: response?.responderSessionId ?? null,
|
|
212
|
-
targetSessionId,
|
|
213
|
-
responsePath,
|
|
214
|
-
});
|
|
215
|
-
safeDeleteFile(
|
|
216
|
-
deps.logger,
|
|
217
|
-
responsePath,
|
|
218
|
-
"forwarded permission response",
|
|
219
|
-
);
|
|
220
|
-
safeDeleteFile(deps.logger, requestPath, "forwarded permission request");
|
|
221
|
-
cleanupPermissionForwardingLocationIfEmpty(deps.logger, location);
|
|
222
|
-
return response ?? { approved: false, state: "denied" };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
await sleep(PERMISSION_FORWARDING_POLL_INTERVAL_MS);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
logPermissionForwardingWarning(
|
|
229
|
-
deps.logger,
|
|
230
|
-
`Timed out waiting for forwarded permission response '${responsePath}'`,
|
|
231
|
-
);
|
|
232
|
-
deps.writeReviewLog("forwarded_permission.response_timed_out", {
|
|
233
|
-
requestId,
|
|
234
|
-
requesterAgentName,
|
|
235
|
-
targetSessionId,
|
|
236
|
-
responsePath,
|
|
237
|
-
});
|
|
238
|
-
safeDeleteFile(deps.logger, requestPath, "forwarded permission request");
|
|
239
|
-
cleanupPermissionForwardingLocationIfEmpty(deps.logger, location);
|
|
240
|
-
return { approved: false, state: "denied" };
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export async function processForwardedPermissionRequests(
|
|
244
|
-
ctx: ExtensionContext,
|
|
245
|
-
deps: PermissionForwardingDeps,
|
|
246
|
-
): Promise<void> {
|
|
247
|
-
if (!ctx.hasUI) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const currentSessionId = getSessionId(ctx);
|
|
252
|
-
const location = getExistingPermissionForwardingLocation(
|
|
253
|
-
deps.forwardingDir,
|
|
254
|
-
currentSessionId,
|
|
255
|
-
);
|
|
256
|
-
if (!location) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const requestFiles = listRequestFiles(deps.logger, location.requestsDir);
|
|
261
|
-
if (requestFiles.length === 0) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
for (const fileName of requestFiles) {
|
|
266
|
-
const requestPath = join(location.requestsDir, fileName);
|
|
267
|
-
const request = readForwardedPermissionRequest(deps.logger, requestPath);
|
|
268
|
-
if (!request) {
|
|
269
|
-
safeDeleteFile(
|
|
270
|
-
deps.logger,
|
|
271
|
-
requestPath,
|
|
272
|
-
`${location.label} forwarded permission request`,
|
|
273
|
-
);
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (!isForwardedPermissionRequestForSession(request, currentSessionId)) {
|
|
278
|
-
logPermissionForwardingWarning(
|
|
279
|
-
deps.logger,
|
|
280
|
-
`Ignoring forwarded permission request '${request.id}' because it targets session '${request.targetSessionId}' instead of '${currentSessionId}'`,
|
|
281
|
-
);
|
|
282
|
-
safeDeleteFile(
|
|
283
|
-
deps.logger,
|
|
284
|
-
requestPath,
|
|
285
|
-
`${location.label} forwarded permission request`,
|
|
286
|
-
);
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const forwardedPermissionLogDetails = {
|
|
291
|
-
requestId: request.id,
|
|
292
|
-
source: location.label,
|
|
293
|
-
requesterAgentName: request.requesterAgentName,
|
|
294
|
-
requesterSessionId: request.requesterSessionId,
|
|
295
|
-
targetSessionId: request.targetSessionId,
|
|
296
|
-
requestPath,
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
let decision: PermissionPromptDecision = {
|
|
300
|
-
approved: false,
|
|
301
|
-
state: "denied",
|
|
302
|
-
};
|
|
303
|
-
if (deps.shouldAutoApprove()) {
|
|
304
|
-
deps.writeReviewLog(
|
|
305
|
-
"forwarded_permission.auto_approved",
|
|
306
|
-
forwardedPermissionLogDetails,
|
|
307
|
-
);
|
|
308
|
-
decision = { approved: true, state: "approved" };
|
|
309
|
-
} else {
|
|
310
|
-
deps.writeReviewLog(
|
|
311
|
-
"forwarded_permission.prompted",
|
|
312
|
-
forwardedPermissionLogDetails,
|
|
313
|
-
);
|
|
314
|
-
try {
|
|
315
|
-
const forwardedMessage = formatForwardedPermissionPrompt(request);
|
|
316
|
-
if (deps.events) {
|
|
317
|
-
emitUiPromptEvent(
|
|
318
|
-
deps.events,
|
|
319
|
-
buildForwardedUiPrompt({
|
|
320
|
-
requestId: request.id,
|
|
321
|
-
message: forwardedMessage,
|
|
322
|
-
requesterAgentName: request.requesterAgentName || null,
|
|
323
|
-
requesterSessionId: request.requesterSessionId || null,
|
|
324
|
-
source: request.source ?? null,
|
|
325
|
-
surface: request.surface ?? null,
|
|
326
|
-
value: request.value ?? null,
|
|
327
|
-
}),
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
decision = await deps.requestPermissionDecisionFromUi(
|
|
331
|
-
ctx.ui,
|
|
332
|
-
"Permission Required (Subagent)",
|
|
333
|
-
forwardedMessage,
|
|
334
|
-
);
|
|
335
|
-
} catch (error) {
|
|
336
|
-
logPermissionForwardingError(
|
|
337
|
-
deps.logger,
|
|
338
|
-
"Failed to show forwarded permission confirmation dialog",
|
|
339
|
-
error,
|
|
340
|
-
);
|
|
341
|
-
decision = { approved: false, state: "denied" };
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const responsePath = join(location.responsesDir, `${request.id}.json`);
|
|
346
|
-
deps.writeReviewLog(
|
|
347
|
-
decision.approved
|
|
348
|
-
? "forwarded_permission.approved"
|
|
349
|
-
: "forwarded_permission.denied",
|
|
350
|
-
{
|
|
351
|
-
requestId: request.id,
|
|
352
|
-
source: location.label,
|
|
353
|
-
requesterAgentName: request.requesterAgentName,
|
|
354
|
-
requesterSessionId: request.requesterSessionId,
|
|
355
|
-
targetSessionId: request.targetSessionId,
|
|
356
|
-
responsePath,
|
|
357
|
-
resolution: decision.state,
|
|
358
|
-
denialReason: decision.denialReason ?? null,
|
|
359
|
-
},
|
|
360
|
-
);
|
|
361
|
-
try {
|
|
362
|
-
writeJsonFileAtomic(deps.logger, responsePath, {
|
|
363
|
-
approved: decision.approved,
|
|
364
|
-
state: decision.state,
|
|
365
|
-
denialReason: decision.denialReason,
|
|
366
|
-
responderSessionId: currentSessionId,
|
|
367
|
-
respondedAt: Date.now(),
|
|
368
|
-
} satisfies ForwardedPermissionResponse);
|
|
369
|
-
} catch (error) {
|
|
370
|
-
logPermissionForwardingError(
|
|
371
|
-
deps.logger,
|
|
372
|
-
`Failed to write ${location.label} forwarded permission response '${responsePath}'`,
|
|
373
|
-
error,
|
|
374
|
-
);
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
safeDeleteFile(
|
|
379
|
-
deps.logger,
|
|
380
|
-
requestPath,
|
|
381
|
-
`${location.label} forwarded permission request`,
|
|
382
|
-
);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
cleanupPermissionForwardingLocationIfEmpty(deps.logger, location);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
export async function confirmPermission(
|
|
389
|
-
ctx: ExtensionContext,
|
|
390
|
-
message: string,
|
|
391
|
-
deps: PermissionForwardingDeps,
|
|
392
|
-
options?: RequestPermissionOptions,
|
|
393
|
-
forwarded?: ForwardedPromptDisplay,
|
|
394
|
-
): Promise<PermissionPromptDecision> {
|
|
395
|
-
if (ctx.hasUI) {
|
|
396
|
-
return deps.requestPermissionDecisionFromUi(
|
|
397
|
-
ctx.ui,
|
|
398
|
-
"Permission Required",
|
|
399
|
-
message,
|
|
400
|
-
options,
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (
|
|
405
|
-
!isSubagentExecutionContext(ctx, deps.subagentSessionsDir, deps.registry)
|
|
406
|
-
) {
|
|
407
|
-
return { approved: false, state: "denied" };
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return waitForForwardedPermissionApproval(ctx, message, deps, forwarded);
|
|
411
|
-
}
|