@wyattjoh/demur 0.1.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/LICENSE +21 -0
- package/README.md +200 -0
- package/extensions/demur/index.ts +63 -0
- package/package.json +70 -0
- package/src/adapters/claude-code.ts +71 -0
- package/src/analyze.ts +621 -0
- package/src/cli.ts +41 -0
- package/src/guard.internal.ts +164 -0
- package/src/guard.ts +84 -0
- package/src/judge.ts +233 -0
- package/src/key.ts +45 -0
- package/src/policy.ts +215 -0
- package/src/questions.ts +70 -0
- package/src/state.ts +287 -0
- package/src/types.ts +153 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { CommandAnalysis } from "./analyze.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The coding agent that is about to run the command.
|
|
5
|
+
*/
|
|
6
|
+
export type Host = "pi" | "claude-code" | "cli";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Everything demur knows about a command before judging it.
|
|
10
|
+
*
|
|
11
|
+
* This is handed to the model verbatim as System One state, so every field is
|
|
12
|
+
* named the way we want the model to read it.
|
|
13
|
+
*/
|
|
14
|
+
export type CommandState = {
|
|
15
|
+
/**
|
|
16
|
+
* The exact shell command the agent is about to execute.
|
|
17
|
+
*/
|
|
18
|
+
command: string;
|
|
19
|
+
/**
|
|
20
|
+
* Absolute working directory the command will run in.
|
|
21
|
+
*/
|
|
22
|
+
cwd: string;
|
|
23
|
+
/**
|
|
24
|
+
* Which coding agent requested the command.
|
|
25
|
+
*/
|
|
26
|
+
agent: Host;
|
|
27
|
+
/**
|
|
28
|
+
* Git facts about `cwd`, or `undefined` when it is not a work tree.
|
|
29
|
+
*/
|
|
30
|
+
git: GitState | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Throwaway unique value included in the request, used only by the stability
|
|
33
|
+
* harness so repeated calls are sampled independently rather than served from
|
|
34
|
+
* a cache. `undefined` in normal operation.
|
|
35
|
+
*/
|
|
36
|
+
nonce: string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* What code could determine about the command without running it.
|
|
39
|
+
*
|
|
40
|
+
* Deterministic parsing the model should not have to do by eye: real program
|
|
41
|
+
* names, resolved paths, heredoc bodies, command substitutions.
|
|
42
|
+
*/
|
|
43
|
+
analysis: CommandAnalysis | undefined;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Git working-tree facts that change whether a command is actually destructive.
|
|
48
|
+
*
|
|
49
|
+
* `git reset --hard` destroys nothing in a clean tree; it destroys hours of
|
|
50
|
+
* work in a dirty one. Rules cannot see that difference, so we measure it.
|
|
51
|
+
*/
|
|
52
|
+
export type GitState = {
|
|
53
|
+
/**
|
|
54
|
+
* Absolute path to the repository root.
|
|
55
|
+
*/
|
|
56
|
+
root: string;
|
|
57
|
+
/**
|
|
58
|
+
* Current branch name, or `undefined` when detached.
|
|
59
|
+
*/
|
|
60
|
+
branch: string | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Number of files with staged or unstaged modifications.
|
|
63
|
+
*/
|
|
64
|
+
uncommittedFileCount: number;
|
|
65
|
+
/**
|
|
66
|
+
* Number of untracked files, which no git command can recover.
|
|
67
|
+
*/
|
|
68
|
+
untrackedFileCount: number;
|
|
69
|
+
/**
|
|
70
|
+
* Commits on the current branch not present on its upstream, which are lost
|
|
71
|
+
* if the branch is reset or force-deleted.
|
|
72
|
+
*/
|
|
73
|
+
unpushedCommitCount: number;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the branch has an upstream to recover from.
|
|
76
|
+
*/
|
|
77
|
+
hasUpstream: boolean;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The raw judgments returned for one command, before policy is applied.
|
|
82
|
+
*
|
|
83
|
+
* These are kept separate from the verdict so thresholds can change without
|
|
84
|
+
* re-running inference.
|
|
85
|
+
*/
|
|
86
|
+
export type Judgments = {
|
|
87
|
+
/**
|
|
88
|
+
* Probability the command actually performs a destructive operation, rather
|
|
89
|
+
* than merely containing destructive-looking text as data.
|
|
90
|
+
*/
|
|
91
|
+
executesDestruction: number;
|
|
92
|
+
/**
|
|
93
|
+
* Probability that what it destroys cannot be recovered.
|
|
94
|
+
*/
|
|
95
|
+
unrecoverable: number;
|
|
96
|
+
/**
|
|
97
|
+
* Probability it acts on shared, remote, or production systems.
|
|
98
|
+
*/
|
|
99
|
+
targetsSharedInfrastructure: number;
|
|
100
|
+
/**
|
|
101
|
+
* Expected blast radius from 0 (ephemeral) to 3 (remote or production).
|
|
102
|
+
*/
|
|
103
|
+
blastRadius: number;
|
|
104
|
+
/**
|
|
105
|
+
* Model confidence in `blastRadius`, from 0 to 1.
|
|
106
|
+
*/
|
|
107
|
+
blastRadiusConfidence: number;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* What demur decided to do with a command.
|
|
112
|
+
*/
|
|
113
|
+
export type Decision = "allow" | "ask" | "deny";
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Why demur could not reach a judgment, when it had to fall back.
|
|
117
|
+
*/
|
|
118
|
+
export type FailureKind =
|
|
119
|
+
| "no-api-key"
|
|
120
|
+
| "timeout"
|
|
121
|
+
| "api-error"
|
|
122
|
+
| "unexpected";
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A complete guard result, including the evidence behind it.
|
|
126
|
+
*/
|
|
127
|
+
export type Verdict = {
|
|
128
|
+
/**
|
|
129
|
+
* The action the host should take.
|
|
130
|
+
*/
|
|
131
|
+
decision: Decision;
|
|
132
|
+
/**
|
|
133
|
+
* Human-readable justification, shown to the agent and the user.
|
|
134
|
+
*/
|
|
135
|
+
reason: string;
|
|
136
|
+
/**
|
|
137
|
+
* The judgments the decision was derived from, or `undefined` when the guard
|
|
138
|
+
* failed closed without reaching the model.
|
|
139
|
+
*/
|
|
140
|
+
judgments: Judgments | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* Set when the decision came from a failure path rather than a judgment.
|
|
143
|
+
*/
|
|
144
|
+
failure: FailureKind | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Wall-clock milliseconds the guard took end to end.
|
|
147
|
+
*/
|
|
148
|
+
latencyMs: number;
|
|
149
|
+
/**
|
|
150
|
+
* Token usage for the judgment call, when one completed.
|
|
151
|
+
*/
|
|
152
|
+
usage: { inputTokens: number; outputTokens: number } | undefined;
|
|
153
|
+
};
|