@gotgenes/pi-permission-system 5.5.0 → 5.6.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 +23 -0
- package/package.json +1 -1
- package/src/handlers/before-agent-start.ts +7 -7
- package/src/handlers/gates/bash-external-directory.ts +70 -67
- package/src/handlers/gates/descriptor.ts +115 -0
- package/src/handlers/gates/external-directory.ts +62 -130
- package/src/handlers/gates/index.ts +12 -4
- package/src/handlers/gates/runner.ts +144 -0
- package/src/handlers/gates/skill-read.ts +40 -59
- package/src/handlers/gates/tool.ts +35 -104
- package/src/handlers/gates/types.ts +0 -2
- package/src/handlers/input.ts +3 -3
- package/src/handlers/lifecycle.ts +21 -21
- package/src/handlers/tool-call.ts +121 -20
- package/src/handlers/types.ts +20 -7
- package/src/index.ts +6 -1
- package/src/runtime.ts +17 -9
- package/tests/handlers/before-agent-start.test.ts +17 -27
- package/tests/handlers/gates/bash-external-directory.test.ts +129 -184
- package/tests/handlers/gates/external-directory.test.ts +118 -264
- package/tests/handlers/gates/runner.test.ts +361 -0
- package/tests/handlers/gates/skill-read.test.ts +86 -137
- package/tests/handlers/gates/tool.test.ts +109 -346
- package/tests/handlers/input-events.test.ts +10 -21
- package/tests/handlers/input.test.ts +26 -43
- package/tests/handlers/lifecycle.test.ts +47 -66
- package/tests/handlers/tool-call-events.test.ts +29 -40
- package/tests/handlers/tool-call.test.ts +19 -30
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { PermissionPromptDecision } from "../../permission-dialog";
|
|
2
|
+
import { applyPermissionGate } from "../../permission-gate";
|
|
3
|
+
import type { PermissionCheckResult } from "../../types";
|
|
4
|
+
import type { GateDescriptor, GateRunnerDeps } from "./descriptor";
|
|
5
|
+
import { deriveResolution } from "./helpers";
|
|
6
|
+
import type { GateOutcome } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Execute the full check→log→emit→approve cycle for a gate descriptor.
|
|
10
|
+
*
|
|
11
|
+
* This is the single site for:
|
|
12
|
+
* - Permission checking (or using pre-resolved state)
|
|
13
|
+
* - Session-hit fast path
|
|
14
|
+
* - Interactive prompt orchestration
|
|
15
|
+
* - Decision event emission
|
|
16
|
+
* - Session-rule recording
|
|
17
|
+
*
|
|
18
|
+
* Gate functions produce descriptors; this runner executes them.
|
|
19
|
+
*/
|
|
20
|
+
export async function runGateCheck(
|
|
21
|
+
descriptor: GateDescriptor,
|
|
22
|
+
agentName: string | null,
|
|
23
|
+
toolCallId: string,
|
|
24
|
+
deps: GateRunnerDeps,
|
|
25
|
+
): Promise<GateOutcome> {
|
|
26
|
+
// 1. Resolve permission state — pre-check, pre-resolved, or via checkPermission
|
|
27
|
+
let check: PermissionCheckResult;
|
|
28
|
+
if (descriptor.preCheck) {
|
|
29
|
+
check = descriptor.preCheck;
|
|
30
|
+
} else if (descriptor.preResolved) {
|
|
31
|
+
check = {
|
|
32
|
+
state: descriptor.preResolved.state,
|
|
33
|
+
toolName: descriptor.surface,
|
|
34
|
+
source: "tool",
|
|
35
|
+
origin: "builtin",
|
|
36
|
+
};
|
|
37
|
+
} else {
|
|
38
|
+
check = deps.checkPermission(
|
|
39
|
+
descriptor.surface,
|
|
40
|
+
descriptor.input,
|
|
41
|
+
agentName ?? undefined,
|
|
42
|
+
deps.getSessionRuleset(),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. Session-hit fast path
|
|
47
|
+
if (check.source === "session") {
|
|
48
|
+
deps.writeReviewLog("permission_request.session_approved", {
|
|
49
|
+
...descriptor.logContext,
|
|
50
|
+
agentName,
|
|
51
|
+
resolution: "session_approved",
|
|
52
|
+
sessionApprovalPattern: check.matchedPattern,
|
|
53
|
+
});
|
|
54
|
+
deps.emitDecision({
|
|
55
|
+
surface: descriptor.decision.surface,
|
|
56
|
+
value: descriptor.decision.value,
|
|
57
|
+
result: "allow",
|
|
58
|
+
resolution: "session_approved",
|
|
59
|
+
origin: check.origin ?? null,
|
|
60
|
+
agentName: agentName ?? null,
|
|
61
|
+
matchedPattern: check.matchedPattern ?? null,
|
|
62
|
+
});
|
|
63
|
+
return { action: "allow" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 3. Apply the deny/ask/allow gate
|
|
67
|
+
const canConfirm = deps.canConfirm();
|
|
68
|
+
|
|
69
|
+
// Resolve the first pattern for applyPermissionGate's sessionApproval param
|
|
70
|
+
const singleSessionApproval = descriptor.sessionApproval
|
|
71
|
+
? "pattern" in descriptor.sessionApproval
|
|
72
|
+
? {
|
|
73
|
+
surface: descriptor.sessionApproval.surface,
|
|
74
|
+
pattern: descriptor.sessionApproval.pattern,
|
|
75
|
+
}
|
|
76
|
+
: descriptor.sessionApproval.patterns.length > 0
|
|
77
|
+
? {
|
|
78
|
+
surface: descriptor.sessionApproval.surface,
|
|
79
|
+
pattern: descriptor.sessionApproval.patterns[0],
|
|
80
|
+
}
|
|
81
|
+
: undefined
|
|
82
|
+
: undefined;
|
|
83
|
+
|
|
84
|
+
let autoApproved = false;
|
|
85
|
+
const gateResult = await applyPermissionGate({
|
|
86
|
+
state: check.state,
|
|
87
|
+
canConfirm,
|
|
88
|
+
sessionApproval: singleSessionApproval,
|
|
89
|
+
promptForApproval: async () => {
|
|
90
|
+
const decision = await deps.promptPermission({
|
|
91
|
+
requestId: toolCallId,
|
|
92
|
+
...descriptor.promptDetails,
|
|
93
|
+
});
|
|
94
|
+
autoApproved = decision.autoApproved === true;
|
|
95
|
+
return decision;
|
|
96
|
+
},
|
|
97
|
+
writeLog: deps.writeReviewLog,
|
|
98
|
+
logContext: { ...descriptor.logContext, agentName },
|
|
99
|
+
messages: descriptor.messages,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// 4. Determine whether session approval was granted
|
|
103
|
+
const hasSessionApproval =
|
|
104
|
+
gateResult.action === "allow" && gateResult.sessionApproval !== undefined;
|
|
105
|
+
|
|
106
|
+
// 5. Emit decision event
|
|
107
|
+
deps.emitDecision({
|
|
108
|
+
surface: descriptor.decision.surface,
|
|
109
|
+
value: descriptor.decision.value,
|
|
110
|
+
result: gateResult.action === "allow" ? "allow" : "deny",
|
|
111
|
+
resolution: deriveResolution(
|
|
112
|
+
check.state,
|
|
113
|
+
gateResult.action,
|
|
114
|
+
hasSessionApproval,
|
|
115
|
+
canConfirm,
|
|
116
|
+
autoApproved,
|
|
117
|
+
),
|
|
118
|
+
origin: check.origin ?? null,
|
|
119
|
+
agentName: agentName ?? null,
|
|
120
|
+
matchedPattern: check.matchedPattern ?? null,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// 6. Record session approval(s)
|
|
124
|
+
if (gateResult.action === "allow" && hasSessionApproval) {
|
|
125
|
+
if (descriptor.sessionApproval) {
|
|
126
|
+
if ("patterns" in descriptor.sessionApproval) {
|
|
127
|
+
for (const pattern of descriptor.sessionApproval.patterns) {
|
|
128
|
+
deps.approveSessionRule(descriptor.sessionApproval.surface, pattern);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
deps.approveSessionRule(
|
|
132
|
+
descriptor.sessionApproval.surface,
|
|
133
|
+
descriptor.sessionApproval.pattern,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (gateResult.action === "block") {
|
|
140
|
+
return { action: "block", reason: gateResult.reason };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { action: "allow" };
|
|
144
|
+
}
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { toRecord } from "../../common";
|
|
2
2
|
import { normalizePathForComparison } from "../../external-directory";
|
|
3
|
-
import { emitDecisionEvent } from "../../permission-events";
|
|
4
|
-
import { applyPermissionGate } from "../../permission-gate";
|
|
5
3
|
import {
|
|
6
4
|
formatSkillPathAskPrompt,
|
|
7
5
|
formatSkillPathDenyReason,
|
|
8
6
|
} from "../../permission-prompts";
|
|
7
|
+
import type { SkillPromptEntry } from "../../skill-prompt-sanitizer";
|
|
9
8
|
import { findSkillPathMatch } from "../../skill-prompt-sanitizer";
|
|
10
|
-
import type {
|
|
11
|
-
import {
|
|
12
|
-
import type { GateOutcome, ToolCallContext } from "./types";
|
|
9
|
+
import type { GateDescriptor } from "./descriptor";
|
|
10
|
+
import type { ToolCallContext } from "./types";
|
|
13
11
|
|
|
14
12
|
/**
|
|
15
|
-
*
|
|
13
|
+
* Build a pure descriptor for the skill-read permission gate.
|
|
16
14
|
*
|
|
17
15
|
* Returns `null` when the gate does not apply (tool is not `read`, no active
|
|
18
16
|
* skill entries, or the read path does not match any skill).
|
|
17
|
+
* Returns a GateDescriptor with preResolved state from the matched skill entry.
|
|
19
18
|
*/
|
|
20
|
-
export
|
|
19
|
+
export function describeSkillReadGate(
|
|
21
20
|
tcc: ToolCallContext,
|
|
22
|
-
|
|
23
|
-
):
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
getActiveSkillEntries: () => SkillPromptEntry[],
|
|
22
|
+
): GateDescriptor | null {
|
|
23
|
+
const activeSkillEntries = getActiveSkillEntries();
|
|
24
|
+
|
|
25
|
+
if (tcc.toolName !== "read" || activeSkillEntries.length === 0) {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -35,7 +35,7 @@ export async function evaluateSkillReadGate(
|
|
|
35
35
|
const normalizedReadPath = normalizePathForComparison(path, tcc.cwd);
|
|
36
36
|
const matchedSkill = findSkillPathMatch(
|
|
37
37
|
normalizedReadPath,
|
|
38
|
-
|
|
38
|
+
activeSkillEntries,
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
if (!matchedSkill) {
|
|
@@ -47,31 +47,10 @@ export async function evaluateSkillReadGate(
|
|
|
47
47
|
path,
|
|
48
48
|
tcc.agentName ?? undefined,
|
|
49
49
|
);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
state: matchedSkill.state,
|
|
55
|
-
canConfirm: skillReadCanConfirm,
|
|
56
|
-
promptForApproval: () =>
|
|
57
|
-
deps.promptPermission(deps.runtime.runtimeContext!, {
|
|
58
|
-
requestId: tcc.toolCallId,
|
|
59
|
-
source: "skill_read",
|
|
60
|
-
agentName: tcc.agentName,
|
|
61
|
-
message: skillReadMessage,
|
|
62
|
-
toolCallId: tcc.toolCallId,
|
|
63
|
-
toolName: tcc.toolName,
|
|
64
|
-
skillName: matchedSkill.name,
|
|
65
|
-
path,
|
|
66
|
-
}),
|
|
67
|
-
writeLog: deps.runtime.writeReviewLog,
|
|
68
|
-
logContext: {
|
|
69
|
-
source: "skill_read",
|
|
70
|
-
skillName: matchedSkill.name,
|
|
71
|
-
agentName: tcc.agentName,
|
|
72
|
-
path,
|
|
73
|
-
message: skillReadMessage,
|
|
74
|
-
},
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
surface: "skill",
|
|
53
|
+
input: { name: matchedSkill.name },
|
|
75
54
|
messages: {
|
|
76
55
|
denyReason: formatSkillPathDenyReason(
|
|
77
56
|
matchedSkill,
|
|
@@ -86,26 +65,28 @@ export async function evaluateSkillReadGate(
|
|
|
86
65
|
return `User denied access to skill '${matchedSkill.name}'.${denialReason}`;
|
|
87
66
|
},
|
|
88
67
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
68
|
+
promptDetails: {
|
|
69
|
+
source: "skill_read",
|
|
70
|
+
agentName: tcc.agentName,
|
|
71
|
+
message: skillReadMessage,
|
|
72
|
+
toolCallId: tcc.toolCallId,
|
|
73
|
+
toolName: tcc.toolName,
|
|
74
|
+
skillName: matchedSkill.name,
|
|
75
|
+
path,
|
|
76
|
+
},
|
|
77
|
+
logContext: {
|
|
78
|
+
source: "skill_read",
|
|
79
|
+
skillName: matchedSkill.name,
|
|
80
|
+
agentName: tcc.agentName,
|
|
81
|
+
path,
|
|
82
|
+
message: skillReadMessage,
|
|
83
|
+
},
|
|
84
|
+
decision: {
|
|
85
|
+
surface: "skill",
|
|
86
|
+
value: matchedSkill.name,
|
|
87
|
+
},
|
|
88
|
+
preResolved: {
|
|
89
|
+
state: matchedSkill.state,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
111
92
|
}
|
|
@@ -1,55 +1,26 @@
|
|
|
1
1
|
import { PATH_BEARING_TOOLS } from "../../external-directory";
|
|
2
2
|
import { suggestSessionPattern } from "../../pattern-suggest";
|
|
3
|
-
import { emitDecisionEvent } from "../../permission-events";
|
|
4
|
-
import { applyPermissionGate } from "../../permission-gate";
|
|
5
3
|
import {
|
|
6
4
|
formatAskPrompt,
|
|
7
5
|
formatDenyReason,
|
|
8
6
|
formatUserDeniedReason,
|
|
9
7
|
} from "../../permission-prompts";
|
|
10
8
|
import { getPermissionLogContext } from "../../tool-input-preview";
|
|
11
|
-
import type {
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
9
|
+
import type { PermissionCheckResult } from "../../types";
|
|
10
|
+
import type { GateDescriptor } from "./descriptor";
|
|
11
|
+
import { deriveDecisionValue } from "./helpers";
|
|
12
|
+
import type { ToolCallContext } from "./types";
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
|
-
*
|
|
15
|
+
* Build a pure descriptor for the normal tool permission gate.
|
|
17
16
|
*
|
|
18
|
-
*
|
|
17
|
+
* Takes a pre-computed PermissionCheckResult (from checkPermission) and
|
|
18
|
+
* returns a GateDescriptor that the runner can execute. No side effects.
|
|
19
19
|
*/
|
|
20
|
-
export
|
|
20
|
+
export function describeToolGate(
|
|
21
21
|
tcc: ToolCallContext,
|
|
22
|
-
|
|
23
|
-
):
|
|
24
|
-
const check = deps.runtime.permissionManager.checkPermission(
|
|
25
|
-
tcc.toolName,
|
|
26
|
-
tcc.input,
|
|
27
|
-
tcc.agentName ?? undefined,
|
|
28
|
-
deps.runtime.sessionRules.getRuleset(),
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
// Session-hit: already approved by a session rule — skip the gate entirely.
|
|
32
|
-
if (check.source === "session") {
|
|
33
|
-
deps.runtime.writeReviewLog("permission_request.session_approved", {
|
|
34
|
-
source: "tool_call",
|
|
35
|
-
toolCallId: tcc.toolCallId,
|
|
36
|
-
toolName: tcc.toolName,
|
|
37
|
-
agentName: tcc.agentName,
|
|
38
|
-
resolution: "session_approved",
|
|
39
|
-
sessionApprovalPattern: check.matchedPattern,
|
|
40
|
-
});
|
|
41
|
-
emitDecisionEvent(deps.events, {
|
|
42
|
-
surface: tcc.toolName,
|
|
43
|
-
value: deriveDecisionValue(tcc.toolName, check),
|
|
44
|
-
result: "allow",
|
|
45
|
-
resolution: "session_approved",
|
|
46
|
-
origin: check.origin ?? null,
|
|
47
|
-
agentName: tcc.agentName ?? null,
|
|
48
|
-
matchedPattern: check.matchedPattern ?? null,
|
|
49
|
-
});
|
|
50
|
-
return { action: "allow" };
|
|
51
|
-
}
|
|
52
|
-
|
|
22
|
+
check: PermissionCheckResult,
|
|
23
|
+
): GateDescriptor {
|
|
53
24
|
const permissionLogContext = getPermissionLogContext(
|
|
54
25
|
check,
|
|
55
26
|
tcc.input,
|
|
@@ -71,90 +42,50 @@ export async function evaluateToolGate(
|
|
|
71
42
|
typeof (tcc.input as Record<string, unknown>)?.command === "string"
|
|
72
43
|
? ((tcc.input as Record<string, unknown>).command as string)
|
|
73
44
|
: null;
|
|
74
|
-
const
|
|
45
|
+
const unavailableReason = inputCommand
|
|
75
46
|
? `Running bash command '${inputCommand}' requires approval, but no interactive UI is available.`
|
|
76
47
|
: tcc.toolName === "mcp"
|
|
77
48
|
? "Using tool 'mcp' requires approval, but no interactive UI is available."
|
|
78
49
|
: `Using tool '${tcc.toolName}' requires approval, but no interactive UI is available.`;
|
|
79
50
|
|
|
80
|
-
const
|
|
51
|
+
const askMessage = formatAskPrompt(
|
|
81
52
|
check,
|
|
82
53
|
tcc.agentName ?? undefined,
|
|
83
54
|
tcc.input,
|
|
84
55
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
surface: tcc.toolName,
|
|
59
|
+
input: tcc.input,
|
|
60
|
+
messages: {
|
|
61
|
+
denyReason: formatDenyReason(check, tcc.agentName ?? undefined),
|
|
62
|
+
unavailableReason,
|
|
63
|
+
userDeniedReason: (decision) =>
|
|
64
|
+
formatUserDeniedReason(check, decision.denialReason),
|
|
65
|
+
},
|
|
92
66
|
sessionApproval: {
|
|
93
67
|
surface: suggestion.surface,
|
|
94
68
|
pattern: suggestion.pattern,
|
|
95
69
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
toolCallId: tcc.toolCallId,
|
|
105
|
-
toolName: tcc.toolName,
|
|
106
|
-
sessionLabel: suggestion.label,
|
|
107
|
-
...permissionLogContext,
|
|
108
|
-
},
|
|
109
|
-
);
|
|
110
|
-
toolDecisionAutoApproved = decision.autoApproved === true;
|
|
111
|
-
return decision;
|
|
70
|
+
promptDetails: {
|
|
71
|
+
source: "tool_call",
|
|
72
|
+
agentName: tcc.agentName,
|
|
73
|
+
message: askMessage,
|
|
74
|
+
toolCallId: tcc.toolCallId,
|
|
75
|
+
toolName: tcc.toolName,
|
|
76
|
+
sessionLabel: suggestion.label,
|
|
77
|
+
...permissionLogContext,
|
|
112
78
|
},
|
|
113
|
-
writeLog: deps.runtime.writeReviewLog,
|
|
114
79
|
logContext: {
|
|
115
80
|
source: "tool_call",
|
|
116
81
|
toolCallId: tcc.toolCallId,
|
|
117
82
|
toolName: tcc.toolName,
|
|
118
|
-
|
|
119
|
-
message: toolAskMessage,
|
|
83
|
+
message: askMessage,
|
|
120
84
|
...permissionLogContext,
|
|
121
85
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
userDeniedReason: (decision) =>
|
|
126
|
-
formatUserDeniedReason(check, decision.denialReason),
|
|
86
|
+
decision: {
|
|
87
|
+
surface: tcc.toolName,
|
|
88
|
+
value: deriveDecisionValue(tcc.toolName, check),
|
|
127
89
|
},
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const toolGateHasSession =
|
|
131
|
-
toolGate.action === "allow" && toolGate.sessionApproval !== undefined;
|
|
132
|
-
emitDecisionEvent(deps.events, {
|
|
133
|
-
surface: tcc.toolName,
|
|
134
|
-
value: deriveDecisionValue(tcc.toolName, check),
|
|
135
|
-
result: toolGate.action === "allow" ? "allow" : "deny",
|
|
136
|
-
resolution: deriveResolution(
|
|
137
|
-
check.state,
|
|
138
|
-
toolGate.action,
|
|
139
|
-
toolGateHasSession,
|
|
140
|
-
toolCanConfirm,
|
|
141
|
-
toolDecisionAutoApproved,
|
|
142
|
-
),
|
|
143
|
-
origin: check.origin ?? null,
|
|
144
|
-
agentName: tcc.agentName ?? null,
|
|
145
|
-
matchedPattern: check.matchedPattern ?? null,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
if (toolGate.action === "block") {
|
|
149
|
-
return { action: "block", reason: toolGate.reason };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (toolGate.sessionApproval) {
|
|
153
|
-
deps.runtime.sessionRules.approve(
|
|
154
|
-
toolGate.sessionApproval.surface,
|
|
155
|
-
toolGate.sessionApproval.pattern,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return { action: "allow" };
|
|
90
|
+
};
|
|
160
91
|
}
|
package/src/handlers/input.ts
CHANGED
|
@@ -40,7 +40,7 @@ export async function handleInput(
|
|
|
40
40
|
event: InputPayload,
|
|
41
41
|
ctx: ExtensionContext,
|
|
42
42
|
): Promise<InputEventResult> {
|
|
43
|
-
deps.
|
|
43
|
+
deps.session.runtimeContext = ctx;
|
|
44
44
|
deps.startForwardedPermissionPolling(ctx);
|
|
45
45
|
|
|
46
46
|
const skillName = extractSkillNameFromInput(event.text);
|
|
@@ -49,7 +49,7 @@ export async function handleInput(
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const agentName = deps.resolveAgentName(ctx);
|
|
52
|
-
const check = deps.
|
|
52
|
+
const check = deps.session.permissionManager.checkPermission(
|
|
53
53
|
"skill",
|
|
54
54
|
{ name: skillName },
|
|
55
55
|
agentName ?? undefined,
|
|
@@ -82,7 +82,7 @@ export async function handleInput(
|
|
|
82
82
|
skillInputAutoApproved = decision.autoApproved === true;
|
|
83
83
|
return decision;
|
|
84
84
|
},
|
|
85
|
-
writeLog: deps.
|
|
85
|
+
writeLog: deps.writeReviewLog,
|
|
86
86
|
logContext: {
|
|
87
87
|
source: "skill_input",
|
|
88
88
|
skillName,
|
|
@@ -19,25 +19,25 @@ export async function handleSessionStart(
|
|
|
19
19
|
event: SessionStartPayload,
|
|
20
20
|
ctx: ExtensionContext,
|
|
21
21
|
): Promise<void> {
|
|
22
|
-
deps.
|
|
22
|
+
deps.session.runtimeContext = ctx;
|
|
23
23
|
deps.refreshExtensionConfig(ctx);
|
|
24
|
-
deps.
|
|
25
|
-
deps.
|
|
26
|
-
deps.
|
|
27
|
-
deps.
|
|
28
|
-
deps.
|
|
24
|
+
deps.session.permissionManager = deps.createPermissionManagerForCwd(ctx.cwd);
|
|
25
|
+
deps.session.activeSkillEntries = [];
|
|
26
|
+
deps.session.lastActiveToolsCacheKey = null;
|
|
27
|
+
deps.session.lastPromptStateCacheKey = null;
|
|
28
|
+
deps.session.lastKnownActiveAgentName = getActiveAgentName(ctx);
|
|
29
29
|
deps.startForwardedPermissionPolling(ctx);
|
|
30
30
|
deps.logResolvedConfigPaths();
|
|
31
31
|
|
|
32
|
-
const agentName = deps.
|
|
32
|
+
const agentName = deps.session.lastKnownActiveAgentName;
|
|
33
33
|
const policyIssues =
|
|
34
|
-
deps.
|
|
34
|
+
deps.session.permissionManager.getConfigIssues(agentName);
|
|
35
35
|
for (const issue of policyIssues) {
|
|
36
36
|
deps.notifyWarning(issue);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (event.reason === "reload") {
|
|
40
|
-
deps.
|
|
40
|
+
deps.writeDebugLog("lifecycle.reload", {
|
|
41
41
|
triggeredBy: "session_start",
|
|
42
42
|
reason: event.reason,
|
|
43
43
|
cwd: ctx.cwd,
|
|
@@ -53,14 +53,14 @@ export async function handleResourcesDiscover(
|
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const { runtimeContext } = deps.
|
|
57
|
-
deps.
|
|
56
|
+
const { runtimeContext } = deps.session;
|
|
57
|
+
deps.session.permissionManager = deps.createPermissionManagerForCwd(
|
|
58
58
|
runtimeContext?.cwd,
|
|
59
59
|
);
|
|
60
|
-
deps.
|
|
61
|
-
deps.
|
|
62
|
-
deps.
|
|
63
|
-
deps.
|
|
60
|
+
deps.session.activeSkillEntries = [];
|
|
61
|
+
deps.session.lastActiveToolsCacheKey = null;
|
|
62
|
+
deps.session.lastPromptStateCacheKey = null;
|
|
63
|
+
deps.writeDebugLog("lifecycle.reload", {
|
|
64
64
|
triggeredBy: "resources_discover",
|
|
65
65
|
reason: event.reason,
|
|
66
66
|
cwd: runtimeContext?.cwd ?? null,
|
|
@@ -68,15 +68,15 @@ export async function handleResourcesDiscover(
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export async function handleSessionShutdown(deps: HandlerDeps): Promise<void> {
|
|
71
|
-
const { runtimeContext } = deps.
|
|
71
|
+
const { runtimeContext } = deps.session;
|
|
72
72
|
if (runtimeContext) {
|
|
73
73
|
runtimeContext.ui.setStatus(PERMISSION_SYSTEM_STATUS_KEY, undefined);
|
|
74
74
|
}
|
|
75
|
-
deps.
|
|
76
|
-
deps.
|
|
77
|
-
deps.
|
|
78
|
-
deps.
|
|
79
|
-
deps.
|
|
75
|
+
deps.session.runtimeContext = null;
|
|
76
|
+
deps.session.activeSkillEntries = [];
|
|
77
|
+
deps.session.lastActiveToolsCacheKey = null;
|
|
78
|
+
deps.session.lastPromptStateCacheKey = null;
|
|
79
|
+
deps.session.sessionRules.clear();
|
|
80
80
|
deps.stopForwardedPermissionPolling();
|
|
81
81
|
deps.stopPermissionRpcHandlers();
|
|
82
82
|
}
|