pi-jev-auto-mode 0.1.0 → 0.1.1
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 +9 -1
- package/README.md +9 -9
- package/SECURITY.md +2 -2
- package/docs/calibration.md +1 -1
- package/docs/design.md +6 -6
- package/docs/security.md +1 -1
- package/package.json +2 -2
- package/src/call.ts +2 -2
- package/src/decide.ts +1 -1
- package/src/extension.ts +10 -10
- package/src/jev/criteria.ts +1 -1
- package/src/jev/decide.ts +1 -1
- package/src/jev/engine.ts +9 -9
- package/src/jev/index.ts +1 -1
- package/src/jev/questions.ts +2 -2
- package/src/jev/state.ts +2 -2
- package/src/jev/types.ts +1 -1
- package/src/policy.ts +5 -5
- package/src/settings.ts +2 -2
- package/src/ui.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- Correct the product name. It is **Jev** — TypeSafe's System One model, spelled with a
|
|
6
|
+
capital J and lowercase `ev`, as in their announcement, FAQ, and the model id
|
|
7
|
+
`typesafe-ai/jev` — not "JEV". Fixed in the README, docs, source comments, and the package
|
|
8
|
+
description. The `0.1.0` metadata cannot be edited, so this release exists to carry the
|
|
9
|
+
corrected name. No behavioral change.
|
|
10
|
+
|
|
3
11
|
## 0.1.0
|
|
4
12
|
|
|
5
13
|
Initial release.
|
|
6
14
|
|
|
7
15
|
- Two-layer gate: a deterministic policy envelope (hard-deny, allow/deny patterns,
|
|
8
|
-
dangerous-command detection, protected paths) and a
|
|
16
|
+
dangerous-command detection, protected paths) and a Jev semantic layer that only sees
|
|
9
17
|
what the first layer escalated.
|
|
10
18
|
- Conditions carry a `mode` (`required` / `hazard`) and a `severity` (`hazard` / `soft`),
|
|
11
19
|
calibrated against measured probabilities from the real API (`docs/calibration.md`).
|
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# pi-jev-auto-mode
|
|
2
2
|
|
|
3
3
|
Auto mode for the [Pi coding agent](https://github.com/earendil-works/pi-mono) backed by
|
|
4
|
-
**
|
|
4
|
+
**Jev** (TypeSafe System One, a decision-only model). Pi has no built-in permission system,
|
|
5
5
|
so a gate either exists as an extension or it does not exist at all. This one judges
|
|
6
6
|
`bash`, `write`, and `edit` tool calls semantically and **fails closed** whenever a decision
|
|
7
7
|
cannot be made.
|
|
8
8
|
|
|
9
|
-
> **Status: milestones 1–3 are complete.** The deterministic envelope, the
|
|
9
|
+
> **Status: milestones 1–3 are complete.** The deterministic envelope, the Jev engine,
|
|
10
10
|
> real-API calibration, settings, policy notes, per-rule threshold tuning, and decision
|
|
11
11
|
> records are implemented and tested (157 tests, no network). See [`docs/design.md`](./docs/design.md) for
|
|
12
12
|
> the roadmap and [`docs/calibration.md`](./docs/calibration.md) for the measured
|
|
@@ -18,22 +18,22 @@ The gate has two layers, in this order:
|
|
|
18
18
|
|
|
19
19
|
1. **Deterministic policy** — hard-deny commands, your allow/deny patterns, dangerous-command
|
|
20
20
|
detection, protected paths. Hard-deny is never handed to the semantic layer.
|
|
21
|
-
2. **Semantic judgment (
|
|
21
|
+
2. **Semantic judgment (Jev)** — only the calls the first layer escalated.
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
hard-deny → block (never reaches
|
|
24
|
+
hard-deny → block (never reaches Jev)
|
|
25
25
|
your deny pattern → block
|
|
26
26
|
your allow pattern → allow
|
|
27
27
|
safe read-only command → run, no record
|
|
28
28
|
in-project write/edit → run, no record
|
|
29
|
-
everything else →
|
|
29
|
+
everything else → Jev: allow · block · confirm · block-if-undecidable
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
`rm -rf build` inside the repository is recognized as a scoped local deletion. A write to
|
|
33
33
|
`.env`, `.git/`, `~/.ssh`, `.pi/`, `.github/workflows/`, or `AGENTS.md` is escalated even when
|
|
34
34
|
the path is inside the working directory.
|
|
35
35
|
|
|
36
|
-
### How
|
|
36
|
+
### How Jev decides
|
|
37
37
|
|
|
38
38
|
Conditions are phrased so the safe state is "yes", and each one is classified by
|
|
39
39
|
`mode` and `severity`:
|
|
@@ -91,7 +91,7 @@ Packages are discovered in the [package gallery](https://pi.dev/packages) throug
|
|
|
91
91
|
pi --jev-auto-mode start with auto mode enabled
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
The semantic layer needs a [TypeSafe](https://typesafe.ai/) API key.
|
|
94
|
+
The semantic layer needs a [TypeSafe](https://typesafe.ai/) API key. Jev is early access, so an
|
|
95
95
|
account may be waitlisted; **the gate still works without one**, running in ask-only mode
|
|
96
96
|
(confirm in a UI, block without one) rather than silently allowing everything.
|
|
97
97
|
|
|
@@ -231,10 +231,10 @@ Layout:
|
|
|
231
231
|
The deterministic pattern catalogue is adapted from
|
|
232
232
|
[`@nilskluewer/pi-auto-permission-gate`](https://github.com/nilskluewer/pi-auto-permission-gate)
|
|
233
233
|
(MIT), and the three-layer structure (fast paths → hard rules → classifier) follows the same
|
|
234
|
-
extension and Qwen Code's Auto Mode. The
|
|
234
|
+
extension and Qwen Code's Auto Mode. The Jev design constraints (fail closed, two-sided
|
|
235
235
|
thresholds that keep the middle band meaningful, one request per judgment) come from measuring
|
|
236
236
|
the API directly — [`docs/calibration.md`](./docs/calibration.md) records the measurements and
|
|
237
|
-
the reasoning. Nothing here depends on a wrapper library: the
|
|
237
|
+
the reasoning. Nothing here depends on a wrapper library: the Jev layer is written against the
|
|
238
238
|
official SDK.
|
|
239
239
|
|
|
240
240
|
## License
|
package/SECURITY.md
CHANGED
|
@@ -6,7 +6,7 @@ This extension stands between a coding agent and your shell, so its own failure
|
|
|
6
6
|
|
|
7
7
|
Open a private security advisory on the repository, or an issue if the report does not need to
|
|
8
8
|
stay private. Please include the tool call, the recorded decision (`/jev-auto-mode` records
|
|
9
|
-
are expandable in the transcript), and the
|
|
9
|
+
are expandable in the transcript), and the Jev probabilities if you have them.
|
|
10
10
|
|
|
11
11
|
## What is in scope
|
|
12
12
|
|
|
@@ -23,7 +23,7 @@ The gate is two layers and the order is the enforcement:
|
|
|
23
23
|
1. A deterministic envelope (hard-deny, user rules, dangerous-command patterns, protected
|
|
24
24
|
paths). Hard-deny returns before the semantic layer is constructed, so a probabilistic
|
|
25
25
|
verdict can never resurrect it.
|
|
26
|
-
2.
|
|
26
|
+
2. Jev, which only sees calls the first layer escalated.
|
|
27
27
|
|
|
28
28
|
Anything that cannot be decided — no key, timeout, malformed response, a response missing an
|
|
29
29
|
answer, cancellation, an oversized request — blocks. See
|
package/docs/calibration.md
CHANGED
|
@@ -18,7 +18,7 @@ Thirteen fixtures, one request each (6–7 `noul` questions per request), first
|
|
|
18
18
|
2026-09-17 against `jev-latest`. Every condition's probability is printed, not just
|
|
19
19
|
the failing ones, because a threshold cannot be chosen without the passing values.
|
|
20
20
|
|
|
21
|
-
> **Caveat.** This is one sample per fixture.
|
|
21
|
+
> **Caveat.** This is one sample per fixture. Jev is probabilistic: reruns moved
|
|
22
22
|
> some conditions by ±0.05 (for example `local_scope` on the pytest fixture was
|
|
23
23
|
> 0.78 and 0.81 across runs, `path_not_protected` on the `.env` fixture 0.28 and
|
|
24
24
|
> 0.25). Treat the numbers as evidence about *bands*, not as exact values, and
|
package/docs/design.md
CHANGED
|
@@ -16,11 +16,11 @@ Two prior arts shaped the structure:
|
|
|
16
16
|
| [`@nilskluewer/pi-auto-permission-gate`](https://github.com/nilskluewer/pi-auto-permission-gate) (Pi extension) | hard-deny → user rules → classifier → confirmation → no-UI block; classifier failure is fail-closed; decisions recorded with `pi.appendEntry` (out of LLM context); user policy as a Markdown note; allow patterns disabled for shell control syntax |
|
|
17
17
|
| Qwen Code Auto Mode | three layers: deterministic fast paths and allow rules first, then a classifier; protected "persistence surfaces" (`package.json`, `.github/workflows/`, agent config) always go through the classifier even when the target is inside the workspace |
|
|
18
18
|
|
|
19
|
-
What is different here: the classifier is **
|
|
19
|
+
What is different here: the classifier is **Jev**, a decision-only model (unstructured state in,
|
|
20
20
|
typed decisions out). The call is cheap, has no tokens to inject through, and returns calibrated
|
|
21
21
|
probabilities rather than prose that has to be parsed.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Jev facts this design leans on:
|
|
24
24
|
|
|
25
25
|
- One request carries many `noul` questions; they are evaluated **in parallel and
|
|
26
26
|
independently**, so adding questions barely changes latency.
|
|
@@ -35,13 +35,13 @@ JEV facts this design leans on:
|
|
|
35
35
|
```
|
|
36
36
|
tool_call(bash | write | edit)
|
|
37
37
|
├─ 0. auto mode off / tool not gated → pass through
|
|
38
|
-
├─ 1. hard-deny (deterministic) → block, no
|
|
39
|
-
├─ 2. user disallow pattern → block, no
|
|
38
|
+
├─ 1. hard-deny (deterministic) → block, no Jev ┐
|
|
39
|
+
├─ 2. user disallow pattern → block, no Jev ├ Jev never sees these
|
|
40
40
|
├─ 3. user allow pattern → allow (recorded) ┘
|
|
41
41
|
├─ 4. read-only command, or user-declared safe command
|
|
42
42
|
│ → pass through (silent)
|
|
43
43
|
├─ 5. in-project write/edit, unprotected → pass through (silent)
|
|
44
|
-
└─ 6.
|
|
44
|
+
└─ 6. Jev: one request, all conditions
|
|
45
45
|
├ every `required` condition satisfied, no hazard rejected → allow
|
|
46
46
|
├ any `hazard` condition rejected → block
|
|
47
47
|
├ any `soft` condition rejected → block, unless the user's own request covers it
|
|
@@ -105,7 +105,7 @@ the published package:
|
|
|
105
105
|
|
|
106
106
|
- `scripts/calibrate.ts` sends the fixture set and prints every condition's probability.
|
|
107
107
|
- `scripts/e2e.ts` runs the same fixtures through the real gate path (deterministic layer,
|
|
108
|
-
real
|
|
108
|
+
real Jev, block/ask routing, records) and compares the decision against the expectation.
|
|
109
109
|
|
|
110
110
|
## Remaining work
|
|
111
111
|
|
package/docs/security.md
CHANGED
|
@@ -76,7 +76,7 @@ when it said `deny` or when no decision was available.
|
|
|
76
76
|
paths: a write there changes what the agent believes it was told.
|
|
77
77
|
- One dedicated condition (`prompt_injection_absent`) asks whether the call is trying to make
|
|
78
78
|
other state act as instructions.
|
|
79
|
-
- The gate never returns a
|
|
79
|
+
- The gate never returns a Jev rationale verbatim as a system-level instruction; a block
|
|
80
80
|
reason is a tool-call error string, which is the weakest channel it can use.
|
|
81
81
|
|
|
82
82
|
## What is sent to TypeSafe
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-jev-auto-mode",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Jev (TypeSafe System One) backed auto mode for the Pi coding agent: semantically auto-approves bash, write, and edit tool calls and fails closed when a decision cannot be made.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
7
7
|
"pi-extension",
|
package/src/call.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Turn a Pi tool call into the shape
|
|
2
|
+
* Turn a Pi tool call into the shape Jev will judge.
|
|
3
3
|
*
|
|
4
4
|
* What leaves this module is what a third party (TypeSafe) gets to see, so the
|
|
5
5
|
* boundary is explicit: the command text and the target path are sent, file
|
|
@@ -143,7 +143,7 @@ export const NO_POLICY_PLACEHOLDER = "(no user policy configured)";
|
|
|
143
143
|
export const NO_INTENT_PLACEHOLDER = "(no recent user message available)";
|
|
144
144
|
|
|
145
145
|
/**
|
|
146
|
-
* Assemble the
|
|
146
|
+
* Assemble the Jev request state.
|
|
147
147
|
*
|
|
148
148
|
* The user policy is `context` (session-scoped) so conditions can name it, and
|
|
149
149
|
* the call plus the user intent are `value` (per-call).
|
package/src/decide.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The gate itself (ordering, blocking, recording) is deterministic. Everything
|
|
5
5
|
* probabilistic sits behind `DecisionEngine`, so the extension can be tested
|
|
6
|
-
* without a network and so the
|
|
6
|
+
* without a network and so the Jev implementation can be swapped or disabled
|
|
7
7
|
* without touching the safety-critical path.
|
|
8
8
|
*/
|
|
9
9
|
|
package/src/extension.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Jev auto mode for the Pi coding agent.
|
|
3
3
|
*
|
|
4
4
|
* The gate has two layers, and the order matters:
|
|
5
5
|
*
|
|
6
6
|
* 1. A deterministic policy layer (hard-deny, user rules, dangerous-pattern
|
|
7
7
|
* detection, protected paths). Hard-deny is not negotiable.
|
|
8
|
-
* 2. A semantic layer (
|
|
8
|
+
* 2. A semantic layer (Jev) that only ever sees calls the deterministic layer
|
|
9
9
|
* decided to escalate, and whose "allow" can never resurrect a hard-denied
|
|
10
10
|
* call.
|
|
11
11
|
*
|
|
@@ -171,7 +171,7 @@ function permit(deps: DecisionDeps, input: RecordInput): undefined {
|
|
|
171
171
|
|
|
172
172
|
/** Wraps an engine rationale so the model gets an actionable reason, not a verdict. */
|
|
173
173
|
function blockReason(rationale: string): string {
|
|
174
|
-
return `
|
|
174
|
+
return `Jev auto mode blocked this tool call. ${rationale} Do not repeat the same call unchanged; change the approach or ask the user.`;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
/**
|
|
@@ -318,7 +318,7 @@ export async function evaluateToolCall(
|
|
|
318
318
|
}
|
|
319
319
|
|
|
320
320
|
const dialog = [
|
|
321
|
-
"
|
|
321
|
+
"Jev auto mode wants confirmation before this runs.",
|
|
322
322
|
"",
|
|
323
323
|
`Tool: ${call.tool}`,
|
|
324
324
|
...(call.command ? [call.command] : []),
|
|
@@ -338,7 +338,7 @@ export async function evaluateToolCall(
|
|
|
338
338
|
rationale: "The user declined the confirmation.",
|
|
339
339
|
evidence,
|
|
340
340
|
});
|
|
341
|
-
return { block: true, reason: "Blocked by the user at the
|
|
341
|
+
return { block: true, reason: "Blocked by the user at the Jev auto mode confirmation." };
|
|
342
342
|
}
|
|
343
343
|
|
|
344
344
|
return permit(deps, {
|
|
@@ -450,7 +450,7 @@ export function register(pi: ExtensionAPI, options: RegisterOptions = {}): void
|
|
|
450
450
|
};
|
|
451
451
|
|
|
452
452
|
pi.registerFlag(AUTO_MODE_FLAG, {
|
|
453
|
-
description: "Start with
|
|
453
|
+
description: "Start with Jev auto mode enabled",
|
|
454
454
|
type: "boolean",
|
|
455
455
|
default: false,
|
|
456
456
|
});
|
|
@@ -458,7 +458,7 @@ export function register(pi: ExtensionAPI, options: RegisterOptions = {}): void
|
|
|
458
458
|
registerDecisionEntryRenderer(pi);
|
|
459
459
|
|
|
460
460
|
pi.registerCommand(AUTO_MODE_COMMAND, {
|
|
461
|
-
description: "Show or change the
|
|
461
|
+
description: "Show or change the Jev auto mode settings",
|
|
462
462
|
getArgumentCompletions: (argumentPrefix) => {
|
|
463
463
|
const value = String(argumentPrefix ?? "");
|
|
464
464
|
const tokens = value.split(/\s+/).filter(Boolean);
|
|
@@ -513,7 +513,7 @@ export function register(pi: ExtensionAPI, options: RegisterOptions = {}): void
|
|
|
513
513
|
engineId: deps.engine.id,
|
|
514
514
|
scope: state.scope,
|
|
515
515
|
});
|
|
516
|
-
ctx.ui.notify(`
|
|
516
|
+
ctx.ui.notify(`Jev auto mode ${value === "on" ? "enabled" : "disabled"}.`, "info");
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
519
519
|
|
|
@@ -523,7 +523,7 @@ export function register(pi: ExtensionAPI, options: RegisterOptions = {}): void
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
if (value === "policy edit") {
|
|
526
|
-
const edited = await ctx.ui.editor("
|
|
526
|
+
const edited = await ctx.ui.editor("Jev auto mode policy", state.policyNotes || POLICY_HEADER);
|
|
527
527
|
if (edited === undefined) return;
|
|
528
528
|
await store.savePolicyNotes(edited);
|
|
529
529
|
state.policyNotes = await store.loadPolicyNotes();
|
|
@@ -533,7 +533,7 @@ export function register(pi: ExtensionAPI, options: RegisterOptions = {}): void
|
|
|
533
533
|
|
|
534
534
|
if (value === "policy clear") {
|
|
535
535
|
const confirmed = await ctx.ui.confirm(
|
|
536
|
-
"Clear
|
|
536
|
+
"Clear Jev auto mode policy notes?",
|
|
537
537
|
"The semantic layer will fall back to its built-in criteria.",
|
|
538
538
|
);
|
|
539
539
|
if (!confirmed) return;
|
package/src/jev/criteria.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared noul criteria.
|
|
3
3
|
*
|
|
4
|
-
* The default criteria matter more than they look.
|
|
4
|
+
* The default criteria matter more than they look. Jev is calibrated, and
|
|
5
5
|
* `noul` has no `confidence` field: the only signal is the probability. If the
|
|
6
6
|
* criteria leave the middle open, an ambiguous condition lands somewhere in the
|
|
7
7
|
* middle and the two-sided thresholds in `decide.ts` can route it to a human.
|
package/src/jev/decide.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* p <= 1 - t → rejected ("the opposite is as certain as a pass would be")
|
|
8
8
|
* in between → the middle band
|
|
9
9
|
*
|
|
10
|
-
* The middle band is not a bug to be squeezed out. Measured
|
|
10
|
+
* The middle band is not a bug to be squeezed out. Measured Jev answers sit at
|
|
11
11
|
* 0.98/0.02 for clear cases but also at 0.85–0.95 for conditions that are clear
|
|
12
12
|
* to a human and merely not certain to the model, so a single high bar would
|
|
13
13
|
* report almost everything as uncertain. What the middle band *means* depends on
|
package/src/jev/engine.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
2
|
+
* The Jev decision engine.
|
|
3
3
|
*
|
|
4
4
|
* One tool call in, one request out. All conditions for the call travel in the
|
|
5
|
-
* same request because
|
|
5
|
+
* same request because Jev answers them in parallel and independently, so the
|
|
6
6
|
* marginal cost of an extra condition is a few tokens rather than a round trip.
|
|
7
7
|
*
|
|
8
8
|
* Everything that can go wrong resolves to `unavailable`, and the caller turns
|
|
@@ -43,12 +43,12 @@ export interface JevEngineOptions {
|
|
|
43
43
|
export const DEFAULT_MAX_STATE_CHARACTERS = 120_000;
|
|
44
44
|
|
|
45
45
|
const UNAVAILABLE_TEXT: Record<string, string> = {
|
|
46
|
-
timeout: "the
|
|
47
|
-
network: "the
|
|
48
|
-
http: "the
|
|
49
|
-
malformed_response: "the
|
|
46
|
+
timeout: "the Jev request timed out",
|
|
47
|
+
network: "the Jev request could not reach the API",
|
|
48
|
+
http: "the Jev API returned an error status",
|
|
49
|
+
malformed_response: "the Jev response did not match the questions that were asked",
|
|
50
50
|
state_too_large: "the call description exceeded the request budget",
|
|
51
|
-
unknown: "the
|
|
51
|
+
unknown: "the Jev request failed for an unknown reason",
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
export function createJevEngine(options: JevEngineOptions): DecisionEngine {
|
|
@@ -99,7 +99,7 @@ export function createJevEngine(options: JevEngineOptions): DecisionEngine {
|
|
|
99
99
|
return {
|
|
100
100
|
verdict: "unavailable",
|
|
101
101
|
reason: result.reason,
|
|
102
|
-
rationale: UNAVAILABLE_TEXT[result.reason] ?? UNAVAILABLE_TEXT.unknown ?? "
|
|
102
|
+
rationale: UNAVAILABLE_TEXT[result.reason] ?? UNAVAILABLE_TEXT.unknown ?? "Jev could not decide.",
|
|
103
103
|
latencyMs,
|
|
104
104
|
};
|
|
105
105
|
}
|
|
@@ -109,7 +109,7 @@ export function createJevEngine(options: JevEngineOptions): DecisionEngine {
|
|
|
109
109
|
return {
|
|
110
110
|
verdict: "unavailable",
|
|
111
111
|
reason: parsed.reason,
|
|
112
|
-
rationale: UNAVAILABLE_TEXT[parsed.reason] ?? "The
|
|
112
|
+
rationale: UNAVAILABLE_TEXT[parsed.reason] ?? "The Jev response could not be used.",
|
|
113
113
|
latencyMs,
|
|
114
114
|
};
|
|
115
115
|
}
|
package/src/jev/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Jev layer: transport, question set, response validation, and the engine.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export { createJevEngine, DEFAULT_MAX_STATE_CHARACTERS, type JevEngineOptions, type ObservationMeta } from "./engine.ts";
|
package/src/jev/questions.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The question set.
|
|
3
3
|
*
|
|
4
|
-
* Two things learned from real
|
|
4
|
+
* Two things learned from real Jev answers (see `docs/calibration.md`) shaped
|
|
5
5
|
* this design:
|
|
6
6
|
*
|
|
7
7
|
* 1. **"Absence of a hazard" questions cluster between 0.75 and 0.98.** Asking
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* for". That is the `required` condition. Everything else answers "is a
|
|
16
16
|
* specific bad thing happening", and a clear "yes, it is" should block.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* Jev evaluates questions in parallel and independently and does not send the
|
|
19
19
|
* question keys to the model, so each instruction must stand alone.
|
|
20
20
|
*/
|
|
21
21
|
|
package/src/jev/state.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shapes shared with the
|
|
2
|
+
* Shapes shared with the Jev layer.
|
|
3
3
|
*
|
|
4
4
|
* These are types only. The transport, question set, and decision mapping live in
|
|
5
5
|
* this directory too, but they arrive in a later milestone.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/** A JSON value accepted by the
|
|
8
|
+
/** A JSON value accepted by the Jev `state` field. `Date` / `Map` are not included. */
|
|
9
9
|
export type JevJson = string | number | boolean | null | JevJson[] | { [key: string]: JevJson };
|
|
10
10
|
|
|
11
11
|
/**
|
package/src/jev/types.ts
CHANGED
package/src/policy.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Deterministic policy layer.
|
|
3
3
|
*
|
|
4
|
-
* Everything in this module runs before
|
|
4
|
+
* Everything in this module runs before Jev. Hard-deny rules are deliberate,
|
|
5
5
|
* non-overridable, and must never be reachable by a probabilistic decision: they
|
|
6
6
|
* are the floor that keeps a mis-calibrated semantic verdict from becoming an
|
|
7
7
|
* approved `rm -rf /`.
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
14
14
|
|
|
15
15
|
export interface CommandRuleConfig {
|
|
16
|
-
/** Shell-style `*` / `?` patterns that auto-approve without asking
|
|
16
|
+
/** Shell-style `*` / `?` patterns that auto-approve without asking Jev. */
|
|
17
17
|
readonly allowedCommands: readonly string[];
|
|
18
|
-
/** Shell-style patterns that block immediately, before
|
|
18
|
+
/** Shell-style patterns that block immediately, before Jev. */
|
|
19
19
|
readonly disallowedCommands: readonly string[];
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -196,7 +196,7 @@ const DANGEROUS_PATTERNS: readonly CommandPattern[] = [
|
|
|
196
196
|
];
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* Catastrophic targets. These are never handed to
|
|
199
|
+
* Catastrophic targets. These are never handed to Jev: a look-alike approval
|
|
200
200
|
* would be unsafe even when the surrounding conversation seems to ask for it.
|
|
201
201
|
*
|
|
202
202
|
* The list is deliberately small. Everything else belongs to the semantic layer,
|
|
@@ -437,7 +437,7 @@ export interface WriteTarget {
|
|
|
437
437
|
* Classify a write/edit target lexically.
|
|
438
438
|
*
|
|
439
439
|
* A symlink inside the working directory can still point outside it; resolving
|
|
440
|
-
* that needs a filesystem call and belongs to the
|
|
440
|
+
* that needs a filesystem call and belongs to the Jev layer's state building.
|
|
441
441
|
*/
|
|
442
442
|
export function classifyWriteTarget(
|
|
443
443
|
inputPath: string,
|
package/src/settings.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { dirname, join } from "node:path";
|
|
|
16
16
|
|
|
17
17
|
export interface JevAutoModeSettings {
|
|
18
18
|
readonly enabled: boolean;
|
|
19
|
-
/** Per-attempt
|
|
19
|
+
/** Per-attempt Jev timeout. Kept short: this is a gate, not a batch job. */
|
|
20
20
|
readonly timeoutMs: number;
|
|
21
21
|
/** Retries after the first attempt. */
|
|
22
22
|
readonly maxRetries: number;
|
|
@@ -226,7 +226,7 @@ export class JevAutoModeStore {
|
|
|
226
226
|
await writeFileAtomic(path, `${JSON.stringify(settings, null, 2)}\n`);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/** User-authored policy notes. Advisory input to
|
|
229
|
+
/** User-authored policy notes. Advisory input to Jev, never a hard rule. */
|
|
230
230
|
async loadPolicyNotes(): Promise<string> {
|
|
231
231
|
try {
|
|
232
232
|
return (await readFile(this.policyNotesPath(), "utf8")).slice(0, MAX_POLICY_NOTES_LENGTH);
|
package/src/ui.ts
CHANGED
|
@@ -60,7 +60,7 @@ export const USAGE_TEXT = [
|
|
|
60
60
|
].join("\n");
|
|
61
61
|
|
|
62
62
|
export const POLICY_HEADER = [
|
|
63
|
-
"#
|
|
63
|
+
"# Jev auto mode policy",
|
|
64
64
|
"",
|
|
65
65
|
"Free-form notes describing what this machine and these repositories allow.",
|
|
66
66
|
"They are reference material for the semantic judgment: they can justify an",
|