@gotgenes/pi-permission-system 5.10.0 → 5.11.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 +15 -0
- package/package.json +1 -1
- package/src/forwarding-manager.ts +1 -1
- package/src/handlers/before-agent-start.ts +73 -60
- package/src/handlers/gates/descriptor.ts +1 -1
- package/src/handlers/index.ts +6 -15
- package/src/handlers/lifecycle.ts +54 -42
- package/src/handlers/permission-gate-handler.ts +346 -0
- package/src/index.ts +33 -38
- package/src/permission-prompter.ts +23 -6
- package/src/permission-session.ts +29 -0
- package/src/session-logger.ts +1 -1
- package/src/tool-registry.ts +6 -0
- package/tests/handlers/before-agent-start.test.ts +70 -90
- package/tests/handlers/input-events.test.ts +70 -63
- package/tests/handlers/input.test.ts +85 -83
- package/tests/handlers/lifecycle.test.ts +60 -72
- package/tests/handlers/tool-call-events.test.ts +128 -122
- package/tests/handlers/tool-call.test.ts +84 -58
- package/tests/permission-prompter.test.ts +1 -1
- package/tests/permission-session.test.ts +61 -0
- package/src/handlers/input.ts +0 -126
- package/src/handlers/tool-call.ts +0 -203
- package/src/handlers/types.ts +0 -63
|
@@ -3,10 +3,10 @@ import { describe, expect, it, vi } from "vitest";
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
extractSkillNameFromInput,
|
|
6
|
-
|
|
7
|
-
} from "../../src/handlers/
|
|
8
|
-
import type { HandlerDeps } from "../../src/handlers/types";
|
|
6
|
+
PermissionGateHandler,
|
|
7
|
+
} from "../../src/handlers/permission-gate-handler";
|
|
9
8
|
import type { PermissionSession } from "../../src/permission-session";
|
|
9
|
+
import type { ToolRegistry } from "../../src/tool-registry";
|
|
10
10
|
import type { PermissionState } from "../../src/types";
|
|
11
11
|
|
|
12
12
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
@@ -45,26 +45,40 @@ function makeSession(
|
|
|
45
45
|
getToolPermission: vi.fn().mockReturnValue("allow" as PermissionState),
|
|
46
46
|
getSessionRuleset: vi.fn().mockReturnValue([]),
|
|
47
47
|
approveSessionRule: vi.fn(),
|
|
48
|
+
canPrompt: vi.fn().mockReturnValue(true),
|
|
49
|
+
prompt: vi.fn().mockResolvedValue({ approved: true, state: "approved" }),
|
|
50
|
+
createPermissionRequestId: vi.fn().mockReturnValue("req-id"),
|
|
48
51
|
...overrides,
|
|
49
52
|
} as unknown as PermissionSession;
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
function
|
|
55
|
+
function makeEvents() {
|
|
53
56
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
setActiveTools: vi.fn(),
|
|
64
|
-
...overrides,
|
|
57
|
+
emit: vi.fn(),
|
|
58
|
+
on: vi.fn().mockReturnValue(() => undefined),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function makeToolRegistry(): ToolRegistry {
|
|
63
|
+
return {
|
|
64
|
+
getAll: vi.fn().mockReturnValue([]),
|
|
65
|
+
setActive: vi.fn(),
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
function makeHandler(overrides?: {
|
|
70
|
+
session?: Partial<Record<keyof PermissionSession, unknown>>;
|
|
71
|
+
}): {
|
|
72
|
+
handler: PermissionGateHandler;
|
|
73
|
+
session: PermissionSession;
|
|
74
|
+
} {
|
|
75
|
+
const session = makeSession(overrides?.session);
|
|
76
|
+
const events = makeEvents();
|
|
77
|
+
const toolRegistry = makeToolRegistry();
|
|
78
|
+
const handler = new PermissionGateHandler(session, events, toolRegistry);
|
|
79
|
+
return { handler, session };
|
|
80
|
+
}
|
|
81
|
+
|
|
68
82
|
// ── extractSkillNameFromInput ──────────────────────────────────────────────
|
|
69
83
|
|
|
70
84
|
describe("extractSkillNameFromInput", () => {
|
|
@@ -104,15 +118,14 @@ describe("extractSkillNameFromInput", () => {
|
|
|
104
118
|
describe("handleInput", () => {
|
|
105
119
|
it("activates session with ctx", async () => {
|
|
106
120
|
const ctx = makeCtx();
|
|
107
|
-
const
|
|
108
|
-
await handleInput(
|
|
109
|
-
expect(
|
|
121
|
+
const { handler, session } = makeHandler();
|
|
122
|
+
await handler.handleInput(makeInputEvent("hello"), ctx);
|
|
123
|
+
expect(session.activate).toHaveBeenCalledWith(ctx);
|
|
110
124
|
});
|
|
111
125
|
|
|
112
126
|
it("returns continue for non-skill input", async () => {
|
|
113
|
-
const
|
|
114
|
-
const result = await handleInput(
|
|
115
|
-
deps,
|
|
127
|
+
const { handler } = makeHandler();
|
|
128
|
+
const result = await handler.handleInput(
|
|
116
129
|
makeInputEvent("just a message"),
|
|
117
130
|
makeCtx(),
|
|
118
131
|
);
|
|
@@ -120,15 +133,14 @@ describe("handleInput", () => {
|
|
|
120
133
|
});
|
|
121
134
|
|
|
122
135
|
it("does not check permissions for non-skill input", async () => {
|
|
123
|
-
const
|
|
124
|
-
await handleInput(
|
|
125
|
-
expect(
|
|
136
|
+
const { handler, session } = makeHandler();
|
|
137
|
+
await handler.handleInput(makeInputEvent("just a message"), makeCtx());
|
|
138
|
+
expect(session.checkPermission).not.toHaveBeenCalled();
|
|
126
139
|
});
|
|
127
140
|
|
|
128
141
|
it("returns continue when skill is allowed", async () => {
|
|
129
|
-
const
|
|
130
|
-
const result = await handleInput(
|
|
131
|
-
deps,
|
|
142
|
+
const { handler } = makeHandler();
|
|
143
|
+
const result = await handler.handleInput(
|
|
132
144
|
makeInputEvent("/skill:librarian"),
|
|
133
145
|
makeCtx(),
|
|
134
146
|
);
|
|
@@ -136,12 +148,12 @@ describe("handleInput", () => {
|
|
|
136
148
|
});
|
|
137
149
|
|
|
138
150
|
it("returns handled when skill is denied", async () => {
|
|
139
|
-
const
|
|
140
|
-
|
|
151
|
+
const { handler } = makeHandler({
|
|
152
|
+
session: {
|
|
153
|
+
checkPermission: vi.fn().mockReturnValue({ state: "deny" }),
|
|
154
|
+
},
|
|
141
155
|
});
|
|
142
|
-
const
|
|
143
|
-
const result = await handleInput(
|
|
144
|
-
deps,
|
|
156
|
+
const result = await handler.handleInput(
|
|
145
157
|
makeInputEvent("/skill:librarian"),
|
|
146
158
|
makeCtx(),
|
|
147
159
|
);
|
|
@@ -150,11 +162,12 @@ describe("handleInput", () => {
|
|
|
150
162
|
|
|
151
163
|
it("shows a warning notification when skill is denied and UI is available", async () => {
|
|
152
164
|
const ctx = makeCtx({ hasUI: true });
|
|
153
|
-
const
|
|
154
|
-
|
|
165
|
+
const { handler } = makeHandler({
|
|
166
|
+
session: {
|
|
167
|
+
checkPermission: vi.fn().mockReturnValue({ state: "deny" }),
|
|
168
|
+
},
|
|
155
169
|
});
|
|
156
|
-
|
|
157
|
-
await handleInput(deps, makeInputEvent("/skill:librarian"), ctx);
|
|
170
|
+
await handler.handleInput(makeInputEvent("/skill:librarian"), ctx);
|
|
158
171
|
expect(ctx.ui.notify).toHaveBeenCalledWith(
|
|
159
172
|
expect.stringContaining("librarian"),
|
|
160
173
|
"warning",
|
|
@@ -163,24 +176,23 @@ describe("handleInput", () => {
|
|
|
163
176
|
|
|
164
177
|
it("does not show a warning notification when skill is denied and UI is absent", async () => {
|
|
165
178
|
const ctx = makeCtx({ hasUI: false });
|
|
166
|
-
const
|
|
167
|
-
|
|
179
|
+
const { handler } = makeHandler({
|
|
180
|
+
session: {
|
|
181
|
+
checkPermission: vi.fn().mockReturnValue({ state: "deny" }),
|
|
182
|
+
},
|
|
168
183
|
});
|
|
169
|
-
|
|
170
|
-
await handleInput(deps, makeInputEvent("/skill:librarian"), ctx);
|
|
184
|
+
await handler.handleInput(makeInputEvent("/skill:librarian"), ctx);
|
|
171
185
|
expect(ctx.ui.notify).not.toHaveBeenCalled();
|
|
172
186
|
});
|
|
173
187
|
|
|
174
188
|
it("returns handled when skill requires approval but no UI is available", async () => {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
|
|
189
|
+
const { handler } = makeHandler({
|
|
190
|
+
session: {
|
|
191
|
+
checkPermission: vi.fn().mockReturnValue({ state: "ask" }),
|
|
192
|
+
canPrompt: vi.fn().mockReturnValue(false),
|
|
193
|
+
},
|
|
181
194
|
});
|
|
182
|
-
const result = await handleInput(
|
|
183
|
-
deps,
|
|
195
|
+
const result = await handler.handleInput(
|
|
184
196
|
makeInputEvent("/skill:librarian"),
|
|
185
197
|
makeCtx(),
|
|
186
198
|
);
|
|
@@ -188,38 +200,30 @@ describe("handleInput", () => {
|
|
|
188
200
|
});
|
|
189
201
|
|
|
190
202
|
it("prompts and returns continue when skill ask is approved", async () => {
|
|
191
|
-
const session =
|
|
192
|
-
|
|
203
|
+
const { handler, session } = makeHandler({
|
|
204
|
+
session: {
|
|
205
|
+
checkPermission: vi.fn().mockReturnValue({ state: "ask" }),
|
|
206
|
+
prompt: vi
|
|
207
|
+
.fn()
|
|
208
|
+
.mockResolvedValue({ approved: true, state: "approved" }),
|
|
209
|
+
},
|
|
193
210
|
});
|
|
194
|
-
const
|
|
195
|
-
session,
|
|
196
|
-
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
197
|
-
promptPermission: vi
|
|
198
|
-
.fn()
|
|
199
|
-
.mockResolvedValue({ approved: true, state: "approved" }),
|
|
200
|
-
});
|
|
201
|
-
const result = await handleInput(
|
|
202
|
-
deps,
|
|
211
|
+
const result = await handler.handleInput(
|
|
203
212
|
makeInputEvent("/skill:librarian"),
|
|
204
213
|
makeCtx(),
|
|
205
214
|
);
|
|
206
215
|
expect(result).toEqual({ action: "continue" });
|
|
207
|
-
expect(
|
|
216
|
+
expect(session.prompt).toHaveBeenCalledOnce();
|
|
208
217
|
});
|
|
209
218
|
|
|
210
219
|
it("returns handled when skill ask is denied by user", async () => {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
217
|
-
promptPermission: vi
|
|
218
|
-
.fn()
|
|
219
|
-
.mockResolvedValue({ approved: false, state: "denied" }),
|
|
220
|
+
const { handler } = makeHandler({
|
|
221
|
+
session: {
|
|
222
|
+
checkPermission: vi.fn().mockReturnValue({ state: "ask" }),
|
|
223
|
+
prompt: vi.fn().mockResolvedValue({ approved: false, state: "denied" }),
|
|
224
|
+
},
|
|
220
225
|
});
|
|
221
|
-
const result = await handleInput(
|
|
222
|
-
deps,
|
|
226
|
+
const result = await handler.handleInput(
|
|
223
227
|
makeInputEvent("/skill:librarian"),
|
|
224
228
|
makeCtx(),
|
|
225
229
|
);
|
|
@@ -227,19 +231,17 @@ describe("handleInput", () => {
|
|
|
227
231
|
});
|
|
228
232
|
|
|
229
233
|
it("passes agentName in the prompt permission request", async () => {
|
|
230
|
-
const session =
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.fn()
|
|
239
|
-
.mockResolvedValue({ approved: true, state: "approved" }),
|
|
234
|
+
const { handler, session } = makeHandler({
|
|
235
|
+
session: {
|
|
236
|
+
checkPermission: vi.fn().mockReturnValue({ state: "ask" }),
|
|
237
|
+
resolveAgentName: vi.fn().mockReturnValue("code-agent"),
|
|
238
|
+
prompt: vi
|
|
239
|
+
.fn()
|
|
240
|
+
.mockResolvedValue({ approved: true, state: "approved" }),
|
|
241
|
+
},
|
|
240
242
|
});
|
|
241
|
-
await handleInput(
|
|
242
|
-
expect(
|
|
243
|
+
await handler.handleInput(makeInputEvent("/skill:librarian"), makeCtx());
|
|
244
|
+
expect(session.prompt).toHaveBeenCalledWith(
|
|
243
245
|
expect.anything(),
|
|
244
246
|
expect.objectContaining({
|
|
245
247
|
agentName: "code-agent",
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
handleResourcesDiscover,
|
|
5
|
-
handleSessionShutdown,
|
|
6
|
-
handleSessionStart,
|
|
7
|
-
} from "../../src/handlers/lifecycle";
|
|
8
|
-
import type { HandlerDeps } from "../../src/handlers/types";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { SessionLifecycleHandler } from "../../src/handlers/lifecycle";
|
|
9
4
|
import type { PermissionSession } from "../../src/permission-session";
|
|
10
5
|
|
|
11
6
|
// ── status stub ────────────────────────────────────────────────────────────
|
|
@@ -53,20 +48,17 @@ function makeSession(
|
|
|
53
48
|
} as unknown as PermissionSession;
|
|
54
49
|
}
|
|
55
50
|
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
setActiveTools: vi.fn(),
|
|
68
|
-
...overrides,
|
|
69
|
-
};
|
|
51
|
+
function makeHandler(
|
|
52
|
+
overrides?: Partial<Record<keyof PermissionSession, unknown>>,
|
|
53
|
+
): {
|
|
54
|
+
handler: SessionLifecycleHandler;
|
|
55
|
+
session: PermissionSession;
|
|
56
|
+
cleanupRpc: ReturnType<typeof vi.fn>;
|
|
57
|
+
} {
|
|
58
|
+
const session = makeSession(overrides);
|
|
59
|
+
const cleanupRpc = vi.fn();
|
|
60
|
+
const handler = new SessionLifecycleHandler(session, cleanupRpc);
|
|
61
|
+
return { handler, session, cleanupRpc };
|
|
70
62
|
}
|
|
71
63
|
|
|
72
64
|
// ── handleSessionStart ─────────────────────────────────────────────────────
|
|
@@ -74,52 +66,51 @@ function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
|
|
|
74
66
|
describe("handleSessionStart", () => {
|
|
75
67
|
it("refreshes config with ctx", async () => {
|
|
76
68
|
const ctx = makeCtx();
|
|
77
|
-
const
|
|
78
|
-
await handleSessionStart(
|
|
79
|
-
expect(
|
|
69
|
+
const { handler, session } = makeHandler();
|
|
70
|
+
await handler.handleSessionStart({ reason: "startup" }, ctx);
|
|
71
|
+
expect(session.refreshConfig).toHaveBeenCalledWith(ctx);
|
|
80
72
|
});
|
|
81
73
|
|
|
82
74
|
it("calls resetForNewSession with ctx", async () => {
|
|
83
75
|
const ctx = makeCtx();
|
|
84
|
-
const
|
|
85
|
-
await handleSessionStart(
|
|
86
|
-
expect(
|
|
76
|
+
const { handler, session } = makeHandler();
|
|
77
|
+
await handler.handleSessionStart({ reason: "startup" }, ctx);
|
|
78
|
+
expect(session.resetForNewSession).toHaveBeenCalledWith(ctx);
|
|
87
79
|
});
|
|
88
80
|
|
|
89
81
|
it("logs resolved config paths", async () => {
|
|
90
|
-
const
|
|
91
|
-
await handleSessionStart(
|
|
92
|
-
expect(
|
|
82
|
+
const { handler, session } = makeHandler();
|
|
83
|
+
await handler.handleSessionStart({ reason: "startup" }, makeCtx());
|
|
84
|
+
expect(session.logResolvedConfigPaths).toHaveBeenCalledOnce();
|
|
93
85
|
});
|
|
94
86
|
|
|
95
87
|
it("resolves agent name from ctx", async () => {
|
|
96
88
|
const ctx = makeCtx();
|
|
97
|
-
const
|
|
98
|
-
await handleSessionStart(
|
|
99
|
-
expect(
|
|
89
|
+
const { handler, session } = makeHandler();
|
|
90
|
+
await handler.handleSessionStart({ reason: "startup" }, ctx);
|
|
91
|
+
expect(session.resolveAgentName).toHaveBeenCalledWith(ctx);
|
|
100
92
|
});
|
|
101
93
|
|
|
102
94
|
it("notifies each policy issue", async () => {
|
|
103
|
-
const session =
|
|
95
|
+
const { handler, session } = makeHandler({
|
|
104
96
|
getConfigIssues: vi.fn().mockReturnValue(["issue A", "issue B"]),
|
|
105
97
|
});
|
|
106
|
-
|
|
107
|
-
await handleSessionStart(deps, { reason: "startup" }, makeCtx());
|
|
98
|
+
await handler.handleSessionStart({ reason: "startup" }, makeCtx());
|
|
108
99
|
expect(session.logger.warn).toHaveBeenCalledWith("issue A");
|
|
109
100
|
expect(session.logger.warn).toHaveBeenCalledWith("issue B");
|
|
110
101
|
});
|
|
111
102
|
|
|
112
103
|
it("does not warn when there are no policy issues", async () => {
|
|
113
|
-
const
|
|
114
|
-
await handleSessionStart(
|
|
115
|
-
expect(
|
|
104
|
+
const { handler, session } = makeHandler();
|
|
105
|
+
await handler.handleSessionStart({ reason: "startup" }, makeCtx());
|
|
106
|
+
expect(session.logger.warn).not.toHaveBeenCalled();
|
|
116
107
|
});
|
|
117
108
|
|
|
118
109
|
it("writes lifecycle.reload debug log when reason is reload", async () => {
|
|
119
110
|
const ctx = makeCtx({ cwd: "/proj" });
|
|
120
|
-
const
|
|
121
|
-
await handleSessionStart(
|
|
122
|
-
expect(
|
|
111
|
+
const { handler, session } = makeHandler();
|
|
112
|
+
await handler.handleSessionStart({ reason: "reload" }, ctx);
|
|
113
|
+
expect(session.logger.debug).toHaveBeenCalledWith("lifecycle.reload", {
|
|
123
114
|
triggeredBy: "session_start",
|
|
124
115
|
reason: "reload",
|
|
125
116
|
cwd: "/proj",
|
|
@@ -127,19 +118,18 @@ describe("handleSessionStart", () => {
|
|
|
127
118
|
});
|
|
128
119
|
|
|
129
120
|
it("does not write lifecycle.reload debug log for non-reload reasons", async () => {
|
|
130
|
-
const
|
|
131
|
-
await handleSessionStart(
|
|
132
|
-
expect(
|
|
121
|
+
const { handler, session } = makeHandler();
|
|
122
|
+
await handler.handleSessionStart({ reason: "startup" }, makeCtx());
|
|
123
|
+
expect(session.logger.debug).not.toHaveBeenCalled();
|
|
133
124
|
});
|
|
134
125
|
|
|
135
126
|
it("calls refreshConfig before resetForNewSession", async () => {
|
|
136
127
|
const callOrder: string[] = [];
|
|
137
|
-
const
|
|
128
|
+
const { handler } = makeHandler({
|
|
138
129
|
refreshConfig: vi.fn(() => callOrder.push("refreshConfig")),
|
|
139
130
|
resetForNewSession: vi.fn(() => callOrder.push("resetForNewSession")),
|
|
140
131
|
});
|
|
141
|
-
|
|
142
|
-
await handleSessionStart(deps, { reason: "startup" }, makeCtx());
|
|
132
|
+
await handler.handleSessionStart({ reason: "startup" }, makeCtx());
|
|
143
133
|
expect(callOrder).toEqual(["refreshConfig", "resetForNewSession"]);
|
|
144
134
|
});
|
|
145
135
|
});
|
|
@@ -148,24 +138,23 @@ describe("handleSessionStart", () => {
|
|
|
148
138
|
|
|
149
139
|
describe("handleResourcesDiscover", () => {
|
|
150
140
|
it("does nothing when reason is not reload", async () => {
|
|
151
|
-
const
|
|
152
|
-
await handleResourcesDiscover(
|
|
153
|
-
expect(
|
|
141
|
+
const { handler, session } = makeHandler();
|
|
142
|
+
await handler.handleResourcesDiscover({ reason: "startup" });
|
|
143
|
+
expect(session.reload).not.toHaveBeenCalled();
|
|
154
144
|
});
|
|
155
145
|
|
|
156
146
|
it("calls reload on the session on reload", async () => {
|
|
157
|
-
const
|
|
158
|
-
await handleResourcesDiscover(
|
|
159
|
-
expect(
|
|
147
|
+
const { handler, session } = makeHandler();
|
|
148
|
+
await handler.handleResourcesDiscover({ reason: "reload" });
|
|
149
|
+
expect(session.reload).toHaveBeenCalledOnce();
|
|
160
150
|
});
|
|
161
151
|
|
|
162
152
|
it("writes lifecycle.reload debug log on reload", async () => {
|
|
163
153
|
const ctx = makeCtx({ cwd: "/proj" });
|
|
164
|
-
const session =
|
|
154
|
+
const { handler, session } = makeHandler({
|
|
165
155
|
getRuntimeContext: vi.fn().mockReturnValue(ctx),
|
|
166
156
|
});
|
|
167
|
-
|
|
168
|
-
await handleResourcesDiscover(deps, { reason: "reload" });
|
|
157
|
+
await handler.handleResourcesDiscover({ reason: "reload" });
|
|
169
158
|
expect(session.logger.debug).toHaveBeenCalledWith("lifecycle.reload", {
|
|
170
159
|
triggeredBy: "resources_discover",
|
|
171
160
|
reason: "reload",
|
|
@@ -174,9 +163,9 @@ describe("handleResourcesDiscover", () => {
|
|
|
174
163
|
});
|
|
175
164
|
|
|
176
165
|
it("logs cwd as null when runtimeContext is null on reload", async () => {
|
|
177
|
-
const
|
|
178
|
-
await handleResourcesDiscover(
|
|
179
|
-
expect(
|
|
166
|
+
const { handler, session } = makeHandler();
|
|
167
|
+
await handler.handleResourcesDiscover({ reason: "reload" });
|
|
168
|
+
expect(session.logger.debug).toHaveBeenCalledWith("lifecycle.reload", {
|
|
180
169
|
triggeredBy: "resources_discover",
|
|
181
170
|
reason: "reload",
|
|
182
171
|
cwd: null,
|
|
@@ -189,11 +178,10 @@ describe("handleResourcesDiscover", () => {
|
|
|
189
178
|
describe("handleSessionShutdown", () => {
|
|
190
179
|
it("clears UI status when runtime context is present", async () => {
|
|
191
180
|
const ctx = makeCtx();
|
|
192
|
-
const
|
|
181
|
+
const { handler } = makeHandler({
|
|
193
182
|
getRuntimeContext: vi.fn().mockReturnValue(ctx),
|
|
194
183
|
});
|
|
195
|
-
|
|
196
|
-
await handleSessionShutdown(deps);
|
|
184
|
+
await handler.handleSessionShutdown();
|
|
197
185
|
expect(ctx.ui.setStatus).toHaveBeenCalledWith(
|
|
198
186
|
"permission-system",
|
|
199
187
|
undefined,
|
|
@@ -201,19 +189,19 @@ describe("handleSessionShutdown", () => {
|
|
|
201
189
|
});
|
|
202
190
|
|
|
203
191
|
it("does not throw when runtime context is null", async () => {
|
|
204
|
-
const
|
|
205
|
-
await expect(handleSessionShutdown(
|
|
192
|
+
const { handler } = makeHandler();
|
|
193
|
+
await expect(handler.handleSessionShutdown()).resolves.not.toThrow();
|
|
206
194
|
});
|
|
207
195
|
|
|
208
196
|
it("calls shutdown on the session", async () => {
|
|
209
|
-
const
|
|
210
|
-
await handleSessionShutdown(
|
|
211
|
-
expect(
|
|
197
|
+
const { handler, session } = makeHandler();
|
|
198
|
+
await handler.handleSessionShutdown();
|
|
199
|
+
expect(session.shutdown).toHaveBeenCalledOnce();
|
|
212
200
|
});
|
|
213
201
|
|
|
214
|
-
it("calls
|
|
215
|
-
const
|
|
216
|
-
await handleSessionShutdown(
|
|
217
|
-
expect(
|
|
202
|
+
it("calls cleanupRpc", async () => {
|
|
203
|
+
const { handler, cleanupRpc } = makeHandler();
|
|
204
|
+
await handler.handleSessionShutdown();
|
|
205
|
+
expect(cleanupRpc).toHaveBeenCalledOnce();
|
|
218
206
|
});
|
|
219
207
|
});
|