compact-agent 1.18.0 → 1.19.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/dist/execpolicy.d.ts +56 -0
- package/dist/execpolicy.js +209 -0
- package/dist/execpolicy.js.map +1 -0
- package/dist/permissions.js +23 -1
- package/dist/permissions.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execpolicy DSL — a lightweight command-intent allowlist that gates
|
|
3
|
+
* bash commands BEFORE the existing ask/auto/yolo permission flow.
|
|
4
|
+
*
|
|
5
|
+
* Inspired by codex-rs/execpolicy/. Three decisions per rule:
|
|
6
|
+
* allow — skip the permission prompt (treat as yolo for this cmd)
|
|
7
|
+
* prompt — force the prompt even in `auto` mode
|
|
8
|
+
* forbidden — block outright, don't even prompt; print reason
|
|
9
|
+
*
|
|
10
|
+
* First matching rule wins. If no rule matches, the original
|
|
11
|
+
* permission mode is honored (so the default is "no change to behavior").
|
|
12
|
+
*
|
|
13
|
+
* The DEFAULT_RULES below codify the most-common safety intents the
|
|
14
|
+
* Codex execpolicy ships, condensed to what's portable across Win/
|
|
15
|
+
* macOS/Linux. Users can override at runtime via /perm-policy and the
|
|
16
|
+
* config field `execpolicy` (TODO in v1.20).
|
|
17
|
+
*
|
|
18
|
+
* Real OS-native sandboxing (Seatbelt/landlock/ACL) is intentionally
|
|
19
|
+
* out of scope for this layer — it's an evening's work in TS for an
|
|
20
|
+
* intent gate, vs weeks for proper sandboxing. This is the audit's
|
|
21
|
+
* pragmatic recommendation.
|
|
22
|
+
*/
|
|
23
|
+
export type Decision = 'allow' | 'prompt' | 'forbidden';
|
|
24
|
+
export interface ExecRule {
|
|
25
|
+
/** Command prefix or regex. String values are treated as `^<value>`. */
|
|
26
|
+
pattern: RegExp | string;
|
|
27
|
+
decision: Decision;
|
|
28
|
+
/** Optional: only match if this also tests true */
|
|
29
|
+
match?: RegExp | string;
|
|
30
|
+
/** Optional: only match if this tests FALSE */
|
|
31
|
+
notMatch?: RegExp | string;
|
|
32
|
+
/** Shown to the user when decision != 'allow' */
|
|
33
|
+
reason?: string;
|
|
34
|
+
/** Human-readable rule name for logging / debugging */
|
|
35
|
+
id?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface PolicyResult {
|
|
38
|
+
decision: Decision;
|
|
39
|
+
reason?: string;
|
|
40
|
+
ruleId?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Built-in policy. Ordered: most-specific dangerous patterns first
|
|
44
|
+
* so they fire before broader category rules. Allows come last so
|
|
45
|
+
* a dangerous variant of "git" hits the prompt rule before the
|
|
46
|
+
* "git read ops" allow.
|
|
47
|
+
*/
|
|
48
|
+
export declare const DEFAULT_RULES: ExecRule[];
|
|
49
|
+
/**
|
|
50
|
+
* Evaluate a bash command against the policy. Returns the FIRST
|
|
51
|
+
* matching rule's decision. If nothing matches, returns
|
|
52
|
+
* { decision: 'prompt' } so the user's configured permission mode
|
|
53
|
+
* decides — i.e. a no-op for `auto` (non-destructive tools pass)
|
|
54
|
+
* and a single prompt for `ask`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function evaluateCommand(cmd: string, rules?: ExecRule[]): PolicyResult;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execpolicy DSL — a lightweight command-intent allowlist that gates
|
|
3
|
+
* bash commands BEFORE the existing ask/auto/yolo permission flow.
|
|
4
|
+
*
|
|
5
|
+
* Inspired by codex-rs/execpolicy/. Three decisions per rule:
|
|
6
|
+
* allow — skip the permission prompt (treat as yolo for this cmd)
|
|
7
|
+
* prompt — force the prompt even in `auto` mode
|
|
8
|
+
* forbidden — block outright, don't even prompt; print reason
|
|
9
|
+
*
|
|
10
|
+
* First matching rule wins. If no rule matches, the original
|
|
11
|
+
* permission mode is honored (so the default is "no change to behavior").
|
|
12
|
+
*
|
|
13
|
+
* The DEFAULT_RULES below codify the most-common safety intents the
|
|
14
|
+
* Codex execpolicy ships, condensed to what's portable across Win/
|
|
15
|
+
* macOS/Linux. Users can override at runtime via /perm-policy and the
|
|
16
|
+
* config field `execpolicy` (TODO in v1.20).
|
|
17
|
+
*
|
|
18
|
+
* Real OS-native sandboxing (Seatbelt/landlock/ACL) is intentionally
|
|
19
|
+
* out of scope for this layer — it's an evening's work in TS for an
|
|
20
|
+
* intent gate, vs weeks for proper sandboxing. This is the audit's
|
|
21
|
+
* pragmatic recommendation.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Built-in policy. Ordered: most-specific dangerous patterns first
|
|
25
|
+
* so they fire before broader category rules. Allows come last so
|
|
26
|
+
* a dangerous variant of "git" hits the prompt rule before the
|
|
27
|
+
* "git read ops" allow.
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_RULES = [
|
|
30
|
+
// ── FORBIDDEN (block outright) ────────────────────────
|
|
31
|
+
{
|
|
32
|
+
id: 'rm-rf-root',
|
|
33
|
+
pattern: /\brm\s/,
|
|
34
|
+
match: /-r[a-z]*f|-f[a-z]*r/,
|
|
35
|
+
notMatch: /^[\s\S]*?\srm\s.*\s\.\.?\/?$/, // allow rm -rf . or ..
|
|
36
|
+
decision: 'forbidden',
|
|
37
|
+
reason: 'rm with recursive force flag; require explicit user approval',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'rm-system-path',
|
|
41
|
+
pattern: /\brm\s.*\s(\/|C:\\?|\/usr|\/etc|\/bin|\/sbin|\/var|\/boot)\b/,
|
|
42
|
+
decision: 'forbidden',
|
|
43
|
+
reason: 'targeting a system path',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'shutdown',
|
|
47
|
+
pattern: /^(shutdown|reboot|halt|poweroff)\b/,
|
|
48
|
+
decision: 'forbidden',
|
|
49
|
+
reason: 'system shutdown / reboot',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'dd-disk',
|
|
53
|
+
pattern: /^dd\s/,
|
|
54
|
+
match: /of=\/dev\/(sd|nvme|hd|disk)/,
|
|
55
|
+
decision: 'forbidden',
|
|
56
|
+
reason: 'dd writing to raw disk device',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: 'format-disk',
|
|
60
|
+
pattern: /^(format|mkfs|diskpart)\b/,
|
|
61
|
+
decision: 'forbidden',
|
|
62
|
+
reason: 'disk formatting',
|
|
63
|
+
},
|
|
64
|
+
// ── PROMPT (force prompt even in auto mode) ───────────
|
|
65
|
+
{
|
|
66
|
+
id: 'sudo',
|
|
67
|
+
pattern: /^sudo\b/,
|
|
68
|
+
decision: 'prompt',
|
|
69
|
+
reason: 'requires elevated privileges',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'rm-recursive',
|
|
73
|
+
pattern: /\brm\s/,
|
|
74
|
+
match: /-r/,
|
|
75
|
+
decision: 'prompt',
|
|
76
|
+
reason: 'recursive removal',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'curl-pipe-shell',
|
|
80
|
+
pattern: /\bcurl\b/,
|
|
81
|
+
match: /\|\s*(sh|bash|zsh|fish)\b/,
|
|
82
|
+
decision: 'prompt',
|
|
83
|
+
reason: 'piping curl output directly to shell',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'wget-pipe-shell',
|
|
87
|
+
pattern: /\bwget\b/,
|
|
88
|
+
match: /\|\s*(sh|bash|zsh|fish)\b/,
|
|
89
|
+
decision: 'prompt',
|
|
90
|
+
reason: 'piping wget output directly to shell',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'git-force-push',
|
|
94
|
+
pattern: /^git\s+push\b/,
|
|
95
|
+
match: /--force(?!-with-lease)|--mirror|\+/,
|
|
96
|
+
decision: 'prompt',
|
|
97
|
+
reason: 'force-push (rewrites remote history)',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'git-reset-hard',
|
|
101
|
+
pattern: /^git\s+reset\b/,
|
|
102
|
+
match: /--hard/,
|
|
103
|
+
decision: 'prompt',
|
|
104
|
+
reason: 'hard reset discards uncommitted work',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'git-clean-force',
|
|
108
|
+
pattern: /^git\s+clean\b/,
|
|
109
|
+
match: /-f/,
|
|
110
|
+
decision: 'prompt',
|
|
111
|
+
reason: 'git clean -f deletes untracked files',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'npm-mutate-registry',
|
|
115
|
+
pattern: /^npm\s+(unpublish|deprecate)\b/,
|
|
116
|
+
decision: 'prompt',
|
|
117
|
+
reason: 'mutating the npm registry',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'pip-uninstall',
|
|
121
|
+
pattern: /^pip(3)?\s+uninstall\b/,
|
|
122
|
+
decision: 'prompt',
|
|
123
|
+
reason: 'pip uninstall',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'docker-rm-volume',
|
|
127
|
+
pattern: /^docker\s+(volume\s+rm|system\s+prune|rm\s+--force|kill)\b/,
|
|
128
|
+
decision: 'prompt',
|
|
129
|
+
reason: 'docker destructive operation',
|
|
130
|
+
},
|
|
131
|
+
// ── ALLOW (skip prompt entirely) ──────────────────────
|
|
132
|
+
// Pure read / inspection ops — safe to auto-approve even in `ask` mode
|
|
133
|
+
{ id: 'cat', pattern: /^cat\s/, decision: 'allow' },
|
|
134
|
+
{ id: 'less', pattern: /^less\s/, decision: 'allow' },
|
|
135
|
+
{ id: 'more', pattern: /^more\s/, decision: 'allow' },
|
|
136
|
+
{ id: 'head', pattern: /^head\s/, decision: 'allow' },
|
|
137
|
+
{ id: 'tail', pattern: /^tail\s/, decision: 'allow' },
|
|
138
|
+
{ id: 'ls', pattern: /^ls\b/, decision: 'allow' },
|
|
139
|
+
{ id: 'dir', pattern: /^dir\b/, decision: 'allow' },
|
|
140
|
+
{ id: 'pwd', pattern: /^pwd\s*$/, decision: 'allow' },
|
|
141
|
+
{ id: 'echo', pattern: /^echo\s/, decision: 'allow' },
|
|
142
|
+
{ id: 'which', pattern: /^which\s/, decision: 'allow' },
|
|
143
|
+
{ id: 'where', pattern: /^where\s/, decision: 'allow' },
|
|
144
|
+
{ id: 'whoami', pattern: /^whoami\s*$/, decision: 'allow' },
|
|
145
|
+
{ id: 'env', pattern: /^env\s*$|^env\s+\|\s/, decision: 'allow' },
|
|
146
|
+
{ id: 'date', pattern: /^date\s*$|^date\s+\+/, decision: 'allow' },
|
|
147
|
+
{ id: 'uname', pattern: /^uname\b/, decision: 'allow' },
|
|
148
|
+
{ id: 'file', pattern: /^file\s/, decision: 'allow' },
|
|
149
|
+
{ id: 'wc', pattern: /^wc\s/, decision: 'allow' },
|
|
150
|
+
{ id: 'stat', pattern: /^stat\s/, decision: 'allow' },
|
|
151
|
+
// Git read ops
|
|
152
|
+
{
|
|
153
|
+
id: 'git-read',
|
|
154
|
+
pattern: /^git\s+(status|log|diff|show|branch|tag|remote(\s+-v)?|stash\s+list|config\s+--get|reflog|describe|blame|rev-parse|ls-files)\b/,
|
|
155
|
+
decision: 'allow',
|
|
156
|
+
},
|
|
157
|
+
// npm/pnpm/yarn read ops
|
|
158
|
+
{
|
|
159
|
+
id: 'npm-read',
|
|
160
|
+
pattern: /^(npm|pnpm|yarn)\s+(list|ls|view|info|outdated|audit|why|exec|run\s+--list)\b/,
|
|
161
|
+
decision: 'allow',
|
|
162
|
+
},
|
|
163
|
+
// pip / python read ops
|
|
164
|
+
{
|
|
165
|
+
id: 'pip-read',
|
|
166
|
+
pattern: /^(pip|pip3)\s+(list|show|freeze|check|search)\b/,
|
|
167
|
+
decision: 'allow',
|
|
168
|
+
},
|
|
169
|
+
// Process listing
|
|
170
|
+
{
|
|
171
|
+
id: 'process-list',
|
|
172
|
+
pattern: /^(ps|top|htop|tasklist|jobs)\b/,
|
|
173
|
+
decision: 'allow',
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
function compileMatcher(p, anchor) {
|
|
177
|
+
if (p instanceof RegExp)
|
|
178
|
+
return p;
|
|
179
|
+
return new RegExp(anchor ? `^${p}` : p);
|
|
180
|
+
}
|
|
181
|
+
function applyRule(cmd, rule) {
|
|
182
|
+
if (!compileMatcher(rule.pattern, true).test(cmd))
|
|
183
|
+
return false;
|
|
184
|
+
if (rule.match && !compileMatcher(rule.match, false).test(cmd))
|
|
185
|
+
return false;
|
|
186
|
+
if (rule.notMatch && compileMatcher(rule.notMatch, false).test(cmd))
|
|
187
|
+
return false;
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Evaluate a bash command against the policy. Returns the FIRST
|
|
192
|
+
* matching rule's decision. If nothing matches, returns
|
|
193
|
+
* { decision: 'prompt' } so the user's configured permission mode
|
|
194
|
+
* decides — i.e. a no-op for `auto` (non-destructive tools pass)
|
|
195
|
+
* and a single prompt for `ask`.
|
|
196
|
+
*/
|
|
197
|
+
export function evaluateCommand(cmd, rules = DEFAULT_RULES) {
|
|
198
|
+
const c = cmd.trim();
|
|
199
|
+
if (!c)
|
|
200
|
+
return { decision: 'prompt' };
|
|
201
|
+
for (const r of rules) {
|
|
202
|
+
if (applyRule(c, r)) {
|
|
203
|
+
return { decision: r.decision, reason: r.reason, ruleId: r.id };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// No rule matched — defer to the normal permission flow
|
|
207
|
+
return { decision: 'prompt' };
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=execpolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execpolicy.js","sourceRoot":"","sources":["../src/execpolicy.ts"],"names":[],"mappings":"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"}
|
package/dist/permissions.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { saveConfig } from './config.js';
|
|
3
|
+
import { evaluateCommand } from './execpolicy.js';
|
|
3
4
|
/**
|
|
4
5
|
* Check if a tool call is allowed under the current permission mode.
|
|
5
6
|
* Returns true if allowed, false if denied.
|
|
@@ -11,7 +12,28 @@ import { saveConfig } from './config.js';
|
|
|
11
12
|
* @returns True if the tool call is allowed, false if denied
|
|
12
13
|
*/
|
|
13
14
|
export async function checkPermission(tool, input, config, rl) {
|
|
14
|
-
//
|
|
15
|
+
// ── Execpolicy intent-gate (runs BEFORE other checks) ──
|
|
16
|
+
// For bash commands, evaluate the static-policy DSL first. This lets
|
|
17
|
+
// us auto-approve obviously-safe ops (cat, ls, git log) AND block
|
|
18
|
+
// obviously-dangerous ones (rm -rf system path, shutdown) without
|
|
19
|
+
// burning a user prompt either way. Only fires for the `bash` tool.
|
|
20
|
+
if (tool.name === 'bash') {
|
|
21
|
+
const cmd = String(input.command || '');
|
|
22
|
+
const policy = evaluateCommand(cmd);
|
|
23
|
+
if (policy.decision === 'forbidden') {
|
|
24
|
+
console.log(chalk.red(`\n ✗ Blocked by execpolicy${policy.ruleId ? ` (${policy.ruleId})` : ''}: ${policy.reason || 'command not permitted'}`));
|
|
25
|
+
console.log(chalk.dim(` $ ${cmd.slice(0, 120)}`));
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (policy.decision === 'allow') {
|
|
29
|
+
// Skip the full permission flow — policy says this is safe
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// policy.decision === 'prompt' → fall through to the usual flow
|
|
33
|
+
// (alwaysAllowedTools check, mode check, ask if needed)
|
|
34
|
+
}
|
|
35
|
+
// yolo mode = everything allowed (execpolicy 'forbidden' still wins,
|
|
36
|
+
// applied above; allow + prompt both pass through yolo)
|
|
15
37
|
if (config.permissionMode === 'yolo')
|
|
16
38
|
return true;
|
|
17
39
|
// Read-only tools always allowed
|
package/dist/permissions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compact-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "A dense, feature-rich AI coding agent for the terminal. Built-in voice dictation (Whisper) + TTS readout (ElevenLabs) + screen-reader mode for blind / low-vision users. 80+ slash commands, 9 modes including Hermes self-improving loop, multi-agent orchestration, bundled everything-claude-code skills library, learning system, and observable LLM transport. Works with OpenRouter, OpenAI, Anthropic-compatible, Ollama, LM Studio, DeepSeek, or any OpenAI-compatible API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|