@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.
@@ -6,7 +6,7 @@ import {
6
6
  handleInput,
7
7
  } from "../../src/handlers/input";
8
8
  import type { HandlerDeps } from "../../src/handlers/types";
9
- import type { ExtensionRuntime } from "../../src/runtime";
9
+ import type { SessionState } from "../../src/runtime";
10
10
  import type { SkillPromptEntry } from "../../src/skill-prompt-sanitizer";
11
11
 
12
12
  // ── helpers ────────────────────────────────────────────────────────────────
@@ -34,42 +34,32 @@ function makeInputEvent(text: string) {
34
34
  return { text };
35
35
  }
36
36
 
37
- function makeRuntime(
38
- overrides: Partial<ExtensionRuntime> = {},
39
- ): ExtensionRuntime {
37
+ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
40
38
  return {
41
- agentDir: "/test/agent",
42
- sessionsDir: "/test/agent/sessions",
43
- subagentSessionsDir: "/test/agent/subagent-sessions",
44
- forwardingDir: "/test/agent/sessions/permission-forwarding",
45
- globalLogsDir: "/test/agent/extensions/pi-permission-system/logs",
46
- config: { debugLog: false, permissionReviewLog: true, yoloMode: false },
47
39
  runtimeContext: null,
48
40
  permissionManager: {
49
41
  checkPermission: vi.fn().mockReturnValue({ state: "allow" }),
50
- } as unknown as ExtensionRuntime["permissionManager"],
42
+ } as unknown as SessionState["permissionManager"],
51
43
  activeSkillEntries: [] as SkillPromptEntry[],
52
44
  lastKnownActiveAgentName: null,
53
45
  lastActiveToolsCacheKey: null,
54
46
  lastPromptStateCacheKey: null,
55
- lastConfigWarning: null,
56
47
  sessionRules: {
57
48
  approve: vi.fn(),
58
49
  getRuleset: vi.fn().mockReturnValue([]),
59
50
  clear: vi.fn(),
60
- } as unknown as ExtensionRuntime["sessionRules"],
61
- permissionForwardingContext: null,
62
- permissionForwardingTimer: null,
63
- isProcessingForwardedRequests: false,
64
- writeDebugLog: vi.fn(),
65
- writeReviewLog: vi.fn(),
51
+ } as unknown as SessionState["sessionRules"],
66
52
  ...overrides,
67
- } as ExtensionRuntime;
53
+ };
68
54
  }
69
55
 
70
56
  function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
