@gotgenes/pi-permission-system 3.6.0 → 3.7.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 +18 -0
- package/package.json +1 -1
- package/src/handlers/before-agent-start.ts +112 -0
- package/src/handlers/index.ts +16 -0
- package/src/handlers/input.ts +97 -0
- package/src/handlers/lifecycle.ts +80 -0
- package/src/handlers/tool-call.ts +400 -0
- package/src/handlers/types.ts +95 -0
- package/src/index.ts +101 -701
- package/tests/handlers/before-agent-start.test.ts +274 -0
- package/tests/handlers/input.test.ts +271 -0
- package/tests/handlers/lifecycle.test.ts +331 -0
- package/tests/handlers/tool-call.test.ts +418 -0
package/src/index.ts
CHANGED
|
@@ -11,18 +11,11 @@ import {
|
|
|
11
11
|
type ExtensionCommandContext,
|
|
12
12
|
type ExtensionContext,
|
|
13
13
|
getAgentDir,
|
|
14
|
-
isToolCallEventType,
|
|
15
14
|
} from "@mariozechner/pi-coding-agent";
|
|
16
15
|
import {
|
|
17
16
|
getActiveAgentName,
|
|
18
17
|
getActiveAgentNameFromSystemPrompt,
|
|
19
18
|
} from "./active-agent";
|
|
20
|
-
import {
|
|
21
|
-
createActiveToolsCacheKey,
|
|
22
|
-
createBeforeAgentStartPromptStateKey,
|
|
23
|
-
shouldApplyCachedAgentStartState,
|
|
24
|
-
} from "./before-agent-start-cache";
|
|
25
|
-
import { getNonEmptyString, toRecord } from "./common";
|
|
26
19
|
import { loadAndMergeConfigs, loadUnifiedConfig } from "./config-loader";
|
|
27
20
|
import { registerPermissionSystemCommand } from "./config-modal";
|
|
28
21
|
import {
|
|
@@ -43,63 +36,36 @@ import {
|
|
|
43
36
|
normalizePermissionSystemConfig,
|
|
44
37
|
type PermissionSystemExtensionConfig,
|
|
45
38
|
} from "./extension-config";
|
|
46
|
-
import {
|
|
47
|
-
extractExternalPathsFromBashCommand,
|
|
48
|
-
formatBashExternalDirectoryAskPrompt,
|
|
49
|
-
formatBashExternalDirectoryDenyReason,
|
|
50
|
-
formatExternalDirectoryAskPrompt,
|
|
51
|
-
formatExternalDirectoryDenyReason,
|
|
52
|
-
formatExternalDirectoryHardStopHint,
|
|
53
|
-
formatExternalDirectoryUserDeniedReason,
|
|
54
|
-
getPathBearingToolPath,
|
|
55
|
-
isPathOutsideWorkingDirectory,
|
|
56
|
-
normalizePathForComparison,
|
|
57
|
-
PATH_BEARING_TOOLS,
|
|
58
|
-
} from "./external-directory";
|
|
59
39
|
import { setForwardedPermissionLogger } from "./forwarded-permissions/io";
|
|
60
40
|
import {
|
|
61
41
|
confirmPermission,
|
|
62
42
|
type PermissionForwardingDeps,
|
|
63
43
|
processForwardedPermissionRequests,
|
|
64
44
|
} from "./forwarded-permissions/polling";
|
|
45
|
+
import {
|
|
46
|
+
type HandlerDeps,
|
|
47
|
+
handleBeforeAgentStart,
|
|
48
|
+
handleInput,
|
|
49
|
+
handleResourcesDiscover,
|
|
50
|
+
handleSessionShutdown,
|
|
51
|
+
handleSessionStart,
|
|
52
|
+
handleToolCall,
|
|
53
|
+
} from "./handlers";
|
|
54
|
+
import type { PromptPermissionDetails } from "./handlers/types";
|
|
65
55
|
import { createPermissionSystemLogger } from "./logging";
|
|
66
56
|
import {
|
|
67
57
|
type PermissionPromptDecision,
|
|
68
58
|
requestPermissionDecisionFromUi,
|
|
69
59
|
} from "./permission-dialog";
|
|
70
60
|
import { PERMISSION_FORWARDING_POLL_INTERVAL_MS } from "./permission-forwarding";
|
|
71
|
-
import { applyPermissionGate } from "./permission-gate";
|
|
72
61
|
import { PermissionManager } from "./permission-manager";
|
|
73
|
-
import {
|
|
74
|
-
|
|
75
|
-
formatDenyReason,
|
|
76
|
-
formatMissingToolNameReason,
|
|
77
|
-
formatSkillAskPrompt,
|
|
78
|
-
formatSkillPathAskPrompt,
|
|
79
|
-
formatSkillPathDenyReason,
|
|
80
|
-
formatUnknownToolReason,
|
|
81
|
-
formatUserDeniedReason,
|
|
82
|
-
} from "./permission-prompts";
|
|
83
|
-
import {
|
|
84
|
-
deriveApprovalPrefix,
|
|
85
|
-
SessionApprovalCache,
|
|
86
|
-
} from "./session-approval-cache";
|
|
87
|
-
import {
|
|
88
|
-
findSkillPathMatch,
|
|
89
|
-
resolveSkillPromptEntries,
|
|
90
|
-
type SkillPromptEntry,
|
|
91
|
-
} from "./skill-prompt-sanitizer";
|
|
62
|
+
import { SessionApprovalCache } from "./session-approval-cache";
|
|
63
|
+
import type { SkillPromptEntry } from "./skill-prompt-sanitizer";
|
|
92
64
|
import {
|
|
93
65
|
PERMISSION_SYSTEM_STATUS_KEY,
|
|
94
66
|
syncPermissionSystemStatus,
|
|
95
67
|
} from "./status";
|
|
96
68
|
import { isSubagentExecutionContext } from "./subagent-context";
|
|
97
|
-
import { sanitizeAvailableToolsSection } from "./system-prompt-sanitizer";
|
|
98
|
-
import { getPermissionLogContext } from "./tool-input-preview";
|
|
99
|
-
import {
|
|
100
|
-
checkRequestedToolRegistration,
|
|
101
|
-
getToolNameFromValue,
|
|
102
|
-
} from "./tool-registry";
|
|
103
69
|
import {
|
|
104
70
|
canResolveAskPermissionRequest,
|
|
105
71
|
shouldAutoApprovePermissionState,
|
|
@@ -110,8 +76,6 @@ const SESSIONS_DIR = join(PI_AGENT_DIR, "sessions");
|
|
|
110
76
|
const SUBAGENT_SESSIONS_DIR = join(PI_AGENT_DIR, "subagent-sessions");
|
|
111
77
|
const PERMISSION_FORWARDING_DIR = join(SESSIONS_DIR, "permission-forwarding");
|
|
112
78
|
|
|
113
|
-
type PermissionReviewSource = "tool_call" | "skill_input" | "skill_read";
|
|
114
|
-
|
|
115
79
|
let extensionConfig: PermissionSystemExtensionConfig = {
|
|
116
80
|
...DEFAULT_EXTENSION_CONFIG,
|
|
117
81
|
};
|
|
@@ -140,7 +104,6 @@ function reportLoggingWarning(message: string): void {
|
|
|
140
104
|
if (!loggingWarningReporter || reportedLoggingWarnings.has(message)) {
|
|
141
105
|
return;
|
|
142
106
|
}
|
|
143
|
-
|
|
144
107
|
reportedLoggingWarnings.add(message);
|
|
145
108
|
loggingWarningReporter(message);
|
|
146
109
|
}
|
|
@@ -165,50 +128,6 @@ function writeReviewLog(
|
|
|
165
128
|
}
|
|
166
129
|
}
|
|
167
130
|
|
|
168
|
-
function extractSkillNameFromInput(text: string): string | null {
|
|
169
|
-
const trimmed = text.trim();
|
|
170
|
-
if (!trimmed.startsWith("/skill:")) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const afterPrefix = trimmed.slice("/skill:".length);
|
|
175
|
-
if (!afterPrefix) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const firstWhitespace = afterPrefix.search(/\s/);
|
|
180
|
-
const skillName = (
|
|
181
|
-
firstWhitespace === -1 ? afterPrefix : afterPrefix.slice(0, firstWhitespace)
|
|
182
|
-
).trim();
|
|
183
|
-
return skillName || null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function getEventToolName(event: unknown): string | null {
|
|
187
|
-
return getToolNameFromValue(event);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function getEventInput(event: unknown): unknown {
|
|
191
|
-
const record = toRecord(event);
|
|
192
|
-
|
|
193
|
-
if (record.input !== undefined) {
|
|
194
|
-
return record.input;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (record.arguments !== undefined) {
|
|
198
|
-
return record.arguments;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function canRequestPermissionConfirmation(ctx: ExtensionContext): boolean {
|
|
205
|
-
return canResolveAskPermissionRequest({
|
|
206
|
-
config: extensionConfig,
|
|
207
|
-
hasUI: ctx.hasUI,
|
|
208
|
-
isSubagent: isSubagentExecutionContext(ctx, SUBAGENT_SESSIONS_DIR),
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
131
|
function derivePiProjectPaths(cwd: string | undefined | null): {
|
|
213
132
|
projectGlobalConfigPath: string;
|
|
214
133
|
projectAgentsDir: string;
|
|
@@ -216,7 +135,6 @@ function derivePiProjectPaths(cwd: string | undefined | null): {
|
|
|
216
135
|
if (!cwd) {
|
|
217
136
|
return null;
|
|
218
137
|
}
|
|
219
|
-
|
|
220
138
|
return {
|
|
221
139
|
projectGlobalConfigPath: getProjectConfigPath(cwd),
|
|
222
140
|
projectAgentsDir: join(cwd, ".pi", "agent", "agents"),
|
|
@@ -228,7 +146,6 @@ function createPermissionManagerForCwd(
|
|
|
228
146
|
): PermissionManager {
|
|
229
147
|
const agentDir = getAgentDir();
|
|
230
148
|
const projectPaths = derivePiProjectPaths(cwd);
|
|
231
|
-
|
|
232
149
|
return new PermissionManager({
|
|
233
150
|
globalConfigPath: getGlobalConfigPath(agentDir),
|
|
234
151
|
projectGlobalConfigPath: projectPaths?.projectGlobalConfigPath,
|
|
@@ -249,17 +166,10 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
249
166
|
let runtimeContext: ExtensionContext | null = null;
|
|
250
167
|
let lastConfigWarning: string | null = null;
|
|
251
168
|
|
|
252
|
-
const invalidateAgentStartCache = (): void => {
|
|
253
|
-
activeSkillEntries = [];
|
|
254
|
-
lastActiveToolsCacheKey = null;
|
|
255
|
-
lastPromptStateCacheKey = null;
|
|
256
|
-
};
|
|
257
|
-
|
|
258
169
|
const notifyWarning = (message: string): void => {
|
|
259
170
|
if (!runtimeContext?.hasUI) {
|
|
260
171
|
return;
|
|
261
172
|
}
|
|
262
|
-
|
|
263
173
|
runtimeContext.ui.notify(message, "warning");
|
|
264
174
|
};
|
|
265
175
|
|
|
@@ -267,7 +177,6 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
267
177
|
if (ctx) {
|
|
268
178
|
runtimeContext = ctx;
|
|
269
179
|
}
|
|
270
|
-
|
|
271
180
|
const cwd = runtimeContext?.cwd ?? null;
|
|
272
181
|
const agentDir = getAgentDir();
|
|
273
182
|
const mergeResult = loadAndMergeConfigs(
|
|
@@ -306,7 +215,6 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
306
215
|
const normalized = normalizePermissionSystemConfig(next);
|
|
307
216
|
const globalPath = getGlobalConfigPath(getAgentDir());
|
|
308
217
|
|
|
309
|
-
// Load existing global config and merge runtime knobs into it
|
|
310
218
|
const existing = loadUnifiedConfig(globalPath);
|
|
311
219
|
const merged = {
|
|
312
220
|
...existing.config,
|
|
@@ -366,24 +274,44 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
366
274
|
getConfigPath: () => getGlobalConfigPath(getAgentDir()),
|
|
367
275
|
});
|
|
368
276
|
|
|
369
|
-
const
|
|
370
|
-
|
|
277
|
+
const stopForwardedPermissionPolling = (): void => {
|
|
278
|
+
if (permissionForwardingTimer) {
|
|
279
|
+
clearInterval(permissionForwardingTimer);
|
|
280
|
+
permissionForwardingTimer = null;
|
|
281
|
+
}
|
|
282
|
+
permissionForwardingContext = null;
|
|
283
|
+
isProcessingForwardedRequests = false;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const startForwardedPermissionPolling = (ctx: ExtensionContext): void => {
|
|
287
|
+
if (!ctx.hasUI || isSubagentExecutionContext(ctx, SUBAGENT_SESSIONS_DIR)) {
|
|
288
|
+
stopForwardedPermissionPolling();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
permissionForwardingContext = ctx;
|
|
292
|
+
if (permissionForwardingTimer) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
permissionForwardingTimer = setInterval(() => {
|
|
296
|
+
if (!permissionForwardingContext || isProcessingForwardedRequests) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
isProcessingForwardedRequests = true;
|
|
300
|
+
void processForwardedPermissionRequests(
|
|
301
|
+
permissionForwardingContext,
|
|
302
|
+
forwardingDeps,
|
|
303
|
+
).finally(() => {
|
|
304
|
+
isProcessingForwardedRequests = false;
|
|
305
|
+
});
|
|
306
|
+
}, PERMISSION_FORWARDING_POLL_INTERVAL_MS);
|
|
371
307
|
};
|
|
372
308
|
|
|
309
|
+
const createPermissionRequestId = (prefix: string): string =>
|
|
310
|
+
`${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}-${process.pid}`;
|
|
311
|
+
|
|
373
312
|
const reviewPermissionDecision = (
|
|
374
313
|
event: string,
|
|
375
|
-
details: {
|
|
376
|
-
requestId: string;
|
|
377
|
-
source: PermissionReviewSource;
|
|
378
|
-
agentName: string | null;
|
|
379
|
-
message: string;
|
|
380
|
-
toolCallId?: string;
|
|
381
|
-
toolName?: string;
|
|
382
|
-
skillName?: string;
|
|
383
|
-
path?: string;
|
|
384
|
-
command?: string;
|
|
385
|
-
target?: string;
|
|
386
|
-
toolInputPreview?: string;
|
|
314
|
+
details: PromptPermissionDetails & {
|
|
387
315
|
resolution?: string;
|
|
388
316
|
denialReason?: string;
|
|
389
317
|
},
|
|
@@ -407,27 +335,13 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
407
335
|
|
|
408
336
|
const promptPermission = async (
|
|
409
337
|
ctx: ExtensionContext,
|
|
410
|
-
details:
|
|
411
|
-
requestId: string;
|
|
412
|
-
source: PermissionReviewSource;
|
|
413
|
-
agentName: string | null;
|
|
414
|
-
message: string;
|
|
415
|
-
toolCallId?: string;
|
|
416
|
-
toolName?: string;
|
|
417
|
-
skillName?: string;
|
|
418
|
-
path?: string;
|
|
419
|
-
command?: string;
|
|
420
|
-
target?: string;
|
|
421
|
-
toolInputPreview?: string;
|
|
422
|
-
},
|
|
338
|
+
details: PromptPermissionDetails,
|
|
423
339
|
): Promise<PermissionPromptDecision> => {
|
|
424
340
|
if (shouldAutoApprovePermissionState("ask", extensionConfig)) {
|
|
425
341
|
reviewPermissionDecision("permission_request.auto_approved", details);
|
|
426
342
|
return { approved: true, state: "approved" };
|
|
427
343
|
}
|
|
428
|
-
|
|
429
344
|
reviewPermissionDecision("permission_request.waiting", details);
|
|
430
|
-
|
|
431
345
|
const decision = await confirmPermission(
|
|
432
346
|
ctx,
|
|
433
347
|
details.message,
|
|
@@ -446,42 +360,6 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
446
360
|
return decision;
|
|
447
361
|
};
|
|
448
362
|
|
|
449
|
-
const stopForwardedPermissionPolling = (): void => {
|
|
450
|
-
if (permissionForwardingTimer) {
|
|
451
|
-
clearInterval(permissionForwardingTimer);
|
|
452
|
-
permissionForwardingTimer = null;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
permissionForwardingContext = null;
|
|
456
|
-
isProcessingForwardedRequests = false;
|
|
457
|
-
};
|
|
458
|
-
|
|
459
|
-
const startForwardedPermissionPolling = (ctx: ExtensionContext): void => {
|
|
460
|
-
if (!ctx.hasUI || isSubagentExecutionContext(ctx, SUBAGENT_SESSIONS_DIR)) {
|
|
461
|
-
stopForwardedPermissionPolling();
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
permissionForwardingContext = ctx;
|
|
466
|
-
if (permissionForwardingTimer) {
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
permissionForwardingTimer = setInterval(() => {
|
|
471
|
-
if (!permissionForwardingContext || isProcessingForwardedRequests) {
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
isProcessingForwardedRequests = true;
|
|
476
|
-
void processForwardedPermissionRequests(
|
|
477
|
-
permissionForwardingContext,
|
|
478
|
-
forwardingDeps,
|
|
479
|
-
).finally(() => {
|
|
480
|
-
isProcessingForwardedRequests = false;
|
|
481
|
-
});
|
|
482
|
-
}, PERMISSION_FORWARDING_POLL_INTERVAL_MS);
|
|
483
|
-
};
|
|
484
|
-
|
|
485
363
|
const resolveAgentName = (
|
|
486
364
|
ctx: ExtensionContext,
|
|
487
365
|
systemPrompt?: string,
|
|
@@ -491,35 +369,17 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
491
369
|
lastKnownActiveAgentName = fromSession;
|
|
492
370
|
return fromSession;
|
|
493
371
|
}
|
|
494
|
-
|
|
495
372
|
const fromSystemPrompt = getActiveAgentNameFromSystemPrompt(systemPrompt);
|
|
496
373
|
if (fromSystemPrompt) {
|
|
497
374
|
lastKnownActiveAgentName = fromSystemPrompt;
|
|
498
375
|
return fromSystemPrompt;
|
|
499
376
|
}
|
|
500
|
-
|
|
501
377
|
return lastKnownActiveAgentName;
|
|
502
378
|
};
|
|
503
379
|
|
|
504
|
-
const shouldExposeTool = (
|
|
505
|
-
toolName: string,
|
|
506
|
-
agentName: string | null,
|
|
507
|
-
): boolean => {
|
|
508
|
-
// Use tool-level permission check for tool injection decisions
|
|
509
|
-
// This ensures that agent-specific tool deny rules (e.g., bash: deny) are respected
|
|
510
|
-
// before any command-level permissions are considered
|
|
511
|
-
const toolPermission = permissionManager.getToolPermission(
|
|
512
|
-
toolName,
|
|
513
|
-
agentName ?? undefined,
|
|
514
|
-
);
|
|
515
|
-
return toolPermission !== "deny";
|
|
516
|
-
};
|
|
517
|
-
|
|
518
380
|
const logResolvedConfigPaths = (): void => {
|
|
519
381
|
const policyPaths = permissionManager.getResolvedPolicyPaths();
|
|
520
382
|
const cwd = runtimeContext?.cwd ?? null;
|
|
521
|
-
|
|
522
|
-
// Detect legacy files for the log entry
|
|
523
383
|
const agentDir = getAgentDir();
|
|
524
384
|
const legacyGlobalPolicyDetected = existsSync(
|
|
525
385
|
getLegacyGlobalPolicyPath(agentDir),
|
|
@@ -532,7 +392,6 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
532
392
|
const legacyExtensionConfigDetected =
|
|
533
393
|
normalize(legacyExtConfigPath) !== normalize(newGlobalPath) &&
|
|
534
394
|
existsSync(legacyExtConfigPath);
|
|
535
|
-
|
|
536
395
|
const entry = buildResolvedConfigLogEntry({
|
|
537
396
|
policyPaths,
|
|
538
397
|
legacyGlobalPolicyDetected,
|
|
@@ -549,518 +408,59 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
549
408
|
);
|
|
550
409
|
};
|
|
551
410
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
pi.on("before_agent_start", async (event, ctx) => {
|
|
600
|
-
runtimeContext = ctx;
|
|
601
|
-
refreshExtensionConfig(ctx);
|
|
602
|
-
startForwardedPermissionPolling(ctx);
|
|
603
|
-
const agentName = resolveAgentName(ctx, event.systemPrompt);
|
|
604
|
-
const allTools = pi.getAllTools();
|
|
605
|
-
const allowedTools: string[] = [];
|
|
606
|
-
|
|
607
|
-
for (const tool of allTools) {
|
|
608
|
-
const toolName = getEventToolName(tool);
|
|
609
|
-
if (!toolName) {
|
|
610
|
-
continue;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
if (shouldExposeTool(toolName, agentName)) {
|
|
614
|
-
allowedTools.push(toolName);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
const activeToolsCacheKey = createActiveToolsCacheKey(allowedTools);
|
|
619
|
-
if (
|
|
620
|
-
shouldApplyCachedAgentStartState(
|
|
621
|
-
lastActiveToolsCacheKey,
|
|
622
|
-
activeToolsCacheKey,
|
|
623
|
-
)
|
|
624
|
-
) {
|
|
625
|
-
pi.setActiveTools(allowedTools);
|
|
626
|
-
lastActiveToolsCacheKey = activeToolsCacheKey;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
const promptStateCacheKey = createBeforeAgentStartPromptStateKey({
|
|
630
|
-
agentName,
|
|
631
|
-
cwd: ctx.cwd,
|
|
632
|
-
permissionStamp: permissionManager.getPolicyCacheStamp(
|
|
633
|
-
agentName ?? undefined,
|
|
634
|
-
),
|
|
635
|
-
systemPrompt: event.systemPrompt,
|
|
636
|
-
allowedToolNames: allowedTools,
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
if (
|
|
640
|
-
!shouldApplyCachedAgentStartState(
|
|
641
|
-
lastPromptStateCacheKey,
|
|
642
|
-
promptStateCacheKey,
|
|
643
|
-
)
|
|
644
|
-
) {
|
|
645
|
-
return {};
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
lastPromptStateCacheKey = promptStateCacheKey;
|
|
649
|
-
const toolPromptResult = sanitizeAvailableToolsSection(
|
|
650
|
-
event.systemPrompt,
|
|
651
|
-
allowedTools,
|
|
652
|
-
);
|
|
653
|
-
const skillPromptResult = resolveSkillPromptEntries(
|
|
654
|
-
toolPromptResult.prompt,
|
|
655
|
-
permissionManager,
|
|
656
|
-
agentName,
|
|
657
|
-
ctx.cwd,
|
|
658
|
-
);
|
|
659
|
-
activeSkillEntries = skillPromptResult.entries;
|
|
660
|
-
|
|
661
|
-
if (skillPromptResult.prompt !== event.systemPrompt) {
|
|
662
|
-
return { systemPrompt: skillPromptResult.prompt };
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
return {};
|
|
666
|
-
});
|
|
667
|
-
|
|
668
|
-
pi.on("input", async (event, ctx) => {
|
|
669
|
-
runtimeContext = ctx;
|
|
670
|
-
startForwardedPermissionPolling(ctx);
|
|
671
|
-
const skillName = extractSkillNameFromInput(event.text);
|
|
672
|
-
if (!skillName) {
|
|
673
|
-
return { action: "continue" };
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
const agentName = resolveAgentName(ctx);
|
|
677
|
-
const check = permissionManager.checkPermission(
|
|
678
|
-
"skill",
|
|
679
|
-
{ name: skillName },
|
|
680
|
-
agentName ?? undefined,
|
|
681
|
-
);
|
|
682
|
-
|
|
683
|
-
if (check.state === "deny" && ctx.hasUI) {
|
|
684
|
-
const notifyMessage = agentName
|
|
685
|
-
? `Skill '${skillName}' is not permitted for agent '${agentName}'.`
|
|
686
|
-
: `Skill '${skillName}' is not permitted by the current skill policy.`;
|
|
687
|
-
ctx.ui.notify(notifyMessage, "warning");
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
const skillInputMessage = formatSkillAskPrompt(
|
|
691
|
-
skillName,
|
|
692
|
-
agentName ?? undefined,
|
|
693
|
-
);
|
|
694
|
-
const skillInputGate = await applyPermissionGate({
|
|
695
|
-
state: check.state,
|
|
696
|
-
canConfirm: canRequestPermissionConfirmation(ctx),
|
|
697
|
-
promptForApproval: () =>
|
|
698
|
-
promptPermission(ctx, {
|
|
699
|
-
requestId: createPermissionRequestId("skill-input"),
|
|
700
|
-
source: "skill_input",
|
|
701
|
-
agentName,
|
|
702
|
-
message: skillInputMessage,
|
|
703
|
-
skillName,
|
|
704
|
-
}),
|
|
705
|
-
writeLog: writeReviewLog,
|
|
706
|
-
logContext: {
|
|
707
|
-
source: "skill_input",
|
|
708
|
-
skillName,
|
|
709
|
-
agentName,
|
|
710
|
-
message: skillInputMessage,
|
|
711
|
-
},
|
|
712
|
-
messages: {
|
|
713
|
-
denyReason: skillInputMessage,
|
|
714
|
-
unavailableReason:
|
|
715
|
-
"Skill requires approval, but no interactive UI is available.",
|
|
716
|
-
userDeniedReason: () => "User denied skill.",
|
|
717
|
-
},
|
|
718
|
-
});
|
|
719
|
-
if (skillInputGate.action === "block") {
|
|
720
|
-
return { action: "handled" };
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
return { action: "continue" };
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
pi.on("tool_call", async (event, ctx) => {
|
|
727
|
-
runtimeContext = ctx;
|
|
728
|
-
startForwardedPermissionPolling(ctx);
|
|
729
|
-
const agentName = resolveAgentName(ctx);
|
|
730
|
-
const toolName = getEventToolName(event);
|
|
731
|
-
|
|
732
|
-
if (!toolName) {
|
|
733
|
-
return { block: true, reason: formatMissingToolNameReason() };
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
const registrationCheck = checkRequestedToolRegistration(
|
|
737
|
-
toolName,
|
|
738
|
-
pi.getAllTools(),
|
|
739
|
-
);
|
|
740
|
-
if (registrationCheck.status === "missing-tool-name") {
|
|
741
|
-
return { block: true, reason: formatMissingToolNameReason() };
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
if (registrationCheck.status === "unregistered") {
|
|
745
|
-
return {
|
|
746
|
-
block: true,
|
|
747
|
-
reason: formatUnknownToolReason(
|
|
748
|
-
registrationCheck.requestedToolName,
|
|
749
|
-
registrationCheck.availableToolNames,
|
|
750
|
-
),
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (isToolCallEventType("read", event) && activeSkillEntries.length > 0) {
|
|
755
|
-
const normalizedReadPath = normalizePathForComparison(
|
|
756
|
-
event.input.path,
|
|
757
|
-
ctx.cwd,
|
|
758
|
-
);
|
|
759
|
-
const matchedSkill = findSkillPathMatch(
|
|
760
|
-
normalizedReadPath,
|
|
761
|
-
activeSkillEntries,
|
|
762
|
-
);
|
|
763
|
-
|
|
764
|
-
if (matchedSkill) {
|
|
765
|
-
const skillReadMessage = formatSkillPathAskPrompt(
|
|
766
|
-
matchedSkill,
|
|
767
|
-
event.input.path,
|
|
768
|
-
agentName ?? undefined,
|
|
769
|
-
);
|
|
770
|
-
const skillReadGate = await applyPermissionGate({
|
|
771
|
-
state: matchedSkill.state,
|
|
772
|
-
canConfirm: canRequestPermissionConfirmation(ctx),
|
|
773
|
-
promptForApproval: () =>
|
|
774
|
-
promptPermission(ctx, {
|
|
775
|
-
requestId: event.toolCallId,
|
|
776
|
-
source: "skill_read",
|
|
777
|
-
agentName,
|
|
778
|
-
message: skillReadMessage,
|
|
779
|
-
toolCallId: event.toolCallId,
|
|
780
|
-
toolName: toolName,
|
|
781
|
-
skillName: matchedSkill.name,
|
|
782
|
-
path: event.input.path,
|
|
783
|
-
}),
|
|
784
|
-
writeLog: writeReviewLog,
|
|
785
|
-
logContext: {
|
|
786
|
-
source: "skill_read",
|
|
787
|
-
skillName: matchedSkill.name,
|
|
788
|
-
agentName,
|
|
789
|
-
path: event.input.path,
|
|
790
|
-
message: skillReadMessage,
|
|
791
|
-
},
|
|
792
|
-
messages: {
|
|
793
|
-
denyReason: formatSkillPathDenyReason(
|
|
794
|
-
matchedSkill,
|
|
795
|
-
event.input.path,
|
|
796
|
-
agentName ?? undefined,
|
|
797
|
-
),
|
|
798
|
-
unavailableReason: `Accessing skill '${matchedSkill.name}' requires approval, but no interactive UI is available.`,
|
|
799
|
-
userDeniedReason: (decision) => {
|
|
800
|
-
const denialReason = decision.denialReason
|
|
801
|
-
? ` Reason: ${decision.denialReason}.`
|
|
802
|
-
: "";
|
|
803
|
-
return `User denied access to skill '${matchedSkill.name}'.${denialReason}`;
|
|
804
|
-
},
|
|
805
|
-
},
|
|
806
|
-
});
|
|
807
|
-
if (skillReadGate.action === "block") {
|
|
808
|
-
return { block: true, reason: skillReadGate.reason };
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
const input = getEventInput(event);
|
|
814
|
-
const externalDirectoryPath = ctx.cwd
|
|
815
|
-
? getPathBearingToolPath(toolName, input)
|
|
816
|
-
: null;
|
|
817
|
-
|
|
818
|
-
if (
|
|
819
|
-
ctx.cwd &&
|
|
820
|
-
externalDirectoryPath &&
|
|
821
|
-
isPathOutsideWorkingDirectory(externalDirectoryPath, ctx.cwd)
|
|
822
|
-
) {
|
|
823
|
-
const normalizedExtPath = normalizePathForComparison(
|
|
824
|
-
externalDirectoryPath,
|
|
825
|
-
ctx.cwd,
|
|
826
|
-
);
|
|
827
|
-
const sessionPrefix = sessionApprovalCache.findMatchingPrefix(
|
|
828
|
-
"external_directory",
|
|
829
|
-
normalizedExtPath,
|
|
830
|
-
);
|
|
831
|
-
|
|
832
|
-
if (sessionPrefix) {
|
|
833
|
-
writeReviewLog("permission_request.session_approved", {
|
|
834
|
-
source: "tool_call",
|
|
835
|
-
toolCallId: event.toolCallId,
|
|
836
|
-
toolName,
|
|
837
|
-
agentName,
|
|
838
|
-
path: externalDirectoryPath,
|
|
839
|
-
resolution: "session_approved",
|
|
840
|
-
sessionApprovalPrefix: sessionPrefix,
|
|
841
|
-
});
|
|
842
|
-
// Fall through to normal permission check
|
|
843
|
-
} else {
|
|
844
|
-
const extCheck = permissionManager.checkPermission(
|
|
845
|
-
"external_directory",
|
|
846
|
-
{},
|
|
847
|
-
agentName ?? undefined,
|
|
848
|
-
);
|
|
849
|
-
|
|
850
|
-
let extDirDecision: PermissionPromptDecision | null = null;
|
|
851
|
-
const extDirMessage = formatExternalDirectoryAskPrompt(
|
|
852
|
-
toolName,
|
|
853
|
-
externalDirectoryPath,
|
|
854
|
-
ctx.cwd,
|
|
855
|
-
agentName ?? undefined,
|
|
856
|
-
);
|
|
857
|
-
const extDirGate = await applyPermissionGate({
|
|
858
|
-
state: extCheck.state,
|
|
859
|
-
canConfirm: canRequestPermissionConfirmation(ctx),
|
|
860
|
-
promptForApproval: async () => {
|
|
861
|
-
const decision = await promptPermission(ctx, {
|
|
862
|
-
requestId: event.toolCallId,
|
|
863
|
-
source: "tool_call",
|
|
864
|
-
agentName,
|
|
865
|
-
message: extDirMessage,
|
|
866
|
-
toolCallId: event.toolCallId,
|
|
867
|
-
toolName,
|
|
868
|
-
path: externalDirectoryPath,
|
|
869
|
-
});
|
|
870
|
-
extDirDecision = decision;
|
|
871
|
-
return decision;
|
|
872
|
-
},
|
|
873
|
-
writeLog: writeReviewLog,
|
|
874
|
-
logContext: {
|
|
875
|
-
source: "tool_call",
|
|
876
|
-
toolCallId: event.toolCallId,
|
|
877
|
-
toolName,
|
|
878
|
-
agentName,
|
|
879
|
-
path: externalDirectoryPath,
|
|
880
|
-
message: extDirMessage,
|
|
881
|
-
},
|
|
882
|
-
messages: {
|
|
883
|
-
denyReason: formatExternalDirectoryDenyReason(
|
|
884
|
-
toolName,
|
|
885
|
-
externalDirectoryPath,
|
|
886
|
-
ctx.cwd,
|
|
887
|
-
agentName ?? undefined,
|
|
888
|
-
),
|
|
889
|
-
unavailableReason: `Accessing '${externalDirectoryPath}' outside the working directory requires approval, but no interactive UI is available.`,
|
|
890
|
-
userDeniedReason: (decision) =>
|
|
891
|
-
formatExternalDirectoryUserDeniedReason(
|
|
892
|
-
toolName,
|
|
893
|
-
externalDirectoryPath,
|
|
894
|
-
decision.denialReason,
|
|
895
|
-
),
|
|
896
|
-
},
|
|
897
|
-
});
|
|
898
|
-
if (extDirGate.action === "block") {
|
|
899
|
-
return { block: true, reason: extDirGate.reason };
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
if (extDirDecision?.state === "approved_for_session") {
|
|
903
|
-
const prefix = deriveApprovalPrefix(normalizedExtPath);
|
|
904
|
-
sessionApprovalCache.approve("external_directory", prefix);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
// Fall through to normal permission check
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
// Bash external directory gate: extract paths from bash commands
|
|
911
|
-
if (ctx.cwd && toolName === "bash") {
|
|
912
|
-
const command = getNonEmptyString(toRecord(input).command);
|
|
913
|
-
if (command) {
|
|
914
|
-
const externalPaths = extractExternalPathsFromBashCommand(
|
|
915
|
-
command,
|
|
916
|
-
ctx.cwd,
|
|
917
|
-
);
|
|
918
|
-
if (externalPaths.length > 0) {
|
|
919
|
-
// Filter out paths already covered by session approvals
|
|
920
|
-
const uncoveredPaths = externalPaths.filter(
|
|
921
|
-
(p) => !sessionApprovalCache.has("external_directory", p),
|
|
922
|
-
);
|
|
923
|
-
|
|
924
|
-
if (uncoveredPaths.length === 0) {
|
|
925
|
-
// All external paths are session-approved
|
|
926
|
-
writeReviewLog("permission_request.session_approved", {
|
|
927
|
-
source: "tool_call",
|
|
928
|
-
toolCallId: event.toolCallId,
|
|
929
|
-
toolName,
|
|
930
|
-
agentName,
|
|
931
|
-
command,
|
|
932
|
-
externalPaths,
|
|
933
|
-
resolution: "session_approved",
|
|
934
|
-
});
|
|
935
|
-
// Fall through to normal bash permission check
|
|
936
|
-
} else {
|
|
937
|
-
const extCheck = permissionManager.checkPermission(
|
|
938
|
-
"external_directory",
|
|
939
|
-
{},
|
|
940
|
-
agentName ?? undefined,
|
|
941
|
-
);
|
|
942
|
-
|
|
943
|
-
let bashExtDecision: PermissionPromptDecision | null = null;
|
|
944
|
-
const bashExtMessage = formatBashExternalDirectoryAskPrompt(
|
|
945
|
-
command,
|
|
946
|
-
uncoveredPaths,
|
|
947
|
-
ctx.cwd,
|
|
948
|
-
agentName ?? undefined,
|
|
949
|
-
);
|
|
950
|
-
const bashExtGate = await applyPermissionGate({
|
|
951
|
-
state: extCheck.state,
|
|
952
|
-
canConfirm: canRequestPermissionConfirmation(ctx),
|
|
953
|
-
promptForApproval: async () => {
|
|
954
|
-
const decision = await promptPermission(ctx, {
|
|
955
|
-
requestId: event.toolCallId,
|
|
956
|
-
source: "tool_call",
|
|
957
|
-
agentName,
|
|
958
|
-
message: bashExtMessage,
|
|
959
|
-
toolCallId: event.toolCallId,
|
|
960
|
-
toolName,
|
|
961
|
-
command,
|
|
962
|
-
});
|
|
963
|
-
bashExtDecision = decision;
|
|
964
|
-
return decision;
|
|
965
|
-
},
|
|
966
|
-
writeLog: writeReviewLog,
|
|
967
|
-
logContext: {
|
|
968
|
-
source: "tool_call",
|
|
969
|
-
toolCallId: event.toolCallId,
|
|
970
|
-
toolName,
|
|
971
|
-
agentName,
|
|
972
|
-
command,
|
|
973
|
-
externalPaths: uncoveredPaths,
|
|
974
|
-
message: bashExtMessage,
|
|
975
|
-
},
|
|
976
|
-
messages: {
|
|
977
|
-
denyReason: formatBashExternalDirectoryDenyReason(
|
|
978
|
-
command,
|
|
979
|
-
uncoveredPaths,
|
|
980
|
-
ctx.cwd,
|
|
981
|
-
agentName ?? undefined,
|
|
982
|
-
),
|
|
983
|
-
unavailableReason: `Bash command '${command}' references path(s) outside the working directory and requires approval, but no interactive UI is available.`,
|
|
984
|
-
userDeniedReason: (decision) => {
|
|
985
|
-
const reasonSuffix = decision.denialReason
|
|
986
|
-
? ` Reason: ${decision.denialReason}.`
|
|
987
|
-
: "";
|
|
988
|
-
return `User denied external directory access for bash command '${command}'.${reasonSuffix} ${formatExternalDirectoryHardStopHint()}`;
|
|
989
|
-
},
|
|
990
|
-
},
|
|
991
|
-
});
|
|
992
|
-
if (bashExtGate.action === "block") {
|
|
993
|
-
return { block: true, reason: bashExtGate.reason };
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
if (bashExtDecision?.state === "approved_for_session") {
|
|
997
|
-
for (const extPath of uncoveredPaths) {
|
|
998
|
-
const prefix = deriveApprovalPrefix(extPath);
|
|
999
|
-
sessionApprovalCache.approve("external_directory", prefix);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
// Fall through to normal bash permission check
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
const check = permissionManager.checkPermission(
|
|
1009
|
-
toolName,
|
|
1010
|
-
input,
|
|
1011
|
-
agentName ?? undefined,
|
|
1012
|
-
);
|
|
1013
|
-
const permissionLogContext = getPermissionLogContext(
|
|
1014
|
-
check,
|
|
1015
|
-
input,
|
|
1016
|
-
PATH_BEARING_TOOLS,
|
|
1017
|
-
);
|
|
1018
|
-
|
|
1019
|
-
const toolUnavailableReason =
|
|
1020
|
-
toolName === "bash" && isToolCallEventType("bash", event)
|
|
1021
|
-
? `Running bash command '${event.input.command}' requires approval, but no interactive UI is available.`
|
|
1022
|
-
: toolName === "mcp"
|
|
1023
|
-
? "Using tool 'mcp' requires approval, but no interactive UI is available."
|
|
1024
|
-
: `Using tool '${toolName}' requires approval, but no interactive UI is available.`;
|
|
1025
|
-
|
|
1026
|
-
const toolAskMessage = formatAskPrompt(
|
|
1027
|
-
check,
|
|
1028
|
-
agentName ?? undefined,
|
|
1029
|
-
input,
|
|
1030
|
-
);
|
|
1031
|
-
const toolGate = await applyPermissionGate({
|
|
1032
|
-
state: check.state,
|
|
1033
|
-
canConfirm: canRequestPermissionConfirmation(ctx),
|
|
1034
|
-
promptForApproval: () =>
|
|
1035
|
-
promptPermission(ctx, {
|
|
1036
|
-
requestId: event.toolCallId,
|
|
1037
|
-
source: "tool_call",
|
|
1038
|
-
agentName,
|
|
1039
|
-
message: toolAskMessage,
|
|
1040
|
-
toolCallId: event.toolCallId,
|
|
1041
|
-
toolName,
|
|
1042
|
-
...permissionLogContext,
|
|
1043
|
-
}),
|
|
1044
|
-
writeLog: writeReviewLog,
|
|
1045
|
-
logContext: {
|
|
1046
|
-
source: "tool_call",
|
|
1047
|
-
toolCallId: event.toolCallId,
|
|
1048
|
-
toolName,
|
|
1049
|
-
agentName,
|
|
1050
|
-
message: toolAskMessage,
|
|
1051
|
-
...permissionLogContext,
|
|
1052
|
-
},
|
|
1053
|
-
messages: {
|
|
1054
|
-
denyReason: formatDenyReason(check, agentName ?? undefined),
|
|
1055
|
-
unavailableReason: toolUnavailableReason,
|
|
1056
|
-
userDeniedReason: (decision) =>
|
|
1057
|
-
formatUserDeniedReason(check, decision.denialReason),
|
|
1058
|
-
},
|
|
1059
|
-
});
|
|
1060
|
-
if (toolGate.action === "block") {
|
|
1061
|
-
return { block: true, reason: toolGate.reason };
|
|
1062
|
-
}
|
|
411
|
+
const deps: HandlerDeps = {
|
|
412
|
+
getPermissionManager: () => permissionManager,
|
|
413
|
+
setPermissionManager: (pm) => {
|
|
414
|
+
permissionManager = pm;
|
|
415
|
+
},
|
|
416
|
+
getRuntimeContext: () => runtimeContext,
|
|
417
|
+
setRuntimeContext: (ctx) => {
|
|
418
|
+
runtimeContext = ctx;
|
|
419
|
+
},
|
|
420
|
+
getActiveSkillEntries: () => activeSkillEntries,
|
|
421
|
+
setActiveSkillEntries: (entries) => {
|
|
422
|
+
activeSkillEntries = entries;
|
|
423
|
+
},
|
|
424
|
+
getLastKnownActiveAgentName: () => lastKnownActiveAgentName,
|
|
425
|
+
setLastKnownActiveAgentName: (name) => {
|
|
426
|
+
lastKnownActiveAgentName = name;
|
|
427
|
+
},
|
|
428
|
+
getLastActiveToolsCacheKey: () => lastActiveToolsCacheKey,
|
|
429
|
+
setLastActiveToolsCacheKey: (key) => {
|
|
430
|
+
lastActiveToolsCacheKey = key;
|
|
431
|
+
},
|
|
432
|
+
getLastPromptStateCacheKey: () => lastPromptStateCacheKey,
|
|
433
|
+
setLastPromptStateCacheKey: (key) => {
|
|
434
|
+
lastPromptStateCacheKey = key;
|
|
435
|
+
},
|
|
436
|
+
sessionApprovalCache,
|
|
437
|
+
createPermissionManagerForCwd,
|
|
438
|
+
refreshExtensionConfig,
|
|
439
|
+
notifyWarning,
|
|
440
|
+
logResolvedConfigPaths,
|
|
441
|
+
resolveAgentName,
|
|
442
|
+
canRequestPermissionConfirmation: (ctx) =>
|
|
443
|
+
canResolveAskPermissionRequest({
|
|
444
|
+
config: extensionConfig,
|
|
445
|
+
hasUI: ctx.hasUI,
|
|
446
|
+
isSubagent: isSubagentExecutionContext(ctx, SUBAGENT_SESSIONS_DIR),
|
|
447
|
+
}),
|
|
448
|
+
promptPermission,
|
|
449
|
+
createPermissionRequestId,
|
|
450
|
+
startForwardedPermissionPolling,
|
|
451
|
+
stopForwardedPermissionPolling,
|
|
452
|
+
writeReviewLog,
|
|
453
|
+
writeDebugLog,
|
|
454
|
+
getAllTools: () => pi.getAllTools(),
|
|
455
|
+
setActiveTools: (names) => pi.setActiveTools(names),
|
|
456
|
+
};
|
|
1063
457
|
|
|
1064
|
-
|
|
1065
|
-
|
|
458
|
+
pi.on("session_start", (event, ctx) => handleSessionStart(deps, event, ctx));
|
|
459
|
+
pi.on("resources_discover", (event) => handleResourcesDiscover(deps, event));
|
|
460
|
+
pi.on("session_shutdown", () => handleSessionShutdown(deps));
|
|
461
|
+
pi.on("before_agent_start", (event, ctx) =>
|
|
462
|
+
handleBeforeAgentStart(deps, event, ctx),
|
|
463
|
+
);
|
|
464
|
+
pi.on("input", (event, ctx) => handleInput(deps, event, ctx));
|
|
465
|
+
pi.on("tool_call", (event, ctx) => handleToolCall(deps, event, ctx));
|
|
1066
466
|
}
|