@gotgenes/pi-permission-system 3.0.2 → 3.0.4
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 +26 -0
- package/index.ts +1 -1
- package/package.json +3 -2
- package/src/bash-filter.ts +2 -2
- package/src/common.ts +1 -1
- package/src/config-loader.ts +3 -3
- package/src/config-modal.ts +1 -1
- package/src/config-reporter.ts +1 -1
- package/src/extension-config.ts +1 -1
- package/src/external-directory.ts +1 -1
- package/src/forwarded-permissions/io.ts +2 -2
- package/src/forwarded-permissions/polling.ts +6 -6
- package/src/index.ts +23 -23
- package/src/logging.ts +1 -1
- package/src/permission-forwarding.ts +1 -1
- package/src/permission-manager.ts +6 -6
- package/src/permission-prompts.ts +3 -3
- package/src/skill-prompt-sanitizer.ts +2 -2
- package/src/status.ts +2 -2
- package/src/subagent-context.ts +1 -1
- package/src/system-prompt-sanitizer.ts +22 -1
- package/src/tool-input-preview.ts +3 -3
- package/src/tool-registry.ts +1 -1
- package/src/yolo-mode.ts +2 -2
- package/tests/active-agent.test.ts +1 -1
- package/tests/bash-filter.test.ts +3 -3
- package/tests/common.test.ts +1 -1
- package/tests/config-loader.test.ts +1 -1
- package/tests/config-modal.test.ts +2 -2
- package/tests/config-paths.test.ts +1 -1
- package/tests/config-reporter.test.ts +4 -4
- package/tests/extension-config.test.ts +1 -1
- package/tests/external-directory.test.ts +1 -1
- package/tests/permission-prompts.test.ts +4 -4
- package/tests/permission-system.test.ts +14 -14
- package/tests/session-start.test.ts +4 -4
- package/tests/skill-prompt-sanitizer.test.ts +3 -3
- package/tests/subagent-context.test.ts +2 -2
- package/tests/system-prompt-sanitizer.test.ts +43 -2
- package/tests/tool-input-preview.test.ts +3 -3
- package/tests/tool-registry.test.ts +1 -1
- package/tests/wildcard-matcher.test.ts +1 -1
- package/tests/yolo-mode.test.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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
|
+
## [3.0.4](https://github.com/gotgenes/pi-permission-system/compare/v3.0.3...v3.0.4) (2026-05-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* plan drop .js extensions from internal imports ([#32](https://github.com/gotgenes/pi-permission-system/issues/32)) ([1d73759](https://github.com/gotgenes/pi-permission-system/commit/1d73759bafb4de02f20817d977eca181a5df5a54))
|
|
14
|
+
* **retro:** add retro notes for issue [#33](https://github.com/gotgenes/pi-permission-system/issues/33) ([4e4ef43](https://github.com/gotgenes/pi-permission-system/commit/4e4ef4397efe9fd0c75ac00b1b411eef50603f33))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Miscellaneous Chores
|
|
18
|
+
|
|
19
|
+
* add lint:imports guard against .js extensions ([#32](https://github.com/gotgenes/pi-permission-system/issues/32)) ([fa0b924](https://github.com/gotgenes/pi-permission-system/commit/fa0b924fb5a8741de294eff8b2f2f94aded34f5d))
|
|
20
|
+
|
|
21
|
+
## [3.0.3](https://github.com/gotgenes/pi-permission-system/compare/v3.0.2...v3.0.3) (2026-05-03)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* stop findSection at first non-body line instead of EOF ([#33](https://github.com/gotgenes/pi-permission-system/issues/33)) ([15c178e](https://github.com/gotgenes/pi-permission-system/commit/15c178ea1aa42c091885f0aeacd87ba5b298ce24))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Documentation
|
|
30
|
+
|
|
31
|
+
* plan fix for findSection greedy end boundary ([#33](https://github.com/gotgenes/pi-permission-system/issues/33)) ([72a5f9e](https://github.com/gotgenes/pi-permission-system/commit/72a5f9e4ab14cc9959781994cdc7dc52f1dfa657))
|
|
32
|
+
* **retro:** add retro notes for issue [#35](https://github.com/gotgenes/pi-permission-system/issues/35) ([89830cb](https://github.com/gotgenes/pi-permission-system/commit/89830cb7c562ed092d4f08031584f0e0855326de))
|
|
33
|
+
|
|
8
34
|
## [3.0.2](https://github.com/gotgenes/pi-permission-system/compare/v3.0.1...v3.0.2) (2026-05-03)
|
|
9
35
|
|
|
10
36
|
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotgenes/pi-permission-system",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"description": "Permission enforcement extension for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"lint:fix": "biome check --write .",
|
|
25
25
|
"lint:md": "markdownlint-cli2 '*.md' 'docs/**/*.md' '.pi/prompts/**/*.md'",
|
|
26
26
|
"lint:md:fix": "markdownlint-cli2 --fix '*.md' 'docs/**/*.md' '.pi/prompts/**/*.md'",
|
|
27
|
-
"lint:
|
|
27
|
+
"lint:imports": "! grep -rn --include='*.ts' 'from \"\\.[./][^\"]*\\.js\"' src/ tests/ index.ts",
|
|
28
|
+
"lint:all": "npm run lint && npm run lint:md && npm run lint:imports",
|
|
28
29
|
"format": "biome format --write .",
|
|
29
30
|
"test": "vitest run",
|
|
30
31
|
"test:watch": "vitest",
|
package/src/bash-filter.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { BashPermissions, PermissionState } from "./types
|
|
1
|
+
import type { BashPermissions, PermissionState } from "./types";
|
|
2
2
|
import {
|
|
3
3
|
type CompiledWildcardPattern,
|
|
4
4
|
compileWildcardPatterns,
|
|
5
5
|
findCompiledWildcardMatch,
|
|
6
|
-
} from "./wildcard-matcher
|
|
6
|
+
} from "./wildcard-matcher";
|
|
7
7
|
|
|
8
8
|
type CompiledPattern = CompiledWildcardPattern<PermissionState>;
|
|
9
9
|
|
package/src/common.ts
CHANGED
package/src/config-loader.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { normalize } from "node:path";
|
|
3
3
|
|
|
4
|
-
import { isPermissionState, toRecord } from "./common
|
|
4
|
+
import { isPermissionState, toRecord } from "./common";
|
|
5
5
|
import {
|
|
6
6
|
getGlobalConfigPath,
|
|
7
7
|
getLegacyExtensionConfigPath,
|
|
8
8
|
getLegacyGlobalPolicyPath,
|
|
9
9
|
getLegacyProjectPolicyPath,
|
|
10
10
|
getProjectConfigPath,
|
|
11
|
-
} from "./config-paths
|
|
12
|
-
import type { PermissionDefaultPolicy, PermissionState } from "./types
|
|
11
|
+
} from "./config-paths";
|
|
12
|
+
import type { PermissionDefaultPolicy, PermissionState } from "./types";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Unified config shape combining runtime knobs and policy in one object.
|
package/src/config-modal.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type SettingItem, SettingsList } from "@mariozechner/pi-tui";
|
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_EXTENSION_CONFIG,
|
|
10
10
|
type PermissionSystemExtensionConfig,
|
|
11
|
-
} from "./extension-config
|
|
11
|
+
} from "./extension-config";
|
|
12
12
|
|
|
13
13
|
interface PermissionSystemConfigController {
|
|
14
14
|
getConfig(): PermissionSystemExtensionConfig;
|
package/src/config-reporter.ts
CHANGED
package/src/extension-config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join, normalize, resolve, sep } from "node:path";
|
|
3
3
|
|
|
4
|
-
import { getNonEmptyString, toRecord } from "./common
|
|
4
|
+
import { getNonEmptyString, toRecord } from "./common";
|
|
5
5
|
|
|
6
6
|
export const PATH_BEARING_TOOLS = new Set([
|
|
7
7
|
"read",
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
writeFileSync,
|
|
10
10
|
} from "node:fs";
|
|
11
11
|
|
|
12
|
-
import { isPermissionDecisionState } from "../permission-dialog
|
|
12
|
+
import { isPermissionDecisionState } from "../permission-dialog";
|
|
13
13
|
import {
|
|
14
14
|
createPermissionForwardingLocation,
|
|
15
15
|
type ForwardedPermissionRequest,
|
|
16
16
|
type ForwardedPermissionResponse,
|
|
17
17
|
type PermissionForwardingLocation,
|
|
18
|
-
} from "../permission-forwarding
|
|
18
|
+
} from "../permission-forwarding";
|
|
19
19
|
|
|
20
20
|
type LogFn = (event: string, details: Record<string, unknown>) => void;
|
|
21
21
|
|
|
@@ -5,9 +5,9 @@ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
|
5
5
|
import {
|
|
6
6
|
getActiveAgentName,
|
|
7
7
|
getActiveAgentNameFromSystemPrompt,
|
|
8
|
-
} from "../active-agent
|
|
9
|
-
import { toRecord } from "../common
|
|
10
|
-
import type { PermissionPromptDecision } from "../permission-dialog
|
|
8
|
+
} from "../active-agent";
|
|
9
|
+
import { toRecord } from "../common";
|
|
10
|
+
import type { PermissionPromptDecision } from "../permission-dialog";
|
|
11
11
|
import {
|
|
12
12
|
type ForwardedPermissionRequest,
|
|
13
13
|
type ForwardedPermissionResponse,
|
|
@@ -15,8 +15,8 @@ import {
|
|
|
15
15
|
PERMISSION_FORWARDING_POLL_INTERVAL_MS,
|
|
16
16
|
PERMISSION_FORWARDING_TIMEOUT_MS,
|
|
17
17
|
resolvePermissionForwardingTargetSessionId,
|
|
18
|
-
} from "../permission-forwarding
|
|
19
|
-
import { isSubagentExecutionContext } from "../subagent-context
|
|
18
|
+
} from "../permission-forwarding";
|
|
19
|
+
import { isSubagentExecutionContext } from "../subagent-context";
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
22
|
cleanupPermissionForwardingLocationIfEmpty,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
safeDeleteFile,
|
|
31
31
|
sleep,
|
|
32
32
|
writeJsonFileAtomic,
|
|
33
|
-
} from "./io
|
|
33
|
+
} from "./io";
|
|
34
34
|
|
|
35
35
|
export interface PermissionForwardingDeps {
|
|
36
36
|
forwardingDir: string;
|
package/src/index.ts
CHANGED
|
@@ -16,15 +16,15 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
getActiveAgentName,
|
|
18
18
|
getActiveAgentNameFromSystemPrompt,
|
|
19
|
-
} from "./active-agent
|
|
19
|
+
} from "./active-agent";
|
|
20
20
|
import {
|
|
21
21
|
createActiveToolsCacheKey,
|
|
22
22
|
createBeforeAgentStartPromptStateKey,
|
|
23
23
|
shouldApplyCachedAgentStartState,
|
|
24
|
-
} from "./before-agent-start-cache
|
|
25
|
-
import { toRecord } from "./common
|
|
26
|
-
import { loadAndMergeConfigs, loadUnifiedConfig } from "./config-loader
|
|
27
|
-
import { registerPermissionSystemCommand } from "./config-modal
|
|
24
|
+
} from "./before-agent-start-cache";
|
|
25
|
+
import { toRecord } from "./common";
|
|
26
|
+
import { loadAndMergeConfigs, loadUnifiedConfig } from "./config-loader";
|
|
27
|
+
import { registerPermissionSystemCommand } from "./config-modal";
|
|
28
28
|
import {
|
|
29
29
|
DEBUG_LOG_FILENAME,
|
|
30
30
|
getGlobalConfigPath,
|
|
@@ -34,15 +34,15 @@ import {
|
|
|
34
34
|
getLegacyProjectPolicyPath,
|
|
35
35
|
getProjectConfigPath,
|
|
36
36
|
REVIEW_LOG_FILENAME,
|
|
37
|
-
} from "./config-paths
|
|
38
|
-
import { buildResolvedConfigLogEntry } from "./config-reporter
|
|
37
|
+
} from "./config-paths";
|
|
38
|
+
import { buildResolvedConfigLogEntry } from "./config-reporter";
|
|
39
39
|
import {
|
|
40
40
|
DEFAULT_EXTENSION_CONFIG,
|
|
41
41
|
EXTENSION_ROOT,
|
|
42
42
|
ensurePermissionSystemLogsDirectory,
|
|
43
43
|
normalizePermissionSystemConfig,
|
|
44
44
|
type PermissionSystemExtensionConfig,
|
|
45
|
-
} from "./extension-config
|
|
45
|
+
} from "./extension-config";
|
|
46
46
|
import {
|
|
47
47
|
formatExternalDirectoryAskPrompt,
|
|
48
48
|
formatExternalDirectoryDenyReason,
|
|
@@ -51,20 +51,20 @@ import {
|
|
|
51
51
|
isPathOutsideWorkingDirectory,
|
|
52
52
|
normalizePathForComparison,
|
|
53
53
|
PATH_BEARING_TOOLS,
|
|
54
|
-
} from "./external-directory
|
|
55
|
-
import { setForwardedPermissionLogger } from "./forwarded-permissions/io
|
|
54
|
+
} from "./external-directory";
|
|
55
|
+
import { setForwardedPermissionLogger } from "./forwarded-permissions/io";
|
|
56
56
|
import {
|
|
57
57
|
confirmPermission,
|
|
58
58
|
type PermissionForwardingDeps,
|
|
59
59
|
processForwardedPermissionRequests,
|
|
60
|
-
} from "./forwarded-permissions/polling
|
|
61
|
-
import { createPermissionSystemLogger } from "./logging
|
|
60
|
+
} from "./forwarded-permissions/polling";
|
|
61
|
+
import { createPermissionSystemLogger } from "./logging";
|
|
62
62
|
import {
|
|
63
63
|
type PermissionPromptDecision,
|
|
64
64
|
requestPermissionDecisionFromUi,
|
|
65
|
-
} from "./permission-dialog
|
|
66
|
-
import { PERMISSION_FORWARDING_POLL_INTERVAL_MS } from "./permission-forwarding
|
|
67
|
-
import { PermissionManager } from "./permission-manager
|
|
65
|
+
} from "./permission-dialog";
|
|
66
|
+
import { PERMISSION_FORWARDING_POLL_INTERVAL_MS } from "./permission-forwarding";
|
|
67
|
+
import { PermissionManager } from "./permission-manager";
|
|
68
68
|
import {
|
|
69
69
|
formatAskPrompt,
|
|
70
70
|
formatDenyReason,
|
|
@@ -74,27 +74,27 @@ import {
|
|
|
74
74
|
formatSkillPathDenyReason,
|
|
75
75
|
formatUnknownToolReason,
|
|
76
76
|
formatUserDeniedReason,
|
|
77
|
-
} from "./permission-prompts
|
|
77
|
+
} from "./permission-prompts";
|
|
78
78
|
import {
|
|
79
79
|
findSkillPathMatch,
|
|
80
80
|
resolveSkillPromptEntries,
|
|
81
81
|
type SkillPromptEntry,
|
|
82
|
-
} from "./skill-prompt-sanitizer
|
|
82
|
+
} from "./skill-prompt-sanitizer";
|
|
83
83
|
import {
|
|
84
84
|
PERMISSION_SYSTEM_STATUS_KEY,
|
|
85
85
|
syncPermissionSystemStatus,
|
|
86
|
-
} from "./status
|
|
87
|
-
import { isSubagentExecutionContext } from "./subagent-context
|
|
88
|
-
import { sanitizeAvailableToolsSection } from "./system-prompt-sanitizer
|
|
89
|
-
import { getPermissionLogContext } from "./tool-input-preview
|
|
86
|
+
} from "./status";
|
|
87
|
+
import { isSubagentExecutionContext } from "./subagent-context";
|
|
88
|
+
import { sanitizeAvailableToolsSection } from "./system-prompt-sanitizer";
|
|
89
|
+
import { getPermissionLogContext } from "./tool-input-preview";
|
|
90
90
|
import {
|
|
91
91
|
checkRequestedToolRegistration,
|
|
92
92
|
getToolNameFromValue,
|
|
93
|
-
} from "./tool-registry
|
|
93
|
+
} from "./tool-registry";
|
|
94
94
|
import {
|
|
95
95
|
canResolveAskPermissionRequest,
|
|
96
96
|
shouldAutoApprovePermissionState,
|
|
97
|
-
} from "./yolo-mode
|
|
97
|
+
} from "./yolo-mode";
|
|
98
98
|
|
|
99
99
|
const PI_AGENT_DIR = getAgentDir();
|
|
100
100
|
const SESSIONS_DIR = join(PI_AGENT_DIR, "sessions");
|
package/src/logging.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
LOGS_DIR,
|
|
8
8
|
PERMISSION_REVIEW_LOG_PATH,
|
|
9
9
|
type PermissionSystemExtensionConfig,
|
|
10
|
-
} from "./extension-config
|
|
10
|
+
} from "./extension-config";
|
|
11
11
|
|
|
12
12
|
export function safeJsonStringify(value: unknown): string | undefined {
|
|
13
13
|
const seen = new WeakSet<object>();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
|
|
3
|
-
import type { PermissionDecisionState } from "./permission-dialog
|
|
3
|
+
import type { PermissionDecisionState } from "./permission-dialog";
|
|
4
4
|
|
|
5
5
|
export const PERMISSION_FORWARDING_POLL_INTERVAL_MS = 250;
|
|
6
6
|
export const PERMISSION_FORWARDING_TIMEOUT_MS = 10 * 60 * 1000;
|
|
@@ -2,16 +2,16 @@ import { existsSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { getAgentDir } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
|
|
5
|
-
import { BashFilter } from "./bash-filter
|
|
5
|
+
import { BashFilter } from "./bash-filter";
|
|
6
6
|
import {
|
|
7
7
|
extractFrontmatter,
|
|
8
8
|
getNonEmptyString,
|
|
9
9
|
isPermissionState,
|
|
10
10
|
parseSimpleYamlMap,
|
|
11
11
|
toRecord,
|
|
12
|
-
} from "./common
|
|
13
|
-
import { loadUnifiedConfig, stripJsonComments } from "./config-loader
|
|
14
|
-
import { getGlobalConfigPath } from "./config-paths
|
|
12
|
+
} from "./common";
|
|
13
|
+
import { loadUnifiedConfig, stripJsonComments } from "./config-loader";
|
|
14
|
+
import { getGlobalConfigPath } from "./config-paths";
|
|
15
15
|
import type {
|
|
16
16
|
AgentPermissions,
|
|
17
17
|
BashPermissions,
|
|
@@ -19,13 +19,13 @@ import type {
|
|
|
19
19
|
PermissionCheckResult,
|
|
20
20
|
PermissionDefaultPolicy,
|
|
21
21
|
PermissionState,
|
|
22
|
-
} from "./types
|
|
22
|
+
} from "./types";
|
|
23
23
|
import {
|
|
24
24
|
type CompiledWildcardPattern,
|
|
25
25
|
compileWildcardPatternEntries,
|
|
26
26
|
findCompiledWildcardMatch,
|
|
27
27
|
findCompiledWildcardMatchForNames,
|
|
28
|
-
} from "./wildcard-matcher
|
|
28
|
+
} from "./wildcard-matcher";
|
|
29
29
|
|
|
30
30
|
function defaultGlobalConfigPath(): string {
|
|
31
31
|
return getGlobalConfigPath(getAgentDir());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { SkillPromptEntry } from "./skill-prompt-sanitizer
|
|
2
|
-
import { formatToolInputForPrompt } from "./tool-input-preview
|
|
3
|
-
import type { PermissionCheckResult } from "./types
|
|
1
|
+
import type { SkillPromptEntry } from "./skill-prompt-sanitizer";
|
|
2
|
+
import { formatToolInputForPrompt } from "./tool-input-preview";
|
|
3
|
+
import type { PermissionCheckResult } from "./types";
|
|
4
4
|
|
|
5
5
|
export function formatMissingToolNameReason(): string {
|
|
6
6
|
return "Tool call was blocked because no tool name was provided. Use a registered tool name from pi.getAllTools().";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { dirname, join, normalize, resolve, sep } from "node:path";
|
|
3
3
|
|
|
4
|
-
import type { PermissionManager } from "./permission-manager
|
|
5
|
-
import type { PermissionState } from "./types
|
|
4
|
+
import type { PermissionManager } from "./permission-manager";
|
|
5
|
+
import type { PermissionState } from "./types";
|
|
6
6
|
|
|
7
7
|
const AVAILABLE_SKILLS_OPEN_TAG = "<available_skills>";
|
|
8
8
|
const AVAILABLE_SKILLS_CLOSE_TAG = "</available_skills>";
|
package/src/status.ts
CHANGED
|
@@ -6,8 +6,8 @@ import type {
|
|
|
6
6
|
import {
|
|
7
7
|
EXTENSION_ID,
|
|
8
8
|
type PermissionSystemExtensionConfig,
|
|
9
|
-
} from "./extension-config
|
|
10
|
-
import { isYoloModeEnabled } from "./yolo-mode
|
|
9
|
+
} from "./extension-config";
|
|
10
|
+
import { isYoloModeEnabled } from "./yolo-mode";
|
|
11
11
|
|
|
12
12
|
export const PERMISSION_SYSTEM_STATUS_KEY = EXTENSION_ID;
|
|
13
13
|
export const PERMISSION_SYSTEM_YOLO_STATUS_VALUE = "yolo";
|
package/src/subagent-context.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalize } from "node:path";
|
|
2
2
|
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
3
3
|
|
|
4
|
-
import { SUBAGENT_ENV_HINT_KEYS } from "./permission-forwarding
|
|
4
|
+
import { SUBAGENT_ENV_HINT_KEYS } from "./permission-forwarding";
|
|
5
5
|
|
|
6
6
|
export function normalizeFilesystemPath(pathValue: string): string {
|
|
7
7
|
const normalizedPath = normalize(pathValue);
|
|
@@ -94,6 +94,14 @@ function isTopLevelSectionHeader(line: string): boolean {
|
|
|
94
94
|
);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function isSectionBodyLine(line: string): boolean {
|
|
98
|
+
const trimmed = line.trim();
|
|
99
|
+
if (trimmed.length === 0) return true; // blank line
|
|
100
|
+
if (trimmed.startsWith("- ")) return true; // bullet
|
|
101
|
+
if (line !== line.trimStart()) return true; // indented
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
97
105
|
function findSection(
|
|
98
106
|
lines: readonly string[],
|
|
99
107
|
header: string,
|
|
@@ -103,12 +111,25 @@ function findSection(
|
|
|
103
111
|
return null;
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
|
|
114
|
+
// If a subsequent recognised section header exists, use it as the boundary.
|
|
115
|
+
// This preserves the original behaviour for the common case where sections
|
|
116
|
+
// are adjacent (e.g. "Available tools:" followed by "Guidelines:") and
|
|
117
|
+
// ensures any prose continuation between the two headers is also removed.
|
|
107
118
|
for (let index = start + 1; index < lines.length; index += 1) {
|
|
108
119
|
if (isTopLevelSectionHeader(lines[index])) {
|
|
120
|
+
return { start, end: index };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// No subsequent section header — stop at the first non-body line so that
|
|
125
|
+
// content after the section (e.g. custom user notes) is not silently deleted.
|
|
126
|
+
let end = start + 1;
|
|
127
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
128
|
+
if (!isSectionBodyLine(lines[index])) {
|
|
109
129
|
end = index;
|
|
110
130
|
break;
|
|
111
131
|
}
|
|
132
|
+
end = index + 1;
|
|
112
133
|
}
|
|
113
134
|
|
|
114
135
|
return { start, end };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getNonEmptyString, toRecord } from "./common
|
|
2
|
-
import { safeJsonStringify } from "./logging
|
|
3
|
-
import type { PermissionCheckResult } from "./types
|
|
1
|
+
import { getNonEmptyString, toRecord } from "./common";
|
|
2
|
+
import { safeJsonStringify } from "./logging";
|
|
3
|
+
import type { PermissionCheckResult } from "./types";
|
|
4
4
|
|
|
5
5
|
export const TOOL_INPUT_PREVIEW_MAX_LENGTH = 200;
|
|
6
6
|
export const TOOL_INPUT_LOG_PREVIEW_MAX_LENGTH = 1000;
|
package/src/tool-registry.ts
CHANGED
package/src/yolo-mode.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { PermissionSystemExtensionConfig } from "./extension-config
|
|
2
|
-
import type { PermissionState } from "./types
|
|
1
|
+
import type { PermissionSystemExtensionConfig } from "./extension-config";
|
|
2
|
+
import type { PermissionState } from "./types";
|
|
3
3
|
|
|
4
4
|
export interface AskPermissionResolutionOptions {
|
|
5
5
|
config: PermissionSystemExtensionConfig;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import type { PermissionState } from "../src/types
|
|
3
|
+
import type { PermissionState } from "../src/types";
|
|
4
4
|
|
|
5
5
|
// Mock wildcard-matcher before importing the module under test.
|
|
6
6
|
vi.mock("../src/wildcard-matcher.js", () => ({
|
|
@@ -14,11 +14,11 @@ vi.mock("../src/wildcard-matcher.js", () => ({
|
|
|
14
14
|
findCompiledWildcardMatch: vi.fn(),
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
-
import { BashFilter } from "../src/bash-filter
|
|
17
|
+
import { BashFilter } from "../src/bash-filter";
|
|
18
18
|
import {
|
|
19
19
|
compileWildcardPatterns,
|
|
20
20
|
findCompiledWildcardMatch,
|
|
21
|
-
} from "../src/wildcard-matcher
|
|
21
|
+
} from "../src/wildcard-matcher";
|
|
22
22
|
|
|
23
23
|
const mockedCompilePatterns = vi.mocked(compileWildcardPatterns);
|
|
24
24
|
const mockedFindMatch = vi.mocked(findCompiledWildcardMatch);
|
package/tests/common.test.ts
CHANGED
|
@@ -3,13 +3,13 @@ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { test, vi } from "vitest";
|
|
6
|
-
import { registerPermissionSystemCommand } from "../src/config-modal
|
|
6
|
+
import { registerPermissionSystemCommand } from "../src/config-modal";
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_EXTENSION_CONFIG,
|
|
9
9
|
loadPermissionSystemConfig,
|
|
10
10
|
type PermissionSystemExtensionConfig,
|
|
11
11
|
savePermissionSystemConfig,
|
|
12
|
-
} from "../src/extension-config
|
|
12
|
+
} from "../src/extension-config";
|
|
13
13
|
|
|
14
14
|
vi.mock("@mariozechner/pi-coding-agent", () => ({
|
|
15
15
|
getSettingsListTheme: () => ({}),
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { test } from "vitest";
|
|
12
|
-
import { buildResolvedConfigLogEntry } from "../src/config-reporter
|
|
13
|
-
import { createPermissionSystemLogger } from "../src/logging
|
|
14
|
-
import type { ResolvedPolicyPaths } from "../src/permission-manager
|
|
15
|
-
import { PermissionManager } from "../src/permission-manager
|
|
12
|
+
import { buildResolvedConfigLogEntry } from "../src/config-reporter";
|
|
13
|
+
import { createPermissionSystemLogger } from "../src/logging";
|
|
14
|
+
import type { ResolvedPolicyPaths } from "../src/permission-manager";
|
|
15
|
+
import { PermissionManager } from "../src/permission-manager";
|
|
16
16
|
|
|
17
17
|
test("buildResolvedConfigLogEntry includes policy paths and legacy detection flags", () => {
|
|
18
18
|
const policyPaths: ResolvedPolicyPaths = {
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
detectMisplacedPermissionKeys,
|
|
8
8
|
loadPermissionSystemConfig,
|
|
9
9
|
normalizePermissionSystemConfig,
|
|
10
|
-
} from "../src/extension-config
|
|
10
|
+
} from "../src/extension-config";
|
|
11
11
|
|
|
12
12
|
describe("detectMisplacedPermissionKeys", () => {
|
|
13
13
|
it("returns an empty array for a record with only valid extension keys", () => {
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
formatSkillPathDenyReason,
|
|
16
16
|
formatUnknownToolReason,
|
|
17
17
|
formatUserDeniedReason,
|
|
18
|
-
} from "../src/permission-prompts
|
|
19
|
-
import type { SkillPromptEntry } from "../src/skill-prompt-sanitizer
|
|
20
|
-
import { formatToolInputForPrompt } from "../src/tool-input-preview
|
|
21
|
-
import type { PermissionCheckResult } from "../src/types
|
|
18
|
+
} from "../src/permission-prompts";
|
|
19
|
+
import type { SkillPromptEntry } from "../src/skill-prompt-sanitizer";
|
|
20
|
+
import { formatToolInputForPrompt } from "../src/tool-input-preview";
|
|
21
|
+
import type { PermissionCheckResult } from "../src/types";
|
|
22
22
|
|
|
23
23
|
const mockedFormatToolInput = vi.mocked(formatToolInputForPrompt);
|
|
24
24
|
|
|
@@ -10,51 +10,51 @@ import {
|
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { dirname, join, resolve } from "node:path";
|
|
12
12
|
import { test } from "vitest";
|
|
13
|
-
import { BashFilter } from "../src/bash-filter
|
|
13
|
+
import { BashFilter } from "../src/bash-filter";
|
|
14
14
|
import {
|
|
15
15
|
createActiveToolsCacheKey,
|
|
16
16
|
createBeforeAgentStartPromptStateKey,
|
|
17
17
|
shouldApplyCachedAgentStartState,
|
|
18
|
-
} from "../src/before-agent-start-cache
|
|
19
|
-
import { getGlobalConfigPath } from "../src/config-paths
|
|
18
|
+
} from "../src/before-agent-start-cache";
|
|
19
|
+
import { getGlobalConfigPath } from "../src/config-paths";
|
|
20
20
|
import {
|
|
21
21
|
DEFAULT_EXTENSION_CONFIG,
|
|
22
22
|
loadPermissionSystemConfig,
|
|
23
23
|
savePermissionSystemConfig,
|
|
24
|
-
} from "../src/extension-config
|
|
25
|
-
import piPermissionSystemExtension from "../src/index
|
|
26
|
-
import { createPermissionSystemLogger } from "../src/logging
|
|
24
|
+
} from "../src/extension-config";
|
|
25
|
+
import piPermissionSystemExtension from "../src/index";
|
|
26
|
+
import { createPermissionSystemLogger } from "../src/logging";
|
|
27
27
|
import {
|
|
28
28
|
createPermissionForwardingLocation,
|
|
29
29
|
isForwardedPermissionRequestForSession,
|
|
30
30
|
resolvePermissionForwardingTargetSessionId,
|
|
31
31
|
SUBAGENT_ENV_HINT_KEYS,
|
|
32
32
|
SUBAGENT_PARENT_SESSION_ENV_KEY,
|
|
33
|
-
} from "../src/permission-forwarding
|
|
33
|
+
} from "../src/permission-forwarding";
|
|
34
34
|
import {
|
|
35
35
|
normalizeRawPermission,
|
|
36
36
|
PermissionManager,
|
|
37
|
-
} from "../src/permission-manager
|
|
37
|
+
} from "../src/permission-manager";
|
|
38
38
|
import {
|
|
39
39
|
findSkillPathMatch,
|
|
40
40
|
parseAllSkillPromptSections,
|
|
41
41
|
resolveSkillPromptEntries,
|
|
42
|
-
} from "../src/skill-prompt-sanitizer
|
|
43
|
-
import { getPermissionSystemStatus } from "../src/status
|
|
44
|
-
import { sanitizeAvailableToolsSection } from "../src/system-prompt-sanitizer
|
|
42
|
+
} from "../src/skill-prompt-sanitizer";
|
|
43
|
+
import { getPermissionSystemStatus } from "../src/status";
|
|
44
|
+
import { sanitizeAvailableToolsSection } from "../src/system-prompt-sanitizer";
|
|
45
45
|
import {
|
|
46
46
|
checkRequestedToolRegistration,
|
|
47
47
|
getToolNameFromValue,
|
|
48
|
-
} from "../src/tool-registry
|
|
48
|
+
} from "../src/tool-registry";
|
|
49
49
|
import type {
|
|
50
50
|
AgentPermissions,
|
|
51
51
|
GlobalPermissionConfig,
|
|
52
52
|
PermissionState,
|
|
53
|
-
} from "../src/types
|
|
53
|
+
} from "../src/types";
|
|
54
54
|
import {
|
|
55
55
|
canResolveAskPermissionRequest,
|
|
56
56
|
shouldAutoApprovePermissionState,
|
|
57
|
-
} from "../src/yolo-mode
|
|
57
|
+
} from "../src/yolo-mode";
|
|
58
58
|
|
|
59
59
|
type CreateManagerOptions = {
|
|
60
60
|
mcpServerNames?: readonly string[];
|
|
@@ -2,10 +2,10 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
5
|
-
import { getGlobalConfigPath } from "../src/config-paths
|
|
6
|
-
import { DEFAULT_EXTENSION_CONFIG } from "../src/extension-config
|
|
7
|
-
import piPermissionSystemExtension from "../src/index
|
|
8
|
-
import type { GlobalPermissionConfig } from "../src/types
|
|
5
|
+
import { getGlobalConfigPath } from "../src/config-paths";
|
|
6
|
+
import { DEFAULT_EXTENSION_CONFIG } from "../src/extension-config";
|
|
7
|
+
import piPermissionSystemExtension from "../src/index";
|
|
8
|
+
import type { GlobalPermissionConfig } from "../src/types";
|
|
9
9
|
|
|
10
10
|
type MockHandler = (
|
|
11
11
|
event: Record<string, unknown>,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
-
import type { PermissionManager } from "../src/permission-manager
|
|
2
|
+
import type { PermissionManager } from "../src/permission-manager";
|
|
3
3
|
import {
|
|
4
4
|
findSkillPathMatch,
|
|
5
5
|
resolveSkillPromptEntries,
|
|
6
|
-
} from "../src/skill-prompt-sanitizer
|
|
7
|
-
import type { PermissionCheckResult } from "../src/types
|
|
6
|
+
} from "../src/skill-prompt-sanitizer";
|
|
7
|
+
import type { PermissionCheckResult } from "../src/types";
|
|
8
8
|
|
|
9
9
|
afterEach(() => {
|
|
10
10
|
vi.restoreAllMocks();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
3
|
-
import { SUBAGENT_ENV_HINT_KEYS } from "../src/permission-forwarding
|
|
3
|
+
import { SUBAGENT_ENV_HINT_KEYS } from "../src/permission-forwarding";
|
|
4
4
|
import {
|
|
5
5
|
isSubagentExecutionContext,
|
|
6
6
|
normalizeFilesystemPath,
|
|
7
|
-
} from "../src/subagent-context
|
|
7
|
+
} from "../src/subagent-context";
|
|
8
8
|
|
|
9
9
|
afterEach(() => {
|
|
10
10
|
vi.unstubAllEnvs();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
2
|
|
|
3
|
-
import { sanitizeAvailableToolsSection } from "../src/system-prompt-sanitizer
|
|
3
|
+
import { sanitizeAvailableToolsSection } from "../src/system-prompt-sanitizer";
|
|
4
4
|
|
|
5
5
|
afterEach(() => {
|
|
6
6
|
vi.restoreAllMocks();
|
|
@@ -32,7 +32,7 @@ describe("sanitizeAvailableToolsSection — Available tools section", () => {
|
|
|
32
32
|
|
|
33
33
|
// Bug #33: findSection extends to lines.length when no subsequent recognised
|
|
34
34
|
// header follows, so content after the last section is silently deleted.
|
|
35
|
-
test
|
|
35
|
+
test("preserves content that follows the Available tools section (bug #33)", () => {
|
|
36
36
|
const input = prompt(
|
|
37
37
|
availableToolsSection(["bash", "read"]),
|
|
38
38
|
"Other content",
|
|
@@ -184,3 +184,44 @@ describe("sanitizeAvailableToolsSection — multi-section prompt", () => {
|
|
|
184
184
|
expect(result.prompt).not.toMatch(/\n{3,}/);
|
|
185
185
|
});
|
|
186
186
|
});
|
|
187
|
+
|
|
188
|
+
describe("sanitizeAvailableToolsSection — findSection boundary edge cases", () => {
|
|
189
|
+
test("preserves content after Guidelines when Guidelines is the last recognised section", () => {
|
|
190
|
+
const input = prompt(
|
|
191
|
+
guidelinesSection(["use bash for file operations like ls, rg, find"]),
|
|
192
|
+
"Trailing custom instructions",
|
|
193
|
+
);
|
|
194
|
+
const result = sanitizeAvailableToolsSection(input, []);
|
|
195
|
+
expect(result.prompt).toContain("Trailing custom instructions");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("preserves trailing prose when both sections are removed", () => {
|
|
199
|
+
const input = prompt(
|
|
200
|
+
availableToolsSection(["bash"]),
|
|
201
|
+
guidelinesSection(["use bash for file operations like ls, rg, find"]),
|
|
202
|
+
"Important user note",
|
|
203
|
+
);
|
|
204
|
+
const result = sanitizeAvailableToolsSection(input, []);
|
|
205
|
+
expect(result.removed).toBe(true);
|
|
206
|
+
expect(result.prompt).not.toContain("Available tools:");
|
|
207
|
+
expect(result.prompt).not.toContain("Guidelines:");
|
|
208
|
+
expect(result.prompt).toContain("Important user note");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("section at EOF with no trailing content still works", () => {
|
|
212
|
+
const input = availableToolsSection(["bash", "read"]);
|
|
213
|
+
const result = sanitizeAvailableToolsSection(input, ["bash", "read"]);
|
|
214
|
+
expect(result.removed).toBe(true);
|
|
215
|
+
expect(result.prompt).toBe("");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("section followed by blank lines then prose — prose survives", () => {
|
|
219
|
+
const input = ["Available tools:", "- bash", "", "", "Custom note"].join(
|
|
220
|
+
"\n",
|
|
221
|
+
);
|
|
222
|
+
const result = sanitizeAvailableToolsSection(input, ["bash"]);
|
|
223
|
+
expect(result.removed).toBe(true);
|
|
224
|
+
expect(result.prompt).toContain("Custom note");
|
|
225
|
+
expect(result.prompt).not.toContain("Available tools:");
|
|
226
|
+
});
|
|
227
|
+
});
|
|
@@ -5,7 +5,7 @@ vi.mock("../src/logging.js", () => ({
|
|
|
5
5
|
safeJsonStringify: vi.fn((value: unknown) => JSON.stringify(value)),
|
|
6
6
|
}));
|
|
7
7
|
|
|
8
|
-
import { safeJsonStringify } from "../src/logging
|
|
8
|
+
import { safeJsonStringify } from "../src/logging";
|
|
9
9
|
import {
|
|
10
10
|
countTextLines,
|
|
11
11
|
formatCount,
|
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
TOOL_INPUT_PREVIEW_MAX_LENGTH,
|
|
25
25
|
TOOL_TEXT_SUMMARY_MAX_LENGTH,
|
|
26
26
|
truncateInlineText,
|
|
27
|
-
} from "../src/tool-input-preview
|
|
28
|
-
import type { PermissionCheckResult } from "../src/types
|
|
27
|
+
} from "../src/tool-input-preview";
|
|
28
|
+
import type { PermissionCheckResult } from "../src/types";
|
|
29
29
|
|
|
30
30
|
const mockedStringify = vi.mocked(safeJsonStringify);
|
|
31
31
|
|
package/tests/yolo-mode.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
2
|
-
import type { PermissionSystemExtensionConfig } from "../src/extension-config
|
|
2
|
+
import type { PermissionSystemExtensionConfig } from "../src/extension-config";
|
|
3
3
|
import {
|
|
4
4
|
canResolveAskPermissionRequest,
|
|
5
5
|
shouldAutoApprovePermissionState,
|
|
6
|
-
} from "../src/yolo-mode
|
|
6
|
+
} from "../src/yolo-mode";
|
|
7
7
|
|
|
8
8
|
afterEach(() => {
|
|
9
9
|
vi.restoreAllMocks();
|