@gotgenes/pi-permission-system 5.7.0 → 5.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.
- package/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/handlers/input.ts +1 -1
- package/src/handlers/lifecycle.ts +3 -3
- package/src/handlers/tool-call.ts +1 -1
- package/src/handlers/types.ts +3 -5
- package/src/index.ts +2 -4
- package/src/session-logger.ts +29 -0
- package/tests/handlers/before-agent-start.test.ts +1 -3
- package/tests/handlers/input-events.test.ts +1 -3
- package/tests/handlers/input.test.ts +1 -3
- package/tests/handlers/lifecycle.test.ts +9 -11
- package/tests/handlers/tool-call-events.test.ts +1 -3
- package/tests/handlers/tool-call.test.ts +1 -3
- package/tests/session-logger.test.ts +113 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [5.8.0](https://github.com/gotgenes/pi-permission-system/compare/v5.7.0...v5.8.0) (2026-05-08)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add SessionLogger interface and createSessionLogger factory ([#127](https://github.com/gotgenes/pi-permission-system/issues/127)) ([8765ab8](https://github.com/gotgenes/pi-permission-system/commit/8765ab8cfe461324fc2a89c80486d3dde190d9d9))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* plan SessionLogger extraction ([#127](https://github.com/gotgenes/pi-permission-system/issues/127)) ([b13ac62](https://github.com/gotgenes/pi-permission-system/commit/b13ac62513d4b233ee4fc3f554324a54518f75ba))
|
|
19
|
+
* **retro:** add retro notes for issue [#126](https://github.com/gotgenes/pi-permission-system/issues/126) ([3d8a38a](https://github.com/gotgenes/pi-permission-system/commit/3d8a38a09f9dfd2570178c856aec260ebdba89b1))
|
|
20
|
+
* update architecture doc for SessionLogger ([#127](https://github.com/gotgenes/pi-permission-system/issues/127)) ([8fa4123](https://github.com/gotgenes/pi-permission-system/commit/8fa41237dc1b43cbe4487ba7d0acf75dc768ad9c))
|
|
21
|
+
|
|
8
22
|
## [5.7.0](https://github.com/gotgenes/pi-permission-system/compare/v5.6.3...v5.7.0) (2026-05-08)
|
|
9
23
|
|
|
10
24
|
|
package/package.json
CHANGED
package/src/handlers/input.ts
CHANGED
|
@@ -33,11 +33,11 @@ export async function handleSessionStart(
|
|
|
33
33
|
const policyIssues =
|
|
34
34
|
deps.session.permissionManager.getConfigIssues(agentName);
|
|
35
35
|
for (const issue of policyIssues) {
|
|
36
|
-
deps.
|
|
36
|
+
deps.logger.warn(issue);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (event.reason === "reload") {
|
|
40
|
-
deps.
|
|
40
|
+
deps.logger.debug("lifecycle.reload", {
|
|
41
41
|
triggeredBy: "session_start",
|
|
42
42
|
reason: event.reason,
|
|
43
43
|
cwd: ctx.cwd,
|
|
@@ -60,7 +60,7 @@ export async function handleResourcesDiscover(
|
|
|
60
60
|
deps.session.activeSkillEntries = [];
|
|
61
61
|
deps.session.lastActiveToolsCacheKey = null;
|
|
62
62
|
deps.session.lastPromptStateCacheKey = null;
|
|
63
|
-
deps.
|
|
63
|
+
deps.logger.debug("lifecycle.reload", {
|
|
64
64
|
triggeredBy: "resources_discover",
|
|
65
65
|
reason: event.reason,
|
|
66
66
|
cwd: runtimeContext?.cwd ?? null,
|
|
@@ -91,7 +91,7 @@ export async function handleToolCall(
|
|
|
91
91
|
deps.promptPermission(ctx, details);
|
|
92
92
|
const emitDecision: GateRunnerDeps["emitDecision"] = (e) =>
|
|
93
93
|
emitDecisionEvent(deps.events, e);
|
|
94
|
-
const { writeReviewLog } = deps;
|
|
94
|
+
const { review: writeReviewLog } = deps.logger;
|
|
95
95
|
const checkPermission: GateRunnerDeps["checkPermission"] = (
|
|
96
96
|
surface,
|
|
97
97
|
input,
|
package/src/handlers/types.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { PermissionPromptDecision } from "../permission-dialog";
|
|
|
4
4
|
import type { PermissionEventBus } from "../permission-events";
|
|
5
5
|
import type { PermissionManager } from "../permission-manager";
|
|
6
6
|
import type { SessionState } from "../runtime";
|
|
7
|
+
import type { SessionLogger } from "../session-logger";
|
|
7
8
|
|
|
8
9
|
export type PermissionReviewSource = "tool_call" | "skill_input" | "skill_read";
|
|
9
10
|
|
|
@@ -37,9 +38,8 @@ export interface HandlerDeps {
|
|
|
37
38
|
/** Mutable session state: permissionManager, sessionRules, cache keys. */
|
|
38
39
|
readonly session: SessionState;
|
|
39
40
|
|
|
40
|
-
// ── Logging
|
|
41
|
-
|
|
42
|
-
writeReviewLog(event: string, details?: Record<string, unknown>): void;
|
|
41
|
+
// ── Logging ────────────────────────────────────────────────────────────
|
|
42
|
+
readonly logger: SessionLogger;
|
|
43
43
|
|
|
44
44
|
// ── Immutable infrastructure paths ───────────────────────────────────
|
|
45
45
|
readonly piInfrastructureDirs: readonly string[];
|
|
@@ -59,8 +59,6 @@ export interface HandlerDeps {
|
|
|
59
59
|
// ── Config & lifecycle helpers ─────────────────────────────────────────
|
|
60
60
|
/** Reload merged config from disk; optionally update the stored runtime context. */
|
|
61
61
|
refreshExtensionConfig(ctx?: ExtensionContext): void;
|
|
62
|
-
/** Show a warning notification to the user (no-op when no UI is available). */
|
|
63
|
-
notifyWarning(message: string): void;
|
|
64
62
|
/** Write the resolved config path set to the review and debug logs. */
|
|
65
63
|
logResolvedConfigPaths(): void;
|
|
66
64
|
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
startForwardedPermissionPolling,
|
|
26
26
|
stopForwardedPermissionPolling,
|
|
27
27
|
} from "./runtime";
|
|
28
|
+
import { createSessionLogger } from "./session-logger";
|
|
28
29
|
import { isSubagentExecutionContext } from "./subagent-context";
|
|
29
30
|
import {
|
|
30
31
|
canResolveAskPermissionRequest,
|
|
@@ -79,8 +80,7 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
79
80
|
|
|
80
81
|
const deps: HandlerDeps = {
|
|
81
82
|
session: runtime,
|
|
82
|
-
|
|
83
|
-
writeReviewLog: (event, details) => runtime.writeReviewLog(event, details),
|
|
83
|
+
logger: createSessionLogger(runtime),
|
|
84
84
|
piInfrastructureDirs: runtime.piInfrastructureDirs,
|
|
85
85
|
getPiInfrastructureReadPaths: () =>
|
|
86
86
|
runtime.config.piInfrastructureReadPaths ?? [],
|
|
@@ -88,8 +88,6 @@ export default function piPermissionSystemExtension(pi: ExtensionAPI): void {
|
|
|
88
88
|
createPermissionManagerForCwd: (cwd) =>
|
|
89
89
|
createPermissionManagerForCwd(runtime.agentDir, cwd),
|
|
90
90
|
refreshExtensionConfig: (ctx) => refreshExtensionConfig(runtime, ctx),
|
|
91
|
-
notifyWarning: (message) =>
|
|
92
|
-
runtime.runtimeContext?.ui.notify(message, "warning"),
|
|
93
91
|
logResolvedConfigPaths: () => logResolvedConfigPaths(runtime),
|
|
94
92
|
resolveAgentName: (ctx, systemPrompt) =>
|
|
95
93
|
resolveAgentName(runtime, ctx, systemPrompt),
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ExtensionRuntime } from "./runtime";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unified logging + notification surface for handler deps.
|
|
5
|
+
*
|
|
6
|
+
* Replaces three separate HandlerDeps fields (`writeDebugLog`,
|
|
7
|
+
* `writeReviewLog`, `notifyWarning`) with a single typed collaborator.
|
|
8
|
+
* This is an intermediate abstraction on the path to PermissionSession (#129).
|
|
9
|
+
*/
|
|
10
|
+
export interface SessionLogger {
|
|
11
|
+
debug(event: string, details?: Record<string, unknown>): void;
|
|
12
|
+
review(event: string, details?: Record<string, unknown>): void;
|
|
13
|
+
warn(message: string): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a SessionLogger backed by an ExtensionRuntime.
|
|
18
|
+
*
|
|
19
|
+
* Captures `runtime` by reference so `warn` always reads the current
|
|
20
|
+
* `runtimeContext` at call time — matching the behavior of the inline
|
|
21
|
+
* closures it replaces in `src/index.ts`.
|
|
22
|
+
*/
|
|
23
|
+
export function createSessionLogger(runtime: ExtensionRuntime): SessionLogger {
|
|
24
|
+
return {
|
|
25
|
+
debug: (event, details) => runtime.writeDebugLog(event, details),
|
|
26
|
+
review: (event, details) => runtime.writeReviewLog(event, details),
|
|
27
|
+
warn: (message) => runtime.runtimeContext?.ui.notify(message, "warning"),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -77,13 +77,11 @@ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
|
|
|
77
77
|
function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
|
|
78
78
|
return {
|
|
79
79
|
session: makeSession(),
|
|
80
|
-
|
|
81
|
-
writeReviewLog: vi.fn(),
|
|
80
|
+
logger: { debug: vi.fn(), review: vi.fn(), warn: vi.fn() },
|
|
82
81
|
piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
|
|
83
82
|
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
84
83
|
createPermissionManagerForCwd: vi.fn().mockReturnValue(makePm()),
|
|
85
84
|
refreshExtensionConfig: vi.fn(),
|
|
86
|
-
notifyWarning: vi.fn(),
|
|
87
85
|
logResolvedConfigPaths: vi.fn(),
|
|
88
86
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
89
87
|
canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
|
|
@@ -68,14 +68,12 @@ function makeDeps(
|
|
|
68
68
|
): HandlerDeps {
|
|
69
69
|
return {
|
|
70
70
|
session: makeSession(state),
|
|
71
|
-
|
|
72
|
-
writeReviewLog: vi.fn(),
|
|
71
|
+
logger: { debug: vi.fn(), review: vi.fn(), warn: vi.fn() },
|
|
73
72
|
piInfrastructureDirs: ["/test/agent"],
|
|
74
73
|
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
75
74
|
events: makeEvents(),
|
|
76
75
|
createPermissionManagerForCwd: vi.fn(),
|
|
77
76
|
refreshExtensionConfig: vi.fn(),
|
|
78
|
-
notifyWarning: vi.fn(),
|
|
79
77
|
logResolvedConfigPaths: vi.fn(),
|
|
80
78
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
81
79
|
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
@@ -56,13 +56,11 @@ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
|
|
|
56
56
|
function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
|
|
57
57
|
return {
|
|
58
58
|
session: makeSession(),
|
|
59
|
-
|
|
60
|
-
writeReviewLog: vi.fn(),
|
|
59
|
+
logger: { debug: vi.fn(), review: vi.fn(), warn: vi.fn() },
|
|
61
60
|
piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
|
|
62
61
|
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
63
62
|
createPermissionManagerForCwd: vi.fn(),
|
|
64
63
|
refreshExtensionConfig: vi.fn(),
|
|
65
|
-
notifyWarning: vi.fn(),
|
|
66
64
|
logResolvedConfigPaths: vi.fn(),
|
|
67
65
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
68
66
|
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
@@ -83,15 +83,13 @@ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
|
|
|
83
83
|
function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
|
|
84
84
|
return {
|
|
85
85
|
session: makeSession(),
|
|
86
|
-
|
|
87
|
-
writeReviewLog: vi.fn(),
|
|
86
|
+
logger: { debug: vi.fn(), review: vi.fn(), warn: vi.fn() },
|
|
88
87
|
piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
|
|
89
88
|
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
90
89
|
createPermissionManagerForCwd: vi
|
|
91
90
|
.fn()
|
|
92
91
|
.mockReturnValue(makePermissionManager()),
|
|
93
92
|
refreshExtensionConfig: vi.fn(),
|
|
94
|
-
notifyWarning: vi.fn(),
|
|
95
93
|
logResolvedConfigPaths: vi.fn(),
|
|
96
94
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
97
95
|
canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
|
|
@@ -189,21 +187,21 @@ describe("handleSessionStart", () => {
|
|
|
189
187
|
createPermissionManagerForCwd: vi.fn().mockReturnValue(pm),
|
|
190
188
|
});
|
|
191
189
|
await handleSessionStart(deps, { reason: "startup" }, makeCtx());
|
|
192
|
-
expect(deps.
|
|
193
|
-
expect(deps.
|
|
190
|
+
expect(deps.logger.warn).toHaveBeenCalledWith("issue A");
|
|
191
|
+
expect(deps.logger.warn).toHaveBeenCalledWith("issue B");
|
|
194
192
|
});
|
|
195
193
|
|
|
196
194
|
it("does not call notifyWarning when there are no policy issues", async () => {
|
|
197
195
|
const deps = makeDeps();
|
|
198
196
|
await handleSessionStart(deps, { reason: "startup" }, makeCtx());
|
|
199
|
-
expect(deps.
|
|
197
|
+
expect(deps.logger.warn).not.toHaveBeenCalled();
|
|
200
198
|
});
|
|
201
199
|
|
|
202
200
|
it("writes lifecycle.reload debug log when reason is reload", async () => {
|
|
203
201
|
const ctx = makeCtx({ cwd: "/proj" });
|
|
204
202
|
const deps = makeDeps();
|
|
205
203
|
await handleSessionStart(deps, { reason: "reload" }, ctx);
|
|
206
|
-
expect(deps.
|
|
204
|
+
expect(deps.logger.debug).toHaveBeenCalledWith("lifecycle.reload", {
|
|
207
205
|
triggeredBy: "session_start",
|
|
208
206
|
reason: "reload",
|
|
209
207
|
cwd: "/proj",
|
|
@@ -213,7 +211,7 @@ describe("handleSessionStart", () => {
|
|
|
213
211
|
it("does not write lifecycle.reload debug log for non-reload reasons", async () => {
|
|
214
212
|
const deps = makeDeps();
|
|
215
213
|
await handleSessionStart(deps, { reason: "startup" }, makeCtx());
|
|
216
|
-
expect(deps.
|
|
214
|
+
expect(deps.logger.debug).not.toHaveBeenCalled();
|
|
217
215
|
});
|
|
218
216
|
});
|
|
219
217
|
|
|
@@ -224,7 +222,7 @@ describe("handleResourcesDiscover", () => {
|
|
|
224
222
|
const deps = makeDeps();
|
|
225
223
|
await handleResourcesDiscover(deps, { reason: "startup" });
|
|
226
224
|
expect(deps.createPermissionManagerForCwd).not.toHaveBeenCalled();
|
|
227
|
-
expect(deps.
|
|
225
|
+
expect(deps.logger.debug).not.toHaveBeenCalled();
|
|
228
226
|
});
|
|
229
227
|
|
|
230
228
|
it("creates and stores a new PM using runtimeContext.cwd on reload", async () => {
|
|
@@ -259,7 +257,7 @@ describe("handleResourcesDiscover", () => {
|
|
|
259
257
|
const ctx = makeCtx({ cwd: "/proj" });
|
|
260
258
|
const deps = makeDeps({ session: makeSession({ runtimeContext: ctx }) });
|
|
261
259
|
await handleResourcesDiscover(deps, { reason: "reload" });
|
|
262
|
-
expect(deps.
|
|
260
|
+
expect(deps.logger.debug).toHaveBeenCalledWith("lifecycle.reload", {
|
|
263
261
|
triggeredBy: "resources_discover",
|
|
264
262
|
reason: "reload",
|
|
265
263
|
cwd: "/proj",
|
|
@@ -269,7 +267,7 @@ describe("handleResourcesDiscover", () => {
|
|
|
269
267
|
it("logs cwd as null when runtimeContext is null on reload", async () => {
|
|
270
268
|
const deps = makeDeps();
|
|
271
269
|
await handleResourcesDiscover(deps, { reason: "reload" });
|
|
272
|
-
expect(deps.
|
|
270
|
+
expect(deps.logger.debug).toHaveBeenCalledWith("lifecycle.reload", {
|
|
273
271
|
triggeredBy: "resources_discover",
|
|
274
272
|
reason: "reload",
|
|
275
273
|
cwd: null,
|
|
@@ -89,14 +89,12 @@ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
|
|
|
89
89
|
function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
|
|
90
90
|
return {
|
|
91
91
|
session: makeSession(),
|
|
92
|
-
|
|
93
|
-
writeReviewLog: vi.fn(),
|
|
92
|
+
logger: { debug: vi.fn(), review: vi.fn(), warn: vi.fn() },
|
|
94
93
|
piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
|
|
95
94
|
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
96
95
|
events: makeEvents(),
|
|
97
96
|
createPermissionManagerForCwd: vi.fn(),
|
|
98
97
|
refreshExtensionConfig: vi.fn(),
|
|
99
|
-
notifyWarning: vi.fn(),
|
|
100
98
|
logResolvedConfigPaths: vi.fn(),
|
|
101
99
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
102
100
|
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
@@ -77,13 +77,11 @@ function makeSession(overrides: Partial<SessionState> = {}): SessionState {
|
|
|
77
77
|
function makeDeps(overrides: Partial<HandlerDeps> = {}): HandlerDeps {
|
|
78
78
|
return {
|
|
79
79
|
session: makeSession(),
|
|
80
|
-
|
|
81
|
-
writeReviewLog: vi.fn(),
|
|
80
|
+
logger: { debug: vi.fn(), review: vi.fn(), warn: vi.fn() },
|
|
82
81
|
piInfrastructureDirs: ["/test/agent", "/test/agent/git"],
|
|
83
82
|
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
84
83
|
createPermissionManagerForCwd: vi.fn(),
|
|
85
84
|
refreshExtensionConfig: vi.fn(),
|
|
86
|
-
notifyWarning: vi.fn(),
|
|
87
85
|
logResolvedConfigPaths: vi.fn(),
|
|
88
86
|
resolveAgentName: vi.fn().mockReturnValue(null),
|
|
89
87
|
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { ExtensionRuntime } from "../src/runtime";
|
|
3
|
+
import { createSessionLogger } from "../src/session-logger";
|
|
4
|
+
|
|
5
|
+
// ── helpers ────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
function makeRuntime(
|
|
8
|
+
overrides: Partial<ExtensionRuntime> = {},
|
|
9
|
+
): ExtensionRuntime {
|
|
10
|
+
return {
|
|
11
|
+
runtimeContext: null,
|
|
12
|
+
writeDebugLog: vi.fn(),
|
|
13
|
+
writeReviewLog: vi.fn(),
|
|
14
|
+
...overrides,
|
|
15
|
+
} as unknown as ExtensionRuntime;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ── createSessionLogger ────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
describe("createSessionLogger", () => {
|
|
21
|
+
describe("debug", () => {
|
|
22
|
+
it("delegates to runtime.writeDebugLog with event and details", () => {
|
|
23
|
+
const runtime = makeRuntime();
|
|
24
|
+
const logger = createSessionLogger(runtime);
|
|
25
|
+
|
|
26
|
+
logger.debug("test.event", { key: "value" });
|
|
27
|
+
|
|
28
|
+
expect(runtime.writeDebugLog).toHaveBeenCalledWith("test.event", {
|
|
29
|
+
key: "value",
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("delegates to runtime.writeDebugLog with event and no details", () => {
|
|
34
|
+
const runtime = makeRuntime();
|
|
35
|
+
const logger = createSessionLogger(runtime);
|
|
36
|
+
|
|
37
|
+
logger.debug("test.event");
|
|
38
|
+
|
|
39
|
+
expect(runtime.writeDebugLog).toHaveBeenCalledWith(
|
|
40
|
+
"test.event",
|
|
41
|
+
undefined,
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("review", () => {
|
|
47
|
+
it("delegates to runtime.writeReviewLog with event and details", () => {
|
|
48
|
+
const runtime = makeRuntime();
|
|
49
|
+
const logger = createSessionLogger(runtime);
|
|
50
|
+
|
|
51
|
+
logger.review("permission.granted", { agentName: "coder" });
|
|
52
|
+
|
|
53
|
+
expect(runtime.writeReviewLog).toHaveBeenCalledWith(
|
|
54
|
+
"permission.granted",
|
|
55
|
+
{ agentName: "coder" },
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("delegates to runtime.writeReviewLog with event and no details", () => {
|
|
60
|
+
const runtime = makeRuntime();
|
|
61
|
+
const logger = createSessionLogger(runtime);
|
|
62
|
+
|
|
63
|
+
logger.review("permission.granted");
|
|
64
|
+
|
|
65
|
+
expect(runtime.writeReviewLog).toHaveBeenCalledWith(
|
|
66
|
+
"permission.granted",
|
|
67
|
+
undefined,
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("warn", () => {
|
|
73
|
+
it("calls ui.notify with the message and 'warning' severity when runtimeContext is present", () => {
|
|
74
|
+
const notify = vi.fn();
|
|
75
|
+
const runtime = makeRuntime({
|
|
76
|
+
runtimeContext: {
|
|
77
|
+
ui: { notify, setStatus: vi.fn(), select: vi.fn(), input: vi.fn() },
|
|
78
|
+
} as unknown as ExtensionRuntime["runtimeContext"],
|
|
79
|
+
});
|
|
80
|
+
const logger = createSessionLogger(runtime);
|
|
81
|
+
|
|
82
|
+
logger.warn("Something went wrong");
|
|
83
|
+
|
|
84
|
+
expect(notify).toHaveBeenCalledWith("Something went wrong", "warning");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("does not throw when runtimeContext is null", () => {
|
|
88
|
+
const runtime = makeRuntime({ runtimeContext: null });
|
|
89
|
+
const logger = createSessionLogger(runtime);
|
|
90
|
+
|
|
91
|
+
expect(() => logger.warn("no-op warning")).not.toThrow();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("reads runtimeContext at call time, not at creation time", () => {
|
|
95
|
+
const runtime = makeRuntime({ runtimeContext: null });
|
|
96
|
+
const logger = createSessionLogger(runtime);
|
|
97
|
+
|
|
98
|
+
// runtimeContext is null at creation — warn should be a no-op now
|
|
99
|
+
logger.warn("early warning");
|
|
100
|
+
|
|
101
|
+
// Later runtimeContext is set
|
|
102
|
+
const notify = vi.fn();
|
|
103
|
+
runtime.runtimeContext = {
|
|
104
|
+
ui: { notify, setStatus: vi.fn(), select: vi.fn(), input: vi.fn() },
|
|
105
|
+
} as unknown as ExtensionRuntime["runtimeContext"];
|
|
106
|
+
|
|
107
|
+
logger.warn("late warning");
|
|
108
|
+
|
|
109
|
+
expect(notify).toHaveBeenCalledOnce();
|
|
110
|
+
expect(notify).toHaveBeenCalledWith("late warning", "warning");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|