@gotgenes/pi-permission-system 3.7.0 → 3.8.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.
@@ -61,7 +61,7 @@ export async function handleToolCall(
61
61
  event: unknown,
62
62
  ctx: ExtensionContext,
63
63
  ): Promise<{ block?: true; reason?: string }> {
64
- deps.setRuntimeContext(ctx);
64
+ deps.runtime.runtimeContext = ctx;
65
65
  deps.startForwardedPermissionPolling(ctx);
66
66
 
67
67
  const agentName = deps.resolveAgentName(ctx);
@@ -92,7 +92,7 @@ export async function handleToolCall(
92
92
  // ── Skill-read gate ──────────────────────────────────────────────────────
93
93
  if (
94
94
  isToolCallEventType("read", event as ToolCallEvent) &&
95
- deps.getActiveSkillEntries().length > 0
95
+ deps.runtime.activeSkillEntries.length > 0
96
96
  ) {
97
97
  const normalizedReadPath = normalizePathForComparison(
98
98
  (event as ToolCallEvent & { input: { path: string } }).input.path,
@@ -100,7 +100,7 @@ export async function handleToolCall(
100
100
  );
101
101
  const matchedSkill = findSkillPathMatch(
102
102
  normalizedReadPath,
103
- deps.getActiveSkillEntries(),
103
+ deps.runtime.activeSkillEntries,
104
104
  );
105
105
 
106
106
  if (matchedSkill) {
@@ -124,7 +124,7 @@ export async function handleToolCall(
124
124
  skillName: matchedSkill.name,
125
125
  path: readEvent.input.path,
126
126
  }),
127
- writeLog: deps.writeReviewLog,
127
+ writeLog: deps.runtime.writeReviewLog,
128
128
  logContext: {
129
129
  source: "skill_read",
130
130
  skillName: matchedSkill.name,
@@ -169,13 +169,13 @@ export async function handleToolCall(
169
169
  externalDirectoryPath,
170
170
  ctx.cwd,
171
171
  );
172
- const sessionPrefix = deps.sessionApprovalCache.findMatchingPrefix(
172
+ const sessionPrefix = deps.runtime.sessionApprovalCache.findMatchingPrefix(
173
173
  "external_directory",
174
174
  normalizedExtPath,
175
175
  );
176
176
 
177
177
  if (sessionPrefix) {
178
- deps.writeReviewLog("permission_request.session_approved", {
178
+ deps.runtime.writeReviewLog("permission_request.session_approved", {
179
179
  source: "tool_call",
180
180
  toolCallId: (event as { toolCallId: string }).toolCallId,
181
181
  toolName,
@@ -186,9 +186,11 @@ export async function handleToolCall(
186
186
  });
187
187
  // Fall through to normal permission check
188
188
  } else {
189
- const extCheck = deps
190
- .getPermissionManager()
191
- .checkPermission("external_directory", {}, agentName ?? undefined);
189
+ const extCheck = deps.runtime.permissionManager.checkPermission(
190
+ "external_directory",
191
+ {},
192
+ agentName ?? undefined,
193
+ );
192
194
 
193
195
  let extDirDecision: PermissionPromptDecision | null = null;
194
196
  const extDirMessage = formatExternalDirectoryAskPrompt(
@@ -213,7 +215,7 @@ export async function handleToolCall(
213
215
  extDirDecision = decision;
214
216
  return decision;
215
217
  },
216
- writeLog: deps.writeReviewLog,
218
+ writeLog: deps.runtime.writeReviewLog,
217
219
  logContext: {
218
220
  source: "tool_call",
219
221
  toolCallId: (event as { toolCallId: string }).toolCallId,
@@ -244,7 +246,7 @@ export async function handleToolCall(
244
246
 
245
247
  if (extDirDecision?.state === "approved_for_session") {
246
248
  const prefix = deriveApprovalPrefix(normalizedExtPath);
247
- deps.sessionApprovalCache.approve("external_directory", prefix);
249
+ deps.runtime.sessionApprovalCache.approve("external_directory", prefix);
248
250
  }
249
251
  }
250
252
  // Fall through to normal permission check
@@ -260,11 +262,12 @@ export async function handleToolCall(
260
262
  );
261
263
  if (externalPaths.length > 0) {
262
264
  const uncoveredPaths = externalPaths.filter(
263
- (p) => !deps.sessionApprovalCache.has("external_directory", p),
265
+ (p) =>
266
+ !deps.runtime.sessionApprovalCache.has("external_directory", p),
264
267
  );
265
268
 
266
269
  if (uncoveredPaths.length === 0) {
267
- deps.writeReviewLog("permission_request.session_approved", {
270
+ deps.runtime.writeReviewLog("permission_request.session_approved", {
268
271
  source: "tool_call",
269
272
  toolCallId: (event as { toolCallId: string }).toolCallId,
270
273
  toolName,
@@ -275,9 +278,11 @@ export async function handleToolCall(
275
278
  });
276
279
  // Fall through to normal bash permission check
277
280
  } else {
278
- const extCheck = deps
279
- .getPermissionManager()
280
- .checkPermission("external_directory", {}, agentName ?? undefined);
281
+ const extCheck = deps.runtime.permissionManager.checkPermission(
282
+ "external_directory",
283
+ {},
284
+ agentName ?? undefined,
285
+ );
281
286
 
282
287
  let bashExtDecision: PermissionPromptDecision | null = null;
283
288
  const bashExtMessage = formatBashExternalDirectoryAskPrompt(
@@ -302,7 +307,7 @@ export async function handleToolCall(
302
307
  bashExtDecision = decision;
303
308
  return decision;
304
309
  },
305
- writeLog: deps.writeReviewLog,
310
+ writeLog: deps.runtime.writeReviewLog,
306
311
  logContext: {
307
312
  source: "tool_call",
308
313
  toolCallId: (event as { toolCallId: string }).toolCallId,
@@ -335,7 +340,10 @@ export async function handleToolCall(
335
340
  if (bashExtDecision?.state === "approved_for_session") {
336
341
  for (const extPath of uncoveredPaths) {
337
342
  const prefix = deriveApprovalPrefix(extPath);
338
- deps.sessionApprovalCache.approve("external_directory", prefix);
343
+ deps.runtime.sessionApprovalCache.approve(
344
+ "external_directory",
345
+ prefix,
346
+ );
339
347
  }
340
348
  }
341
349
  }
@@ -345,9 +353,11 @@ export async function handleToolCall(
345
353
  }
346
354
 
347
355
  // ── Normal tool permission gate ───────────────────────────────────────────
348
- const check = deps
349
- .getPermissionManager()
350
- .checkPermission(toolName, input, agentName ?? undefined);
356
+ const check = deps.runtime.permissionManager.checkPermission(
357
+ toolName,
358
+ input,
359
+ agentName ?? undefined,
360
+ );
351
361
  const permissionLogContext = getPermissionLogContext(
352
362
  check,
353
363
  input,
@@ -375,7 +385,7 @@ export async function handleToolCall(
375
385
  toolName,
376
386
  ...permissionLogContext,
377
387
  }),
378
- writeLog: deps.writeReviewLog,
388
+ writeLog: deps.runtime.writeReviewLog,
379
389
  logContext: {
380
390
  source: "tool_call",
381
391
  toolCallId: (event as { toolCallId: string }).toolCallId,
@@ -2,8 +2,7 @@ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
2
 
3
3
  import type { PermissionPromptDecision } from "../permission-dialog";
4
4
  import type { PermissionManager } from "../permission-manager";
5
- import type { SessionApprovalCache } from "../session-approval-cache";
6
- import type { SkillPromptEntry } from "../skill-prompt-sanitizer";
5
+ import type { ExtensionRuntime } from "../runtime";
7
6
 
8
7
  export type PermissionReviewSource = "tool_call" | "skill_input" | "skill_read";
9
8
 
@@ -25,31 +24,13 @@ export interface PromptPermissionDetails {
25
24
  /**
26
25
  * Explicit dependency bag passed to each extracted event handler.
27
26
  *
28
- * All mutable shared state is wrapped in getter/setter pairs so that:
29
- * - Tests can construct a plain object with stubs and exercise handlers in
30
- * isolation without importing src/index.ts.
31
- * - Issue #43 can replace this interface with ExtensionRuntime without
32
- * changing handler signatures — only the deps object construction in
33
- * index.ts needs to change.
27
+ * Mutable state lives in `runtime`; handlers read and write `deps.runtime.*`
28
+ * directly instead of going through getter/setter pairs.
34
29
  */
35
30
  export interface HandlerDeps {
36
- // ── Mutable state accessors ────────────────────────────────────────────
37
- getPermissionManager(): PermissionManager;
38
- setPermissionManager(pm: PermissionManager): void;
39
- getRuntimeContext(): ExtensionContext | null;
40
- setRuntimeContext(ctx: ExtensionContext | null): void;
41
- getActiveSkillEntries(): SkillPromptEntry[];
42
- setActiveSkillEntries(entries: SkillPromptEntry[]): void;
43
- getLastKnownActiveAgentName(): string | null;
44
- setLastKnownActiveAgentName(name: string | null): void;
45
- /** Cache key for the last set of active tools passed to setActiveTools(). */
46
- getLastActiveToolsCacheKey(): string | null;
47
- setLastActiveToolsCacheKey(key: string | null): void;
48
- /** Cache key for the last before_agent_start prompt state. */
49
- getLastPromptStateCacheKey(): string | null;
50
- setLastPromptStateCacheKey(key: string | null): void;
51
- /** Session-scoped approval cache (passed by reference; mutations are visible). */
52
- sessionApprovalCache: SessionApprovalCache;
31
+ // ── Runtime context ────────────────────────────────────────────────────
32
+ /** All mutable extension state and log-writing methods. */
33
+ readonly runtime: ExtensionRuntime;
53
34
 
54
35
  // ── Factories ──────────────────────────────────────────────────────────
55
36
  /** Create a new PermissionManager scoped to cwd's config hierarchy. */
@@ -68,7 +49,7 @@ export interface HandlerDeps {
68
49
  // ── Permission helpers ─────────────────────────────────────────────────
69
50
  /**
70
51
  * Resolve the active agent name from the session context or system prompt.
71
- * Updates the stored lastKnownActiveAgentName as a side effect.
52
+ * Updates runtime.lastKnownActiveAgentName as a side effect.
72
53
  */
73
54
  resolveAgentName(ctx: ExtensionContext, systemPrompt?: string): string | null;
74
55
  /** Whether the current context can show an interactive permission prompt. */
@@ -85,10 +66,6 @@ export interface HandlerDeps {
85
66
  startForwardedPermissionPolling(ctx: ExtensionContext): void;
86
67
  stopForwardedPermissionPolling(): void;
87
68
 
88
- // ── Logging ────────────────────────────────────────────────────────────
89
- writeReviewLog(event: string, details?: Record<string, unknown>): void;
90
- writeDebugLog(event: string, details?: Record<string, unknown>): void;
91
-
92
69
  // ── Pi API subset ──────────────────────────────────────────────────────
93
70
  getAllTools(): unknown[];
94
71
  setActiveTools(names: string[]): void;