71
57
  return {
72
- runtime: makeRuntime(),
58
+ session: makeSession(),
59
+ writeDebugLog: vi.fn(),
60
+ writeReviewLog: vi.fn(),
61
+ piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
62
+ getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
73
63
  createPermissionManagerForCwd: vi.fn(),
74
64
  refreshExtensionConfig: vi.fn(),
75
65
  notifyWarning: vi.fn(),
@@ -131,7 +121,7 @@ describe("handleInput", () => {
131
121
  const ctx = makeCtx();
132
122
  const deps = makeDeps();
133
123
  await handleInput(deps, makeInputEvent("hello"), ctx);
134
- expect(deps.runtime.runtimeContext).toBe(ctx);
124
+ expect(deps.session.runtimeContext).toBe(ctx);
135
125
  });
136
126
 
137
127
  it("starts forwarded permission polling", async () => {
@@ -155,7 +145,7 @@ describe("handleInput", () => {
155
145
  const deps = makeDeps();
156
146
  await handleInput(deps, makeInputEvent("just a message"), makeCtx());
157
147
  expect(
158
- deps.runtime.permissionManager.checkPermission,
148
+ deps.session.permissionManager.checkPermission,
159
149
  ).not.toHaveBeenCalled();
160
150
  });
161
151
 
@@ -175,9 +165,8 @@ describe("handleInput", () => {
175
165
  checkPermission: vi.fn().mockReturnValue({ state: "deny" }),
176
166
  };
177
167
  const deps = makeDeps({
178
- runtime: makeRuntime({
179
- permissionManager:
180
- pm as unknown as ExtensionRuntime["permissionManager"],
168
+ session: makeSession({
169
+ permissionManager: pm as unknown as SessionState["permissionManager"],
181
170
  }),
182
171
  });
183
172
  const result = await handleInput(
@@ -194,9 +183,8 @@ describe("handleInput", () => {
194
183
  checkPermission: vi.fn().mockReturnValue({ state: "deny" }),
195
184
  };
196
185
  const deps = makeDeps({
197
- runtime: makeRuntime({
198
- permissionManager:
199
- pm as unknown as ExtensionRuntime["permissionManager"],
186
+ session: makeSession({
187
+ permissionManager: pm as unknown as SessionState["permissionManager"],
200
188
  }),
201
189
  });
202
190
  await handleInput(deps, makeInputEvent("/skill:librarian"), ctx);
@@ -210,9 +198,8 @@ describe("handleInput", () => {
210
198
  const ctx = makeCtx({ hasUI: false });
211
199
  const pm = { checkPermission: vi.fn().mockReturnValue({ state: "deny" }) };
212
200
  const deps = makeDeps({
213
- runtime: makeRuntime({
214
- permissionManager:
215
- pm as unknown as ExtensionRuntime["permissionManager"],
201
+ session: makeSession({
202
+ permissionManager: pm as unknown as SessionState["permissionManager"],
216
203
  }),
217
204
  });
218
205
  await handleInput(deps, makeInputEvent("/skill:librarian"), ctx);
@@ -222,9 +209,8 @@ describe("handleInput", () => {
222
209
  it("returns handled when skill requires approval but no UI is available", async () => {
223
210
  const pm = { checkPermission: vi.fn().mockReturnValue({ state: "ask" }) };
224
211
  const deps = makeDeps({
225
- runtime: makeRuntime({
226
- permissionManager:
227
- pm as unknown as ExtensionRuntime["permissionManager"],
212
+ session: makeSession({
213
+ permissionManager: pm as unknown as SessionState["permissionManager"],
228
214
  }),
229
215
  canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
230
216
  });
@@ -239,9 +225,8 @@ describe("handleInput", () => {
239
225
  it("prompts and returns continue when skill ask is approved", async () => {
240
226
  const pm = { checkPermission: vi.fn().mockReturnValue({ state: "ask" }) };
241
227
  const deps = makeDeps({
242
- runtime: makeRuntime({
243
- permissionManager:
244
- pm as unknown as ExtensionRuntime["permissionManager"],
228
+ session: makeSession({
229
+ permissionManager: pm as unknown as SessionState["permissionManager"],
245
230
  }),
246
231
  canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
247
232
  promptPermission: vi
@@ -260,9 +245,8 @@ describe("handleInput", () => {
260
245
  it("returns handled when skill ask is denied by user", async () => {
261
246
  const pm = { checkPermission: vi.fn().mockReturnValue({ state: "ask" }) };
262
247
  const deps = makeDeps({
263
- runtime: makeRuntime({
264
- permissionManager:
265
- pm as unknown as ExtensionRuntime["permissionManager"],
248
+ session: makeSession({
249
+ permissionManager: pm as unknown as SessionState["permissionManager"],
266
250
  }),
267
251
  canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
268
252
  promptPermission: vi
@@ -280,9 +264,8 @@ describe("handleInput", () => {
280
264
  it("passes agentName in the prompt permission request", async () => {
281
265
  const pm = { checkPermission: vi.fn().mockReturnValue({ state: "ask" }) };
282
266
  const deps = makeDeps({
283
- runtime: makeRuntime({
284
- permissionManager:
285
- pm as unknown as ExtensionRuntime["permissionManager"],
267
+ session: makeSession({
268
+ permissionManager: pm as unknown as SessionState["permissionManager"],
286
269
  }),
287
270
  resolveAgentName: vi.fn().mockReturnValue("code-agent"),
288
271
  canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
@@ -7,7 +7,7 @@ import {
7
7
  } from "../../src/handlers/lifecycle";
8
8
  import type { HandlerDeps } from "../../src/handlers/types";
9
9
  import type { PermissionManager } from "../../src/permission-manager";
10
- import type { ExtensionRuntime } from "../../src/runtime";
10
+ import type { SessionState } from "../../src/runtime";
11
11
  import type { SessionRules } from "../../src/session-rules";
12
12
  import type { SkillPromptEntry } from "../../src/skill-prompt-sanitizer";
13
13
 
@@ -67,36 +67,26 @@ function makeSessionRules(): SessionRules {
67
67
  } as unknown as SessionRules;
68
68
  }
69
69
 
70
- function makeRuntime(
71
- overrides: Partial<ExtensionRuntime> = {},
72
- ): ExtensionRuntime {
70
+ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
73
71
  return {
74
- agentDir: "/test/agent",
75
- sessionsDir: "/test/agent/sessions",
76
- subagentSessionsDir: "/test/agent/subagent-sessions",
77
- forwardingDir: "/test/agent/sessions/permission-forwarding",
78
- globalLogsDir: "/test/agent/extensions/pi-permission-system/logs",
79
- config: { debugLog: false, permissionReviewLog: true, yoloMode: false },
80
72
  runtimeContext: null,
81
73
  permissionManager: makePermissionManager() as unknown as PermissionManager,
82
74
  activeSkillEntries: [] as SkillPromptEntry[],
83
75
  lastKnownActiveAgentName: null,
84
76
  lastActiveToolsCacheKey: null,
85
77
  lastPromptStateCacheKey: null,
86
- lastConfigWarning: null,
87
78
  sessionRules: makeSessionRules(),
88
- permissionForwardingContext: null,
89
- permissionForwardingTimer: null,
90
- isProcessingForwardedRequests: false,
91
- writeDebugLog: vi.fn(),
92
- writeReviewLog: vi.fn(),
93
79
  ...overrides,
94
- } as ExtensionRuntime;
80
+ };
95
81
  }
96
82
 
97
83
  function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
98
84
  return {
99
- runtime: makeRuntime(),
85
+ session: makeSession(),
86
+ writeDebugLog: vi.fn(),
87
+ writeReviewLog: vi.fn(),
88
+ piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
89
+ getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
100
90
  createPermissionManagerForCwd: vi
101
91
  .fn()
102
92
  .mockReturnValue(makePermissionManager()),
@@ -131,7 +121,7 @@ describe("handleSessionStart", () => {
131
121
  const ctx = makeCtx();
132
122
  const deps = makeDeps();
133
123
  await handleSessionStart(deps, { reason: "startup" }, ctx);
134
- expect(deps.runtime.runtimeContext).toBe(ctx);
124
+ expect(deps.session.runtimeContext).toBe(ctx);
135
125
  });
136
126
 
137
127
  it("refreshes extension config with ctx", async () => {
@@ -151,16 +141,16 @@ describe("handleSessionStart", () => {
151
141
  expect(deps.createPermissionManagerForCwd).toHaveBeenCalledWith(
152
142
  "/my/project",
153
143
  );
154
- expect(deps.runtime.permissionManager).toBe(newPm);
144
+ expect(deps.session.permissionManager).toBe(newPm);
155
145
  });
156
146
 
157
147
  it("clears the before_agent_start cache", async () => {
158
148
  const ctx = makeCtx();
159
149
  const deps = makeDeps();
160
150
  await handleSessionStart(deps, { reason: "startup" }, ctx);
161
- expect(deps.runtime.activeSkillEntries).toEqual([]);
162
- expect(deps.runtime.lastActiveToolsCacheKey).toBeNull();
163
- expect(deps.runtime.lastPromptStateCacheKey).toBeNull();
151
+ expect(deps.session.activeSkillEntries).toEqual([]);
152
+ expect(deps.session.lastActiveToolsCacheKey).toBeNull();
153
+ expect(deps.session.lastPromptStateCacheKey).toBeNull();
164
154
  });
165
155
 
166
156
  it("sets lastKnownActiveAgentName from getActiveAgentName", async () => {
@@ -168,7 +158,7 @@ describe("handleSessionStart", () => {
168
158
  const ctx = makeCtx();
169
159
  const deps = makeDeps();
170
160
  await handleSessionStart(deps, { reason: "startup" }, ctx);
171
- expect(deps.runtime.lastKnownActiveAgentName).toBe("my-agent");
161
+ expect(deps.session.lastKnownActiveAgentName).toBe("my-agent");
172
162
  });
173
163
 
174
164
  it("sets lastKnownActiveAgentName to null when no agent is active", async () => {
@@ -176,7 +166,7 @@ describe("handleSessionStart", () => {
176
166
  const ctx = makeCtx();
177
167
  const deps = makeDeps();
178
168
  await handleSessionStart(deps, { reason: "startup" }, ctx);
179
- expect(deps.runtime.lastKnownActiveAgentName).toBeNull();
169
+ expect(deps.session.lastKnownActiveAgentName).toBeNull();
180
170
  });
181
171
 
182
172
  it("starts forwarded permission polling", async () => {
@@ -213,20 +203,17 @@ describe("handleSessionStart", () => {
213
203
  const ctx = makeCtx({ cwd: "/proj" });
214
204
  const deps = makeDeps();
215
205
  await handleSessionStart(deps, { reason: "reload" }, ctx);
216
- expect(deps.runtime.writeDebugLog).toHaveBeenCalledWith(
217
- "lifecycle.reload",
218
- {
219
- triggeredBy: "session_start",
220
- reason: "reload",
221
- cwd: "/proj",
222
- },
223
- );
206
+ expect(deps.writeDebugLog).toHaveBeenCalledWith("lifecycle.reload", {
207
+ triggeredBy: "session_start",
208
+ reason: "reload",
209
+ cwd: "/proj",
210
+ });
224
211
  });
225
212
 
226
213
  it("does not write lifecycle.reload debug log for non-reload reasons", async () => {
227
214
  const deps = makeDeps();
228
215
  await handleSessionStart(deps, { reason: "startup" }, makeCtx());
229
- expect(deps.runtime.writeDebugLog).not.toHaveBeenCalled();
216
+ expect(deps.writeDebugLog).not.toHaveBeenCalled();
230
217
  });
231
218
  });
232
219
 
@@ -237,21 +224,21 @@ describe("handleResourcesDiscover", () => {
237
224
  const deps = makeDeps();
238
225
  await handleResourcesDiscover(deps, { reason: "startup" });
239
226
  expect(deps.createPermissionManagerForCwd).not.toHaveBeenCalled();
240
- expect(deps.runtime.writeDebugLog).not.toHaveBeenCalled();
227
+ expect(deps.writeDebugLog).not.toHaveBeenCalled();
241
228
  });
242
229
 
243
230
  it("creates and stores a new PM using runtimeContext.cwd on reload", async () => {
244
231
  const ctx = makeCtx({ cwd: "/runtime/cwd" });
245
232
  const newPm = makePermissionManager();
246
233
  const deps = makeDeps({
247
- runtime: makeRuntime({ runtimeContext: ctx }),
234
+ session: makeSession({ runtimeContext: ctx }),
248
235
  createPermissionManagerForCwd: vi.fn().mockReturnValue(newPm),
249
236
  });
250
237
  await handleResourcesDiscover(deps, { reason: "reload" });
251
238
  expect(deps.createPermissionManagerForCwd).toHaveBeenCalledWith(
252
239
  "/runtime/cwd",
253
240
  );
254
- expect(deps.runtime.permissionManager).toBe(newPm);
241
+ expect(deps.session.permissionManager).toBe(newPm);
255
242
  });
256
243
 
257
244
  it("uses undefined cwd when runtimeContext is null on reload", async () => {
@@ -263,36 +250,30 @@ describe("handleResourcesDiscover", () => {
263
250
  it("clears the before_agent_start cache on reload", async () => {
264
251
  const deps = makeDeps();
265
252
  await handleResourcesDiscover(deps, { reason: "reload" });
266
- expect(deps.runtime.activeSkillEntries).toEqual([]);
267
- expect(deps.runtime.lastActiveToolsCacheKey).toBeNull();
268
- expect(deps.runtime.lastPromptStateCacheKey).toBeNull();
253
+ expect(deps.session.activeSkillEntries).toEqual([]);
254
+ expect(deps.session.lastActiveToolsCacheKey).toBeNull();
255
+ expect(deps.session.lastPromptStateCacheKey).toBeNull();
269
256
  });
270
257
 
271
258
  it("writes lifecycle.reload debug log on reload", async () => {
272
259
  const ctx = makeCtx({ cwd: "/proj" });
273
- const deps = makeDeps({ runtime: makeRuntime({ runtimeContext: ctx }) });
260
+ const deps = makeDeps({ session: makeSession({ runtimeContext: ctx }) });
274
261
  await handleResourcesDiscover(deps, { reason: "reload" });
275
- expect(deps.runtime.writeDebugLog).toHaveBeenCalledWith(
276
- "lifecycle.reload",
277
- {
278
- triggeredBy: "resources_discover",
279
- reason: "reload",
280
- cwd: "/proj",
281
- },
282
- );
262
+ expect(deps.writeDebugLog).toHaveBeenCalledWith("lifecycle.reload", {
263
+ triggeredBy: "resources_discover",
264
+ reason: "reload",
265
+ cwd: "/proj",
266
+ });
283
267
  });
284
268
 
285
269
  it("logs cwd as null when runtimeContext is null on reload", async () => {
286
270
  const deps = makeDeps();
287
271
  await handleResourcesDiscover(deps, { reason: "reload" });
288
- expect(deps.runtime.writeDebugLog).toHaveBeenCalledWith(
289
- "lifecycle.reload",
290
- {
291
- triggeredBy: "resources_discover",
292
- reason: "reload",
293
- cwd: null,
294
- },
295
- );
272
+ expect(deps.writeDebugLog).toHaveBeenCalledWith("lifecycle.reload", {
273
+ triggeredBy: "resources_discover",
274
+ reason: "reload",
275
+ cwd: null,
276
+ });
296
277
  });
297
278
  });
298
279
 
@@ -302,7 +283,7 @@ describe("handleSessionShutdown", () => {
302
283
  it("clears the UI status when a runtime context is present", async () => {
303
284
  const ctx = makeCtx();
304
285
  const deps = makeDeps({
305
- runtime: makeRuntime({ runtimeContext: ctx }),
286
+ session: makeSession({ runtimeContext: ctx }),
306
287
  });
307
288
  await handleSessionShutdown(deps);
308
289
  expect(ctx.ui.setStatus).toHaveBeenCalledWith(
@@ -318,23 +299,23 @@ describe("handleSessionShutdown", () => {
318
299
 
319
300
  it("sets runtime context to null", async () => {
320
301
  const ctx = makeCtx();
321
- const deps = makeDeps({ runtime: makeRuntime({ runtimeContext: ctx }) });
302
+ const deps = makeDeps({ session: makeSession({ runtimeContext: ctx }) });
322
303
  await handleSessionShutdown(deps);
323
- expect(deps.runtime.runtimeContext).toBeNull();
304
+ expect(deps.session.runtimeContext).toBeNull();
324
305
  });
325
306
 
326
307
  it("clears the before_agent_start cache", async () => {
327
308
  const deps = makeDeps();
328
309
  await handleSessionShutdown(deps);
329
- expect(deps.runtime.activeSkillEntries).toEqual([]);
330
- expect(deps.runtime.lastActiveToolsCacheKey).toBeNull();
331
- expect(deps.runtime.lastPromptStateCacheKey).toBeNull();
310
+ expect(deps.session.activeSkillEntries).toEqual([]);
311
+ expect(deps.session.lastActiveToolsCacheKey).toBeNull();
312
+ expect(deps.session.lastPromptStateCacheKey).toBeNull();
332
313
  });
333
314
 
334
315
  it("clears the session rules", async () => {
335
316
  const deps = makeDeps();
336
317
  await handleSessionShutdown(deps);
337
- expect(deps.runtime.sessionRules.clear).toHaveBeenCalledOnce();
318
+ expect(deps.session.sessionRules.clear).toHaveBeenCalledOnce();
338
319
  });
339
320
 
340
321
  it("stops forwarded permission polling", async () => {
@@ -351,9 +332,9 @@ describe("handleSessionShutdown", () => {
351
332
 
352
333
  it("does not reset lastKnownActiveAgentName", async () => {
353
334
  const deps = makeDeps({
354
- runtime: makeRuntime({ lastKnownActiveAgentName: "remembered" }),
335
+ session: makeSession({ lastKnownActiveAgentName: "remembered" }),
355
336
  });
356
337
  await handleSessionShutdown(deps);
357
- expect(deps.runtime.lastKnownActiveAgentName).toBe("remembered");
338
+ expect(deps.session.lastKnownActiveAgentName).toBe("remembered");
358
339
  });
359
340
  });
@@ -9,7 +9,7 @@ import { handleToolCall } from "../../src/handlers/tool-call";
9
9
  import type { HandlerDeps } from "../../src/handlers/types";
10
10
  import type { PermissionDecisionEvent } from "../../src/permission-events";
11
11
  import { PERMISSIONS_DECISION_CHANNEL } from "../../src/permission-events";
12
- import type { ExtensionRuntime } from "../../src/runtime";
12
+ import type { SessionState } from "../../src/runtime";
13
13
  import type { PermissionCheckResult } from "../../src/types";
14
14
 
15
15
  // ── helpers ────────────────────────────────────────────────────────────────
@@ -67,43 +67,32 @@ function makeCheckResult(
67
67
  };
68
68
  }
69
69
 
70
- function makeRuntime(
71
- overrides: Partial<ExtensionRuntime> = {},
72
- ): ExtensionRuntime {
70
+ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
73
71
  return {
74
- agentDir: "/test/agent",
75
- sessionsDir: "/test/agent/sessions",
76
- subagentSessionsDir: "/test/agent/subagent-sessions",
77
- forwardingDir: "/test/agent/sessions/permission-forwarding",
78
- globalLogsDir: "/test/agent/extensions/pi-permission-system/logs",
79
- piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
80
- config: { debugLog: false, permissionReviewLog: true, yoloMode: false },
81
72
  runtimeContext: null,
82
73
  permissionManager: {
83
74
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("allow")),
84
- } as unknown as ExtensionRuntime["permissionManager"],
75
+ } as unknown as SessionState["permissionManager"],
85
76
  activeSkillEntries: [],
86
77
  lastKnownActiveAgentName: null,
87
78
  lastActiveToolsCacheKey: null,
88
79
  lastPromptStateCacheKey: null,
89
- lastConfigWarning: null,
90
80
  sessionRules: {
91
81
  approve: vi.fn(),
92
82
  getRuleset: vi.fn().mockReturnValue([]),
93
83
  clear: vi.fn(),
94
- } as unknown as ExtensionRuntime["sessionRules"],
95
- permissionForwardingContext: null,
96
- permissionForwardingTimer: null,
97
- isProcessingForwardedRequests: false,
98
- writeDebugLog: vi.fn(),
99
- writeReviewLog: vi.fn(),
84
+ } as unknown as SessionState["sessionRules"],
100
85
  ...overrides,
101
- } as ExtensionRuntime;
86
+ };
102
87
  }
103
88
 
104
89
  function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
105
90
  return {
106
- runtime: makeRuntime(),
91
+ session: makeSession(),
92
+ writeDebugLog: vi.fn(),
93
+ writeReviewLog: vi.fn(),
94
+ piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
95
+ getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
107
96
  events: makeEvents(),
108
97
  createPermissionManagerForCwd: vi.fn(),
109
98
  refreshExtensionConfig: vi.fn(),
@@ -137,7 +126,7 @@ function getDecisionEvents(deps: HandlerDeps): PermissionDecisionEvent[] {
137
126
  describe("handleToolCall decision events — policy_allow", () => {
138
127
  it("emits allow with policy_allow when checkPermission returns allow", async () => {
139
128
  const deps = makeDeps({
140
- runtime: makeRuntime({
129
+ session: makeSession({
141
130
  permissionManager: {
142
131
  checkPermission: vi.fn().mockReturnValue(
143
132
  makeCheckResult("allow", {
@@ -145,7 +134,7 @@ describe("handleToolCall decision events — policy_allow", () => {
145
134
  matchedPattern: "*",
146
135
  }),
147
136
  ),
148
- } as unknown as ExtensionRuntime["permissionManager"],
137
+ } as unknown as SessionState["permissionManager"],
149
138
  }),
150
139
  });
151
140
 
@@ -168,7 +157,7 @@ describe("handleToolCall decision events — policy_allow", () => {
168
157
  describe("handleToolCall decision events — policy_deny", () => {
169
158
  it("emits deny with policy_deny when checkPermission returns deny", async () => {
170
159
  const deps = makeDeps({
171
- runtime: makeRuntime({
160
+ session: makeSession({
172
161
  permissionManager: {
173
162
  checkPermission: vi.fn().mockReturnValue(
174
163
  makeCheckResult("deny", {
@@ -176,7 +165,7 @@ describe("handleToolCall decision events — policy_deny", () => {
176
165
  matchedPattern: "read",
177
166
  }),
178
167
  ),
179
- } as unknown as ExtensionRuntime["permissionManager"],
168
+ } as unknown as SessionState["permissionManager"],
180
169
  }),
181
170
  });
182
171
 
@@ -197,7 +186,7 @@ describe("handleToolCall decision events — policy_deny", () => {
197
186
  describe("handleToolCall decision events — session_approved", () => {
198
187
  it("emits allow with session_approved when checkPermission returns source:session", async () => {
199
188
  const deps = makeDeps({
200
- runtime: makeRuntime({
189
+ session: makeSession({
201
190
  permissionManager: {
202
191
  checkPermission: vi.fn().mockReturnValue(
203
192
  makeCheckResult("allow", {
@@ -205,7 +194,7 @@ describe("handleToolCall decision events — session_approved", () => {
205
194
  matchedPattern: "git *",
206
195
  }),
207
196
  ),
208
- } as unknown as ExtensionRuntime["permissionManager"],
197
+ } as unknown as SessionState["permissionManager"],
209
198
  }),
210
199
  });
211
200
 
@@ -230,10 +219,10 @@ describe("handleToolCall decision events — session_approved", () => {
230
219
  describe("handleToolCall decision events — user_approved", () => {
231
220
  it("emits allow with user_approved when state=ask and user approves once", async () => {
232
221
  const deps = makeDeps({
233
- runtime: makeRuntime({
222
+ session: makeSession({
234
223
  permissionManager: {
235
224
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
236
- } as unknown as ExtensionRuntime["permissionManager"],
225
+ } as unknown as SessionState["permissionManager"],
237
226
  }),
238
227
  promptPermission: vi
239
228
  .fn()
@@ -252,10 +241,10 @@ describe("handleToolCall decision events — user_approved", () => {
252
241
 
253
242
  it("emits allow with user_approved_for_session when user approves for session", async () => {
254
243
  const deps = makeDeps({
255
- runtime: makeRuntime({
244
+ session: makeSession({
256
245
  permissionManager: {
257
246
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
258
- } as unknown as ExtensionRuntime["permissionManager"],
247
+ } as unknown as SessionState["permissionManager"],
259
248
  }),
260
249
  promptPermission: vi
261
250
  .fn()
@@ -278,10 +267,10 @@ describe("handleToolCall decision events — user_approved", () => {
278
267
  describe("handleToolCall decision events — user_denied", () => {
279
268
  it("emits deny with user_denied when state=ask and user denies", async () => {
280
269
  const deps = makeDeps({
281
- runtime: makeRuntime({
270
+ session: makeSession({
282
271
  permissionManager: {
283
272
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
284
- } as unknown as ExtensionRuntime["permissionManager"],
273
+ } as unknown as SessionState["permissionManager"],
285
274
  }),
286
275
  promptPermission: vi
287
276
  .fn()
@@ -304,10 +293,10 @@ describe("handleToolCall decision events — user_denied", () => {
304
293
  describe("handleToolCall decision events — confirmation_unavailable", () => {
305
294
  it("emits deny with confirmation_unavailable when state=ask but no UI", async () => {
306
295
  const deps = makeDeps({
307
- runtime: makeRuntime({
296
+ session: makeSession({
308
297
  permissionManager: {
309
298
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
310
- } as unknown as ExtensionRuntime["permissionManager"],
299
+ } as unknown as SessionState["permissionManager"],
311
300
  }),
312
301
  canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
313
302
  });
@@ -333,11 +322,11 @@ describe("handleToolCall decision events — infrastructure_auto_allowed", () =>
333
322
  it("emits allow with infrastructure_auto_allowed for Pi infra reads", async () => {
334
323
  const infraDir = "/test/agent";
335
324
  const deps = makeDeps({
336
- runtime: makeRuntime({
337
- piInfrastructureDirs: [infraDir],
325
+ piInfrastructureDirs: [infraDir],
326
+ session: makeSession({
338
327
  permissionManager: {
339
328
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("allow")),
340
- } as unknown as ExtensionRuntime["permissionManager"],
329
+ } as unknown as SessionState["permissionManager"],
341
330
  }),
342
331
  });
343
332
 
@@ -364,10 +353,10 @@ describe("handleToolCall decision events — infrastructure_auto_allowed", () =>
364
353
  describe("handleToolCall decision events — auto_approved", () => {
365
354
  it("emits allow with auto_approved when promptPermission returns autoApproved:true", async () => {
366
355
  const deps = makeDeps({
367
- runtime: makeRuntime({
356
+ session: makeSession({
368
357
  permissionManager: {
369
358
  checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
370
- } as unknown as ExtensionRuntime["permissionManager"],
359
+ } as unknown as SessionState["permissionManager"],
371
360
  }),
372
361
  // Simulate what PermissionPrompter returns in yolo mode
373
362
  promptPermission: vi.fn().mockResolvedValue({