@gotgenes/pi-permission-model-judge 1.1.1 → 1.1.2
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 +12 -0
- package/README.md +2 -0
- package/docs/configuration.md +1 -0
- package/package.json +1 -1
- package/src/typo-reviewer.ts +37 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.1.2](https://github.com/gotgenes/pi-packages/compare/pi-permission-model-judge-v1.1.1...pi-permission-model-judge-v1.1.2) (2026-07-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **pi-permission-model-judge:** review typo paths embedded in bash commands ([#630](https://github.com/gotgenes/pi-packages/issues/630)) ([782c83d](https://github.com/gotgenes/pi-packages/commit/782c83d36d5d279c4932be315b9e78aae43ccb26))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* **pi-permission-model-judge:** note bash-command paths are reviewed ([58a82e8](https://github.com/gotgenes/pi-packages/commit/58a82e8ca7a0df5552b767099004afc83e53be55))
|
|
14
|
+
|
|
3
15
|
## [1.1.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-model-judge-v1.1.0...pi-permission-model-judge-v1.1.1) (2026-07-22)
|
|
4
16
|
|
|
5
17
|
|
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ The reviewer runs a short, cheap decision on each ask and defers at the first mi
|
|
|
20
20
|
3. The path matches one of your `typoPatterns` (otherwise defer — no model call).
|
|
21
21
|
4. The model confirms the typo and returns a teaching reason (`deny`), or is unsure (`defer`).
|
|
22
22
|
|
|
23
|
+
The candidate path comes from a file tool's path argument (`read`/`edit`/`write`) or from an external path referenced inside a `bash` command — a typo path in `cat …/pi-permission-system/packages/pi-permission-system/README.md` is reviewed the same way as one passed to `read`.
|
|
24
|
+
|
|
23
25
|
It is fail-safe by construction: a missing model, invalid config, model timeout, unparseable reply, or an unsure verdict all resolve to `defer`.
|
|
24
26
|
Deferring means the ask falls through to the normal permission prompt — this extension only ever *removes* a hand-denial, never grants access (it emits no `allow`).
|
|
25
27
|
|
package/docs/configuration.md
CHANGED
|
@@ -55,6 +55,7 @@ The reviewer forces the model to call a single `report_verdict` tool, so the ver
|
|
|
55
55
|
### `typoPatterns`
|
|
56
56
|
|
|
57
57
|
Each string is compiled with `new RegExp(pattern)` (no flags) and tested against the candidate path.
|
|
58
|
+
The candidate path is a file tool's path argument (`read`/`edit`/`write`) or an external path referenced inside a `bash` command — both surfaces are matched the same way.
|
|
58
59
|
Only a path that matches at least one pattern is sent to the model — this is the cost gate that keeps the reviewer from calling the model on every ask.
|
|
59
60
|
An invalid regular expression is skipped and recorded in the debug log (`model_judge.invalid_patterns`).
|
|
60
61
|
An empty list (the default) matches nothing, so the reviewer defers everything.
|
package/package.json
CHANGED
package/src/typo-reviewer.ts
CHANGED
|
@@ -91,20 +91,29 @@ export function createTypoReviewer(
|
|
|
91
91
|
return { kind: "defer" };
|
|
92
92
|
}
|
|
93
93
|
const { requestId } = details;
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
94
|
+
const candidates = candidatePathsOf(details);
|
|
95
|
+
if (candidates.length === 0) {
|
|
96
96
|
log.debug(SHORT_CIRCUIT_EVENT, { requestId, reason: "no-path" });
|
|
97
97
|
return { kind: "defer" };
|
|
98
98
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
99
|
+
const compiled = compiledFor(config, log);
|
|
100
|
+
let matched: { path: string; matchedPattern: string } | undefined;
|
|
101
|
+
for (const candidate of candidates) {
|
|
102
|
+
const pattern = matchTypoPattern(candidate, compiled);
|
|
103
|
+
if (pattern !== undefined) {
|
|
104
|
+
matched = { path: candidate, matchedPattern: pattern };
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (matched === undefined) {
|
|
101
109
|
log.debug(SHORT_CIRCUIT_EVENT, {
|
|
102
110
|
requestId,
|
|
103
|
-
path,
|
|
111
|
+
path: candidates[0],
|
|
104
112
|
reason: "pattern-miss",
|
|
105
113
|
});
|
|
106
114
|
return { kind: "defer" };
|
|
107
115
|
}
|
|
116
|
+
const { path, matchedPattern } = matched;
|
|
108
117
|
|
|
109
118
|
const modelId = `${config.provider}/${config.model}`;
|
|
110
119
|
const base: DecisionBase = { requestId, path, matchedPattern, modelId };
|
|
@@ -177,9 +186,29 @@ function surfaceOf(details: PromptPermissionDetails): string | undefined {
|
|
|
177
186
|
return details.accessIntent?.surface ?? details.surface ?? undefined;
|
|
178
187
|
}
|
|
179
188
|
|
|
180
|
-
/**
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Candidate paths to test against the typo patterns, most authoritative first.
|
|
191
|
+
*
|
|
192
|
+
* `accessIntent.matchValues` is the raising gate's authoritative path alias set
|
|
193
|
+
* (absolute ∪ cwd-relative ∪ canonical) — present for both file-tool and
|
|
194
|
+
* `bash` external_directory asks, and the only place a `bash`-surfaced path
|
|
195
|
+
* appears (the gate sets `command`/`accessIntent`, never `path`). `path` and
|
|
196
|
+
* `value` are fallbacks for a details bag without `accessIntent` (an older
|
|
197
|
+
* forwarded request or a hand-built local ask). `value` is last because a
|
|
198
|
+
* forwarded `bash` ask's display value is the command string, not a path.
|
|
199
|
+
*/
|
|
200
|
+
function candidatePathsOf(details: PromptPermissionDetails): string[] {
|
|
201
|
+
const seen = new Set<string>();
|
|
202
|
+
for (const value of details.accessIntent?.matchValues ?? []) {
|
|
203
|
+
seen.add(value);
|
|
204
|
+
}
|
|
205
|
+
if (details.path !== undefined) {
|
|
206
|
+
seen.add(details.path);
|
|
207
|
+
}
|
|
208
|
+
if (details.value != null) {
|
|
209
|
+
seen.add(details.value);
|
|
210
|
+
}
|
|
211
|
+
return [...seen];
|
|
183
212
|
}
|
|
184
213
|
|
|
185
214
|
/**
|