@gotgenes/pi-permission-system 5.8.0 → 5.10.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.
@@ -0,0 +1,546 @@
1
+ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
2
+ import { beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+ // ── Module mocks (hoisted) ─────────────────────────────────────────────────
5
+
6
+ const {
7
+ mockGetActiveAgentName,
8
+ mockGetActiveAgentNameFromSystemPrompt,
9
+ mockCreatePermissionManagerForCwd,
10
+ } = vi.hoisted(() => ({
11
+ mockGetActiveAgentName: vi.fn<(ctx: ExtensionContext) => string | null>(),
12
+ mockGetActiveAgentNameFromSystemPrompt:
13
+ vi.fn<(systemPrompt?: string) => string | null>(),
14
+ mockCreatePermissionManagerForCwd: vi.fn(),
15
+ }));
16
+
17
+ vi.mock("../src/active-agent", () => ({
18
+ getActiveAgentName: mockGetActiveAgentName,
19
+ getActiveAgentNameFromSystemPrompt: mockGetActiveAgentNameFromSystemPrompt,
20
+ }));
21
+
22
+ vi.mock("../src/runtime", async (importOriginal) => {
23
+ const original = await importOriginal<typeof import("../src/runtime")>();
24
+ return {
25
+ ...original,
26
+ createPermissionManagerForCwd: mockCreatePermissionManagerForCwd,
27
+ };
28
+ });
29
+
30
+ // ── Test helpers ───────────────────────────────────────────────────────────
31
+
32
+ import type { ExtensionPaths } from "../src/extension-paths";
33
+ import type { ForwardingController } from "../src/forwarding-manager";
34
+ import type { PermissionManager } from "../src/permission-manager";
35
+ import {
36
+ PermissionSession,
37
+ type PermissionSessionRuntimeDeps,
38
+ } from "../src/permission-session";
39
+ import type { SessionLogger } from "../src/session-logger";
40
+ import type { SkillPromptEntry } from "../src/skill-prompt-sanitizer";
41
+ import type { PermissionCheckResult } from "../src/types";
42
+
43
+ function makeSkillEntry(
44
+ name: string,
45
+ overrides: Partial<SkillPromptEntry> = {},
46
+ ): SkillPromptEntry {
47
+ return {
48
+ name,
49
+ description: `${name} skill`,
50
+ location: `/${name}/SKILL.md`,
51
+ state: "allow",
52
+ normalizedLocation: `/${name}/SKILL.md`,
53
+ normalizedBaseDir: `/${name}`,
54
+ ...overrides,
55
+ };
56
+ }
57
+
58
+ function makePaths(overrides: Partial<ExtensionPaths> = {}): ExtensionPaths {
59
+ return {
60
+ agentDir: "/test/agent",
61
+ sessionsDir: "/test/agent/sessions",
62
+ subagentSessionsDir: "/test/agent/subagent-sessions",
63
+ forwardingDir: "/test/agent/sessions/permission-forwarding",
64
+ globalLogsDir: "/test/agent/logs",
65
+ piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
66
+ ...overrides,
67
+ };
68
+ }
69
+
70
+ function makeLogger(): SessionLogger {
71
+ return {
72
+ debug: vi.fn(),
73
+ review: vi.fn(),
74
+ warn: vi.fn(),
75
+ };
76
+ }
77
+
78
+ function makeRuntimeDeps(): PermissionSessionRuntimeDeps {
79
+ return {
80
+ refreshExtensionConfig: vi.fn(),
81
+ logResolvedConfigPaths: vi.fn(),
82
+ getConfig: vi.fn().mockReturnValue({}),
83
+ };
84
+ }
85
+
86
+ function makeForwarding(): ForwardingController {
87
+ return {
88
+ start: vi.fn(),
89
+ stop: vi.fn(),
90
+ };
91
+ }
92
+
93
+ function makeCtx(overrides: Partial<ExtensionContext> = {}): ExtensionContext {
94
+ return {
95
+ cwd: "/test/project",
96
+ hasUI: true,
97
+ ui: {
98
+ setStatus: vi.fn(),
99
+ notify: vi.fn(),
100
+ select: vi.fn(),
101
+ input: vi.fn(),
102
+ },
103
+ sessionManager: {
104
+ getEntries: vi.fn().mockReturnValue([]),
105
+ getSessionDir: vi.fn().mockReturnValue("/sessions/test"),
106
+ addEntry: vi.fn(),
107
+ },
108
+ ...overrides,
109
+ } as unknown as ExtensionContext;
110
+ }
111
+
112
+ function makePermissionManager(
113
+ overrides: Partial<PermissionManager> = {},
114
+ ): PermissionManager {
115
+ return {
116
+ checkPermission: vi.fn().mockReturnValue({
117
+ state: "allow",
118
+ toolName: "read",
119
+ source: "tool",
120
+ origin: "builtin",
121
+ } as PermissionCheckResult),
122
+ getToolPermission: vi.fn().mockReturnValue("allow"),
123
+ getConfigIssues: vi.fn().mockReturnValue([]),
124
+ getPolicyCacheStamp: vi.fn().mockReturnValue("stamp-1"),
125
+ getComposedConfigRules: vi.fn().mockReturnValue([]),
126
+ getResolvedPolicyPaths: vi.fn().mockReturnValue({}),
127
+ ...overrides,
128
+ } as unknown as PermissionManager;
129
+ }
130
+
131
+ function createSession(overrides?: {
132
+ paths?: Partial<ExtensionPaths>;
133
+ logger?: SessionLogger;
134
+ forwarding?: ForwardingController;
135
+ runtimeDeps?: PermissionSessionRuntimeDeps;
136
+ }): {
137
+ session: PermissionSession;
138
+ paths: ExtensionPaths;
139
+ logger: SessionLogger;
140
+ forwarding: ForwardingController;
141
+ runtimeDeps: PermissionSessionRuntimeDeps;
142
+ } {
143
+ const paths = makePaths(overrides?.paths);
144
+ const logger = overrides?.logger ?? makeLogger();
145
+ const forwarding = overrides?.forwarding ?? makeForwarding();
146
+ const runtimeDeps = overrides?.runtimeDeps ?? makeRuntimeDeps();
147
+ const session = new PermissionSession(paths, logger, forwarding, runtimeDeps);
148
+ return { session, paths, logger, forwarding, runtimeDeps };
149
+ }
150
+
151
+ // ── Tests ──────────────────────────────────────────────────────────────────
152
+
153
+ beforeEach(() => {
154
+ mockGetActiveAgentName.mockReset();
155
+ mockGetActiveAgentNameFromSystemPrompt.mockReset();
156
+ mockCreatePermissionManagerForCwd.mockReset();
157
+
158
+ // Default: createPermissionManagerForCwd returns a fresh mock PM
159
+ mockCreatePermissionManagerForCwd.mockReturnValue(makePermissionManager());
160
+ mockGetActiveAgentName.mockReturnValue(null);
161
+ mockGetActiveAgentNameFromSystemPrompt.mockReturnValue(null);
162
+ });
163
+
164
+ describe("PermissionSession", () => {
165
+ describe("constructor and delegation", () => {
166
+ it("delegates checkPermission to internal PermissionManager", () => {
167
+ const pm = makePermissionManager();
168
+ mockCreatePermissionManagerForCwd.mockReturnValue(pm);
169
+ const { session } = createSession();
170
+
171
+ const result = session.checkPermission("bash", { command: "ls" });
172
+
173
+ expect(pm.checkPermission).toHaveBeenCalledWith(
174
+ "bash",
175
+ { command: "ls" },
176
+ undefined,
177
+ undefined,
178
+ );
179
+ expect(result.state).toBe("allow");
180
+ });
181
+
182
+ it("delegates getToolPermission to internal PermissionManager", () => {
183
+ const pm = makePermissionManager();
184
+ mockCreatePermissionManagerForCwd.mockReturnValue(pm);
185
+ const { session } = createSession();
186
+
187
+ const result = session.getToolPermission("read");
188
+
189
+ expect(pm.getToolPermission).toHaveBeenCalledWith("read", undefined);
190
+ expect(result).toBe("allow");
191
+ });
192
+
193
+ it("delegates getConfigIssues to internal PermissionManager", () => {
194
+ const pm = makePermissionManager({
195
+ getConfigIssues: vi.fn().mockReturnValue(["issue1"]),
196
+ });
197
+ mockCreatePermissionManagerForCwd.mockReturnValue(pm);
198
+ const { session } = createSession();
199
+
200
+ expect(session.getConfigIssues("agent1")).toEqual(["issue1"]);
201
+ expect(pm.getConfigIssues).toHaveBeenCalledWith("agent1");
202
+ });
203
+
204
+ it("delegates getPolicyCacheStamp to internal PermissionManager", () => {
205
+ const pm = makePermissionManager();
206
+ mockCreatePermissionManagerForCwd.mockReturnValue(pm);
207
+ const { session } = createSession();
208
+
209
+ expect(session.getPolicyCacheStamp("agent1")).toBe("stamp-1");
210
+ expect(pm.getPolicyCacheStamp).toHaveBeenCalledWith("agent1");
211
+ });
212
+
213
+ it("delegates getSessionRuleset to internal SessionRules", () => {
214
+ const { session } = createSession();
215
+ const rules = session.getSessionRuleset();
216
+ expect(rules).toEqual([]);
217
+ });
218
+
219
+ it("delegates approveSessionRule to internal SessionRules", () => {
220
+ const { session } = createSession();
221
+ session.approveSessionRule("bash", "/usr/bin/*");
222
+ const rules = session.getSessionRuleset();
223
+ expect(rules).toHaveLength(1);
224
+ expect(rules[0]).toMatchObject({
225
+ surface: "bash",
226
+ pattern: "/usr/bin/*",
227
+ action: "allow",
228
+ });
229
+ });
230
+ });
231
+
232
+ describe("activate and deactivate", () => {
233
+ it("stores the context on activate", () => {
234
+ const { session, forwarding } = createSession();
235
+ const ctx = makeCtx();
236
+
237
+ session.activate(ctx);
238
+
239
+ expect(forwarding.start).toHaveBeenCalledWith(ctx);
240
+ });
241
+
242
+ it("clears context on deactivate", () => {
243
+ const { session, forwarding } = createSession();
244
+ session.activate(makeCtx());
245
+ session.deactivate();
246
+
247
+ expect(forwarding.stop).toHaveBeenCalled();
248
+ });
249
+ });
250
+
251
+ describe("resetForNewSession", () => {
252
+ it("creates a new PermissionManager for the context cwd", () => {
253
+ const pm2 = makePermissionManager({
254
+ checkPermission: vi.fn().mockReturnValue({
255
+ state: "deny",
256
+ toolName: "bash",
257
+ source: "bash",
258
+ origin: "global",
259
+ } as PermissionCheckResult),
260
+ });
261
+ mockCreatePermissionManagerForCwd.mockReturnValue(pm2);
262
+ const { session } = createSession();
263
+ const ctx = makeCtx({ cwd: "/new/project" });
264
+
265
+ session.resetForNewSession(ctx);
266
+
267
+ expect(mockCreatePermissionManagerForCwd).toHaveBeenCalledWith(
268
+ "/test/agent",
269
+ "/new/project",
270
+ );
271
+ // Verify the new PM is used for subsequent calls
272
+ const result = session.checkPermission("bash", { command: "rm" });
273
+ expect(result.state).toBe("deny");
274
+ });
275
+
276
+ it("clears cache keys", () => {
277
+ const { session } = createSession();
278
+ session.commitActiveToolsCacheKey("key-1");
279
+ session.commitPromptStateCacheKey("key-2");
280
+ expect(session.shouldUpdateActiveTools("key-1")).toBe(false);
281
+ expect(session.shouldUpdatePromptState("key-2")).toBe(false);
282
+
283
+ session.resetForNewSession(makeCtx());
284
+
285
+ // After reset, same keys should be treated as new
286
+ expect(session.shouldUpdateActiveTools("key-1")).toBe(true);
287
+ expect(session.shouldUpdatePromptState("key-2")).toBe(true);
288
+ });
289
+
290
+ it("clears skill entries", () => {
291
+ const { session } = createSession();
292
+ session.setActiveSkillEntries([makeSkillEntry("test")]);
293
+ expect(session.getActiveSkillEntries()).toHaveLength(1);
294
+
295
+ session.resetForNewSession(makeCtx());
296
+
297
+ expect(session.getActiveSkillEntries()).toEqual([]);
298
+ });
299
+
300
+ it("starts forwarding with the new context", () => {
301
+ const { session, forwarding } = createSession();
302
+ const ctx = makeCtx();
303
+
304
+ session.resetForNewSession(ctx);
305
+
306
+ expect(forwarding.start).toHaveBeenCalledWith(ctx);
307
+ });
308
+
309
+ it("activates the new context", () => {
310
+ const { session } = createSession();
311
+ const ctx = makeCtx();
312
+
313
+ session.resetForNewSession(ctx);
314
+
315
+ // Verify context is stored by calling resolveAgentName which needs it
316
+ mockGetActiveAgentName.mockReturnValue("test-agent");
317
+ const name = session.resolveAgentName(ctx);
318
+ expect(name).toBe("test-agent");
319
+ });
320
+ });
321
+
322
+ describe("shutdown", () => {
323
+ it("clears session rules", () => {
324
+ const { session } = createSession();
325
+ session.approveSessionRule("bash", "*");
326
+ expect(session.getSessionRuleset()).toHaveLength(1);
327
+
328
+ session.shutdown();
329
+
330
+ expect(session.getSessionRuleset()).toEqual([]);
331
+ });
332
+
333
+ it("clears cache keys", () => {
334
+ const { session } = createSession();
335
+ session.commitActiveToolsCacheKey("k1");
336
+ session.commitPromptStateCacheKey("k2");
337
+
338
+ session.shutdown();
339
+
340
+ expect(session.shouldUpdateActiveTools("k1")).toBe(true);
341
+ expect(session.shouldUpdatePromptState("k2")).toBe(true);
342
+ });
343
+
344
+ it("clears skill entries", () => {
345
+ const { session } = createSession();
346
+ session.setActiveSkillEntries([makeSkillEntry("s")]);
347
+
348
+ session.shutdown();
349
+
350
+ expect(session.getActiveSkillEntries()).toEqual([]);
351
+ });
352
+
353
+ it("stops forwarding and deactivates context", () => {
354
+ const { session, forwarding } = createSession();
355
+ session.activate(makeCtx());
356
+
357
+ session.shutdown();
358
+
359
+ expect(forwarding.stop).toHaveBeenCalled();
360
+ });
361
+ });
362
+
363
+ describe("cache key methods", () => {
364
+ it("shouldUpdateActiveTools returns true for new key", () => {
365
+ const { session } = createSession();
366
+ expect(session.shouldUpdateActiveTools("key-1")).toBe(true);
367
+ });
368
+
369
+ it("shouldUpdateActiveTools returns false for committed key", () => {
370
+ const { session } = createSession();
371
+ session.commitActiveToolsCacheKey("key-1");
372
+ expect(session.shouldUpdateActiveTools("key-1")).toBe(false);
373
+ });
374
+
375
+ it("shouldUpdateActiveTools returns true for different key", () => {
376
+ const { session } = createSession();
377
+ session.commitActiveToolsCacheKey("key-1");
378
+ expect(session.shouldUpdateActiveTools("key-2")).toBe(true);
379
+ });
380
+
381
+ it("shouldUpdatePromptState returns true for new key", () => {
382
+ const { session } = createSession();
383
+ expect(session.shouldUpdatePromptState("key-1")).toBe(true);
384
+ });
385
+
386
+ it("shouldUpdatePromptState returns false for committed key", () => {
387
+ const { session } = createSession();
388
+ session.commitPromptStateCacheKey("key-1");
389
+ expect(session.shouldUpdatePromptState("key-1")).toBe(false);
390
+ });
391
+ });
392
+
393
+ describe("skill entries", () => {
394
+ it("get/set skill entries", () => {
395
+ const { session } = createSession();
396
+ const entries = [makeSkillEntry("a"), makeSkillEntry("b")];
397
+ session.setActiveSkillEntries(entries);
398
+ expect(session.getActiveSkillEntries()).toEqual(entries);
399
+ });
400
+ });
401
+
402
+ describe("resolveAgentName", () => {
403
+ it("returns name from session context", () => {
404
+ mockGetActiveAgentName.mockReturnValue("ctx-agent");
405
+ const { session } = createSession();
406
+ const ctx = makeCtx();
407
+
408
+ expect(session.resolveAgentName(ctx)).toBe("ctx-agent");
409
+ });
410
+
411
+ it("falls back to system prompt", () => {
412
+ mockGetActiveAgentName.mockReturnValue(null);
413
+ mockGetActiveAgentNameFromSystemPrompt.mockReturnValue("prompt-agent");
414
+ const { session } = createSession();
415
+ const ctx = makeCtx();
416
+
417
+ expect(session.resolveAgentName(ctx, "system prompt")).toBe(
418
+ "prompt-agent",
419
+ );
420
+ });
421
+
422
+ it("falls back to last known name", () => {
423
+ const { session } = createSession();
424
+ const ctx = makeCtx();
425
+
426
+ // First call sets name
427
+ mockGetActiveAgentName.mockReturnValue("first-agent");
428
+ session.resolveAgentName(ctx);
429
+
430
+ // Second call with no name resolves to last known
431
+ mockGetActiveAgentName.mockReturnValue(null);
432
+ mockGetActiveAgentNameFromSystemPrompt.mockReturnValue(null);
433
+ expect(session.resolveAgentName(ctx)).toBe("first-agent");
434
+ });
435
+
436
+ it("exposes lastKnownActiveAgentName", () => {
437
+ const { session } = createSession();
438
+ expect(session.lastKnownActiveAgentName).toBeNull();
439
+
440
+ mockGetActiveAgentName.mockReturnValue("named");
441
+ session.resolveAgentName(makeCtx());
442
+ expect(session.lastKnownActiveAgentName).toBe("named");
443
+ });
444
+ });
445
+
446
+ describe("infrastructure paths", () => {
447
+ it("getInfrastructureDirs returns paths from ExtensionPaths", () => {
448
+ const { session } = createSession();
449
+ expect(session.getInfrastructureDirs()).toEqual([
450
+ "/test/agent",
451
+ "/test/agent/git",
452
+ ]);
453
+ });
454
+
455
+ it("getInfrastructureReadPaths returns config paths", () => {
456
+ const runtimeDeps = makeRuntimeDeps();
457
+ (runtimeDeps.getConfig as ReturnType<typeof vi.fn>).mockReturnValue({
458
+ piInfrastructureReadPaths: ["/extra/path"],
459
+ });
460
+ const { session } = createSession({ runtimeDeps });
461
+ expect(session.getInfrastructureReadPaths()).toEqual(["/extra/path"]);
462
+ });
463
+
464
+ it("getInfrastructureReadPaths returns empty when config omits the field", () => {
465
+ const { session } = createSession();
466
+ expect(session.getInfrastructureReadPaths()).toEqual([]);
467
+ });
468
+ });
469
+
470
+ describe("config delegation", () => {
471
+ it("refreshConfig delegates to runtimeDeps", () => {
472
+ const { session, runtimeDeps } = createSession();
473
+ const ctx = makeCtx();
474
+ session.refreshConfig(ctx);
475
+ expect(runtimeDeps.refreshExtensionConfig).toHaveBeenCalledWith(ctx);
476
+ });
477
+
478
+ it("logResolvedConfigPaths delegates to runtimeDeps", () => {
479
+ const { session, runtimeDeps } = createSession();
480
+ session.logResolvedConfigPaths();
481
+ expect(runtimeDeps.logResolvedConfigPaths).toHaveBeenCalled();
482
+ });
483
+
484
+ it("config getter delegates to runtimeDeps.getConfig", () => {
485
+ const runtimeDeps = makeRuntimeDeps();
486
+ const fakeConfig = { debugLog: true };
487
+ (runtimeDeps.getConfig as ReturnType<typeof vi.fn>).mockReturnValue(
488
+ fakeConfig,
489
+ );
490
+ const { session } = createSession({ runtimeDeps });
491
+ expect(session.config).toBe(fakeConfig);
492
+ });
493
+ });
494
+
495
+ describe("reload", () => {
496
+ it("recreates PermissionManager for current context cwd", () => {
497
+ const { session } = createSession();
498
+ const ctx = makeCtx({ cwd: "/project" });
499
+ session.activate(ctx);
500
+
501
+ const pm2 = makePermissionManager();
502
+ mockCreatePermissionManagerForCwd.mockReturnValue(pm2);
503
+
504
+ session.reload();
505
+
506
+ expect(mockCreatePermissionManagerForCwd).toHaveBeenCalledWith(
507
+ "/test/agent",
508
+ "/project",
509
+ );
510
+ });
511
+
512
+ it("clears caches and skill entries", () => {
513
+ const { session } = createSession();
514
+ session.commitActiveToolsCacheKey("k1");
515
+ session.commitPromptStateCacheKey("k2");
516
+ session.setActiveSkillEntries([makeSkillEntry("s")]);
517
+
518
+ session.reload();
519
+
520
+ expect(session.shouldUpdateActiveTools("k1")).toBe(true);
521
+ expect(session.shouldUpdatePromptState("k2")).toBe(true);
522
+ expect(session.getActiveSkillEntries()).toEqual([]);
523
+ });
524
+ });
525
+
526
+ describe("getRuntimeContext", () => {
527
+ it("returns null before activation", () => {
528
+ const { session } = createSession();
529
+ expect(session.getRuntimeContext()).toBeNull();
530
+ });
531
+
532
+ it("returns context after activation", () => {
533
+ const { session } = createSession();
534
+ const ctx = makeCtx();
535
+ session.activate(ctx);
536
+ expect(session.getRuntimeContext()).toBe(ctx);
537
+ });
538
+
539
+ it("returns null after deactivation", () => {
540
+ const { session } = createSession();
541
+ session.activate(makeCtx());
542
+ session.deactivate();
543
+ expect(session.getRuntimeContext()).toBeNull();
544
+ });
545
+ });
546
+ });
@@ -8,8 +8,6 @@ const {
8
8
  mockCreateLogger,
9
9
  mockLoadAndMergeConfigs,
10
10
  mockSyncPermissionSystemStatus,
11
- mockGetActiveAgentName,
12
- mockGetActiveAgentNameFromSystemPrompt,
13
11
  mockBuildResolvedConfigLogEntry,
14
12
  mockDiscoverGlobalNodeModulesRoot,
15
13
  } = vi.hoisted(() => ({
@@ -24,9 +22,6 @@ const {
24
22
  mockCreateLogger: vi.fn(),
25
23
  mockLoadAndMergeConfigs: vi.fn(),
26
24
  mockSyncPermissionSystemStatus: vi.fn(),
27
- mockGetActiveAgentName: vi.fn<() => string | null>(),
28
- mockGetActiveAgentNameFromSystemPrompt:
29
- vi.fn<(prompt?: string) => string | null>(),
30
25
  mockBuildResolvedConfigLogEntry: vi.fn(),
31
26
  mockDiscoverGlobalNodeModulesRoot: vi.fn<() => string | null>(),
32
27
  }));
@@ -50,11 +45,6 @@ vi.mock("../src/status", () => ({
50
45
  getPermissionSystemStatus: vi.fn(),
51
46
  }));
52
47
 
53
- vi.mock("../src/active-agent", () => ({
54
- getActiveAgentName: mockGetActiveAgentName,
55
- getActiveAgentNameFromSystemPrompt: mockGetActiveAgentNameFromSystemPrompt,
56
- }));
57
-
58
48
  vi.mock("../src/config-reporter", () => ({
59
49
  buildResolvedConfigLogEntry: mockBuildResolvedConfigLogEntry,
60
50
  }));
@@ -89,7 +79,6 @@ import {
89
79
  createPermissionManagerForCwd,
90
80
  derivePiProjectPaths,
91
81
  refreshExtensionConfig,
92
- resolveAgentName,
93
82
  } from "../src/runtime";
94
83
 
95
84
  // ── test suite ─────────────────────────────────────────────────────────────
@@ -212,21 +201,6 @@ describe("createExtensionRuntime", () => {
212
201
  expect(runtime.lastConfigWarning).toBeNull();
213
202
  });
214
203
 
215
- it("initializes permissionForwardingContext to null", () => {
216
- const runtime = createExtensionRuntime({ agentDir: "/test/agent" });
217
- expect(runtime.permissionForwardingContext).toBeNull();
218
- });
219
-
220
- it("initializes permissionForwardingTimer to null", () => {
221
- const runtime = createExtensionRuntime({ agentDir: "/test/agent" });
222
- expect(runtime.permissionForwardingTimer).toBeNull();
223
- });
224
-
225
- it("initializes isProcessingForwardedRequests to false", () => {
226
- const runtime = createExtensionRuntime({ agentDir: "/test/agent" });
227
- expect(runtime.isProcessingForwardedRequests).toBe(false);
228
- });
229
-
230
204
  it("creates a sessionRules instance", () => {
231
205
  const runtime = createExtensionRuntime({ agentDir: "/test/agent" });
232
206
  expect(runtime.sessionRules).toBeDefined();
@@ -593,69 +567,5 @@ describe("refreshExtensionConfig", () => {
593
567
  });
594
568
  });
595
569
 
596
- // ── resolveAgentName ──────────────────────────────────────────────────────
597
-
598
- describe("resolveAgentName", () => {
599
- function makeRuntime() {
600
- mockCreateLogger.mockReturnValue({
601
- debug: mockLoggerDebug,
602
- review: mockLoggerReview,
603
- });
604
- return createExtensionRuntime({ agentDir: "/test/agent" });
605
- }
606
-
607
- function makeCtx(): ExtensionContext {
608
- return {
609
- cwd: "/test/project",
610
- hasUI: false,
611
- ui: {},
612
- sessionManager: { getEntries: vi.fn(), addEntry: vi.fn() },
613
- } as unknown as ExtensionContext;
614
- }
615
-
616
- beforeEach(() => {
617
- mockLoggerDebug.mockReset().mockReturnValue(undefined);
618
- mockGetActiveAgentName.mockReset().mockReturnValue(null);
619
- mockGetActiveAgentNameFromSystemPrompt.mockReset().mockReturnValue(null);
620
- });
621
-
622
- it("returns and stores name from getActiveAgentName when available", () => {
623
- const runtime = makeRuntime();
624
- mockGetActiveAgentName.mockReturnValue("session-agent");
625
- const result = resolveAgentName(runtime, makeCtx());
626
- expect(result).toBe("session-agent");
627
- expect(runtime.lastKnownActiveAgentName).toBe("session-agent");
628
- });
629
-
630
- it("falls back to getActiveAgentNameFromSystemPrompt when session name is null", () => {
631
- const runtime = makeRuntime();
632
- mockGetActiveAgentName.mockReturnValue(null);
633
- mockGetActiveAgentNameFromSystemPrompt.mockReturnValue("prompt-agent");
634
- const result = resolveAgentName(runtime, makeCtx(), "system prompt text");
635
- expect(result).toBe("prompt-agent");
636
- expect(runtime.lastKnownActiveAgentName).toBe("prompt-agent");
637
- });
638
-
639
- it("falls back to lastKnownActiveAgentName when both sources return null", () => {
640
- const runtime = makeRuntime();
641
- runtime.lastKnownActiveAgentName = "remembered-agent";
642
- mockGetActiveAgentName.mockReturnValue(null);
643
- mockGetActiveAgentNameFromSystemPrompt.mockReturnValue(null);
644
- const result = resolveAgentName(runtime, makeCtx());
645
- expect(result).toBe("remembered-agent");
646
- });
647
-
648
- it("returns null when all sources are null and no prior name", () => {
649
- const runtime = makeRuntime();
650
- const result = resolveAgentName(runtime, makeCtx());
651
- expect(result).toBeNull();
652
- });
653
-
654
- it("does not update lastKnownActiveAgentName when falling back to stored value", () => {
655
- const runtime = makeRuntime();
656
- runtime.lastKnownActiveAgentName = "remembered-agent";
657
- resolveAgentName(runtime, makeCtx());
658
- // Value unchanged — not overwritten with null
659
- expect(runtime.lastKnownActiveAgentName).toBe("remembered-agent");
660
- });
661
- });
570
+ // resolveAgentName was moved to PermissionSession (#129)
571
+ // Tests live in tests/permission-session.test.ts