iosm-cli 0.2.15 → 0.2.16

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.
Files changed (159) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +18 -1
  3. package/dist/cli/args.d.ts +2 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +16 -3
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-profiles.d.ts.map +1 -1
  8. package/dist/core/agent-profiles.js +3 -0
  9. package/dist/core/agent-profiles.js.map +1 -1
  10. package/dist/core/agent-session.d.ts +2 -0
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +26 -10
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/agent-teams.d.ts.map +1 -1
  15. package/dist/core/agent-teams.js +12 -9
  16. package/dist/core/agent-teams.js.map +1 -1
  17. package/dist/core/checkpoint/fs-checkpoint.d.ts +14 -0
  18. package/dist/core/checkpoint/fs-checkpoint.d.ts.map +1 -0
  19. package/dist/core/checkpoint/fs-checkpoint.js +211 -0
  20. package/dist/core/checkpoint/fs-checkpoint.js.map +1 -0
  21. package/dist/core/command-dispatcher.d.ts +2 -0
  22. package/dist/core/command-dispatcher.d.ts.map +1 -1
  23. package/dist/core/command-dispatcher.js +78 -13
  24. package/dist/core/command-dispatcher.js.map +1 -1
  25. package/dist/core/mcp/cli.d.ts.map +1 -1
  26. package/dist/core/mcp/cli.js +26 -0
  27. package/dist/core/mcp/cli.js.map +1 -1
  28. package/dist/core/mcp/config.d.ts.map +1 -1
  29. package/dist/core/mcp/config.js +55 -0
  30. package/dist/core/mcp/config.js.map +1 -1
  31. package/dist/core/mcp/index.d.ts +1 -1
  32. package/dist/core/mcp/index.d.ts.map +1 -1
  33. package/dist/core/mcp/index.js.map +1 -1
  34. package/dist/core/mcp/runtime.d.ts +3 -1
  35. package/dist/core/mcp/runtime.d.ts.map +1 -1
  36. package/dist/core/mcp/runtime.js +21 -2
  37. package/dist/core/mcp/runtime.js.map +1 -1
  38. package/dist/core/mcp/types.d.ts +30 -2
  39. package/dist/core/mcp/types.d.ts.map +1 -1
  40. package/dist/core/mcp/types.js.map +1 -1
  41. package/dist/core/package-manager.d.ts +10 -0
  42. package/dist/core/package-manager.d.ts.map +1 -1
  43. package/dist/core/package-manager.js +100 -2
  44. package/dist/core/package-manager.js.map +1 -1
  45. package/dist/core/policy/engine.d.ts +77 -0
  46. package/dist/core/policy/engine.d.ts.map +1 -0
  47. package/dist/core/policy/engine.js +614 -0
  48. package/dist/core/policy/engine.js.map +1 -0
  49. package/dist/core/policy/index.d.ts +2 -0
  50. package/dist/core/policy/index.d.ts.map +1 -0
  51. package/dist/core/policy/index.js +2 -0
  52. package/dist/core/policy/index.js.map +1 -0
  53. package/dist/core/sandbox/executor.d.ts +13 -0
  54. package/dist/core/sandbox/executor.d.ts.map +1 -0
  55. package/dist/core/sandbox/executor.js +64 -0
  56. package/dist/core/sandbox/executor.js.map +1 -0
  57. package/dist/core/sdk.d.ts +2 -2
  58. package/dist/core/sdk.d.ts.map +1 -1
  59. package/dist/core/sdk.js +3 -3
  60. package/dist/core/sdk.js.map +1 -1
  61. package/dist/core/security/index.d.ts +3 -0
  62. package/dist/core/security/index.d.ts.map +1 -0
  63. package/dist/core/security/index.js +3 -0
  64. package/dist/core/security/index.js.map +1 -0
  65. package/dist/core/security/source-security.d.ts +43 -0
  66. package/dist/core/security/source-security.d.ts.map +1 -0
  67. package/dist/core/security/source-security.js +94 -0
  68. package/dist/core/security/source-security.js.map +1 -0
  69. package/dist/core/security/trust-ledger.d.ts +24 -0
  70. package/dist/core/security/trust-ledger.d.ts.map +1 -0
  71. package/dist/core/security/trust-ledger.js +66 -0
  72. package/dist/core/security/trust-ledger.js.map +1 -0
  73. package/dist/core/session-manager.d.ts.map +1 -1
  74. package/dist/core/session-manager.js +128 -15
  75. package/dist/core/session-manager.js.map +1 -1
  76. package/dist/core/settings-manager.d.ts +6 -0
  77. package/dist/core/settings-manager.d.ts.map +1 -1
  78. package/dist/core/settings-manager.js +22 -1
  79. package/dist/core/settings-manager.js.map +1 -1
  80. package/dist/core/system-prompt.d.ts.map +1 -1
  81. package/dist/core/system-prompt.js +9 -2
  82. package/dist/core/system-prompt.js.map +1 -1
  83. package/dist/core/task-plan.d.ts +1 -0
  84. package/dist/core/task-plan.d.ts.map +1 -1
  85. package/dist/core/task-plan.js +103 -0
  86. package/dist/core/task-plan.js.map +1 -1
  87. package/dist/core/tools/apply-patch.d.ts +29 -0
  88. package/dist/core/tools/apply-patch.d.ts.map +1 -0
  89. package/dist/core/tools/apply-patch.js +167 -0
  90. package/dist/core/tools/apply-patch.js.map +1 -0
  91. package/dist/core/tools/bash.d.ts.map +1 -1
  92. package/dist/core/tools/bash.js +15 -1
  93. package/dist/core/tools/bash.js.map +1 -1
  94. package/dist/core/tools/git-common.d.ts.map +1 -1
  95. package/dist/core/tools/git-common.js +15 -1
  96. package/dist/core/tools/git-common.js.map +1 -1
  97. package/dist/core/tools/index.d.ts +20 -2
  98. package/dist/core/tools/index.d.ts.map +1 -1
  99. package/dist/core/tools/index.js +85 -25
  100. package/dist/core/tools/index.js.map +1 -1
  101. package/dist/core/tools/permissions.d.ts +16 -0
  102. package/dist/core/tools/permissions.d.ts.map +1 -1
  103. package/dist/core/tools/permissions.js +34 -1
  104. package/dist/core/tools/permissions.js.map +1 -1
  105. package/dist/core/tools/task.d.ts.map +1 -1
  106. package/dist/core/tools/task.js +68 -24
  107. package/dist/core/tools/task.js.map +1 -1
  108. package/dist/core/tools/tool-search.d.ts +24 -0
  109. package/dist/core/tools/tool-search.d.ts.map +1 -0
  110. package/dist/core/tools/tool-search.js +85 -0
  111. package/dist/core/tools/tool-search.js.map +1 -0
  112. package/dist/core/tools/tool-suggest.d.ts +18 -0
  113. package/dist/core/tools/tool-suggest.d.ts.map +1 -0
  114. package/dist/core/tools/tool-suggest.js +94 -0
  115. package/dist/core/tools/tool-suggest.js.map +1 -0
  116. package/dist/core/tools/verification-runner.d.ts.map +1 -1
  117. package/dist/core/tools/verification-runner.js +15 -1
  118. package/dist/core/tools/verification-runner.js.map +1 -1
  119. package/dist/core/unified-exec.d.ts +39 -0
  120. package/dist/core/unified-exec.d.ts.map +1 -0
  121. package/dist/core/unified-exec.js +286 -0
  122. package/dist/core/unified-exec.js.map +1 -0
  123. package/dist/main.d.ts.map +1 -1
  124. package/dist/main.js +93 -11
  125. package/dist/main.js.map +1 -1
  126. package/dist/modes/acp/acp-mode.d.ts +17 -0
  127. package/dist/modes/acp/acp-mode.d.ts.map +1 -0
  128. package/dist/modes/acp/acp-mode.js +352 -0
  129. package/dist/modes/acp/acp-mode.js.map +1 -0
  130. package/dist/modes/index.d.ts +2 -1
  131. package/dist/modes/index.d.ts.map +1 -1
  132. package/dist/modes/index.js +1 -0
  133. package/dist/modes/index.js.map +1 -1
  134. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  135. package/dist/modes/interactive/components/tool-execution.js +217 -0
  136. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  137. package/dist/modes/interactive/interactive-mode.d.ts +8 -0
  138. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  139. package/dist/modes/interactive/interactive-mode.js +159 -72
  140. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  141. package/dist/modes/rpc/rpc-client.d.ts +25 -1
  142. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-client.js +33 -0
  144. package/dist/modes/rpc/rpc-client.js.map +1 -1
  145. package/dist/modes/rpc/rpc-mode.d.ts +13 -1
  146. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-mode.js +189 -28
  148. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  149. package/dist/modes/rpc/rpc-types.d.ts +54 -1
  150. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  151. package/dist/modes/rpc/rpc-types.js.map +1 -1
  152. package/dist/utils/tools-manager.d.ts.map +1 -1
  153. package/dist/utils/tools-manager.js +96 -9
  154. package/dist/utils/tools-manager.js.map +1 -1
  155. package/docs/README.md +2 -0
  156. package/docs/acp-rpc-mapping.md +38 -0
  157. package/docs/configuration.generated.md +81 -0
  158. package/docs/configuration.md +7 -0
  159. package/package.json +4 -1
@@ -0,0 +1,77 @@
1
+ import type { SettingsManager } from "../settings-manager.js";
2
+ import type { ToolPermissionRequest } from "../tools/permissions.js";
3
+ export type PolicyEffect = "allow" | "deny" | "ask";
4
+ export type PolicyLayer = "admin" | "user" | "workspace" | "default" | "legacy";
5
+ export type PolicyToolSource = "builtin" | "extension" | "custom" | "mcp";
6
+ export interface PolicyRuleV2 {
7
+ id: string;
8
+ enabled: boolean;
9
+ effect: PolicyEffect;
10
+ priority: number;
11
+ tools: string[];
12
+ toolSource?: PolicyToolSource[];
13
+ mcpName?: string[];
14
+ profiles?: string[];
15
+ cwdGlob?: string[];
16
+ argsPattern?: string;
17
+ checker?: string;
18
+ legacyRule?: string;
19
+ }
20
+ export interface PolicySource {
21
+ layer: PolicyLayer;
22
+ path?: string;
23
+ rules: PolicyRuleV2[];
24
+ allowedSources: string[];
25
+ }
26
+ export interface PolicyEvaluationContext {
27
+ profile?: string;
28
+ }
29
+ export interface PolicyDecision {
30
+ effect: PolicyEffect;
31
+ matched: boolean;
32
+ reason: string;
33
+ rule?: PolicyRuleV2;
34
+ layer?: PolicyLayer;
35
+ }
36
+ export declare class PolicyEngineV2 {
37
+ private readonly cwd;
38
+ private readonly agentDir;
39
+ private readonly adminPolicyPath;
40
+ private readonly settingsManager;
41
+ private sources;
42
+ constructor(options: {
43
+ cwd: string;
44
+ agentDir: string;
45
+ settingsManager: SettingsManager;
46
+ adminPolicyPath?: string;
47
+ });
48
+ refresh(): void;
49
+ getSources(): PolicySource[];
50
+ getAllowedSourceHosts(): string[];
51
+ getLegacyRules(effect: "allow" | "deny"): string[];
52
+ setLegacyRules(effect: "allow" | "deny", rawRules: string[]): void;
53
+ addLegacyRule(effect: "allow" | "deny", rawRule: string): boolean;
54
+ removeLegacyRule(effect: "allow" | "deny", rawRule: string): boolean;
55
+ evaluate(request: ToolPermissionRequest, context?: PolicyEvaluationContext): PolicyDecision;
56
+ private matchesRule;
57
+ private matchesTools;
58
+ private matchesArgsPattern;
59
+ private matchesChecker;
60
+ private serializeInput;
61
+ private readMutablePolicy;
62
+ private writeMutablePolicy;
63
+ }
64
+ export interface PermissionEvaluationResult {
65
+ outcome: "allow" | "deny" | "ask";
66
+ decision: PolicyDecision;
67
+ reason: string;
68
+ }
69
+ export declare function evaluatePermissionWithPolicy(engine: PolicyEngineV2, request: ToolPermissionRequest, context: {
70
+ profile?: string;
71
+ runtimeMode: "interactive" | "rpc";
72
+ permissionMode: "ask" | "auto" | "yolo";
73
+ strictExtensionToolEnforcement?: boolean;
74
+ }): PermissionEvaluationResult;
75
+ export declare function buildLegacyRulePattern(rawRule: string): string;
76
+ export declare function legacyRuleToChecker(rawRule: string): string | undefined;
77
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/policy/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,614 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import TOML from "@iarna/toml";
5
+ import { minimatch } from "minimatch";
6
+ import { CONFIG_DIR_NAME } from "../../config.js";
7
+ const DEFAULT_ALLOWED_SOURCES = [
8
+ "registry.npmjs.org",
9
+ "npmjs.org",
10
+ "github.com",
11
+ "gitlab.com",
12
+ "bitbucket.org",
13
+ "api.github.com",
14
+ "raw.githubusercontent.com",
15
+ ];
16
+ function escapeRegExp(value) {
17
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18
+ }
19
+ function normalizeStringArray(value) {
20
+ if (!Array.isArray(value))
21
+ return [];
22
+ return value
23
+ .filter((item) => typeof item === "string")
24
+ .map((item) => item.trim())
25
+ .filter((item) => item.length > 0);
26
+ }
27
+ function toPolicyToolSourceArray(value) {
28
+ const values = normalizeStringArray(value)
29
+ .map((item) => item.toLowerCase())
30
+ .filter((item) => item === "builtin" || item === "extension" || item === "custom" || item === "mcp");
31
+ return values.length > 0 ? values : undefined;
32
+ }
33
+ function asPolicyEffect(value) {
34
+ if (typeof value !== "string")
35
+ return undefined;
36
+ if (value === "allow" || value === "deny" || value === "ask")
37
+ return value;
38
+ return undefined;
39
+ }
40
+ function asFiniteNumber(value, fallback) {
41
+ if (typeof value !== "number" || Number.isNaN(value))
42
+ return fallback;
43
+ return value;
44
+ }
45
+ function normalizeRule(raw, index) {
46
+ if (!raw || typeof raw !== "object")
47
+ return undefined;
48
+ const obj = raw;
49
+ const effect = asPolicyEffect(obj.effect);
50
+ if (!effect)
51
+ return undefined;
52
+ const idRaw = typeof obj.id === "string" ? obj.id.trim() : "";
53
+ const id = idRaw.length > 0 ? idRaw : `rule-${index + 1}`;
54
+ const tools = normalizeStringArray(obj.tools);
55
+ const normalizedTools = tools.length > 0 ? tools : ["*"];
56
+ const enabled = typeof obj.enabled === "boolean" ? obj.enabled : true;
57
+ const priority = Math.trunc(asFiniteNumber(obj.priority, 0));
58
+ const toolSource = toPolicyToolSourceArray(obj.tool_source);
59
+ const mcpName = normalizeStringArray(obj.mcp_name);
60
+ const profiles = normalizeStringArray(obj.profiles);
61
+ const cwdGlob = normalizeStringArray(obj.cwd_glob);
62
+ const argsPattern = typeof obj.args_pattern === "string" && obj.args_pattern.trim().length > 0 ? obj.args_pattern : undefined;
63
+ const checker = typeof obj.checker === "string" && obj.checker.trim().length > 0 ? obj.checker : undefined;
64
+ const legacyRule = typeof obj.legacy_rule === "string" && obj.legacy_rule.trim().length > 0 ? obj.legacy_rule.trim() : undefined;
65
+ return {
66
+ id,
67
+ enabled,
68
+ effect,
69
+ priority,
70
+ tools: normalizedTools,
71
+ toolSource,
72
+ mcpName: mcpName.length > 0 ? mcpName : undefined,
73
+ profiles: profiles.length > 0 ? profiles : undefined,
74
+ cwdGlob: cwdGlob.length > 0 ? cwdGlob : undefined,
75
+ argsPattern,
76
+ checker,
77
+ legacyRule,
78
+ };
79
+ }
80
+ function toTomlRule(rule) {
81
+ const serialized = {
82
+ id: rule.id,
83
+ enabled: rule.enabled,
84
+ effect: rule.effect,
85
+ priority: rule.priority,
86
+ tools: [...rule.tools],
87
+ };
88
+ if (rule.toolSource && rule.toolSource.length > 0)
89
+ serialized.tool_source = [...rule.toolSource];
90
+ if (rule.mcpName && rule.mcpName.length > 0)
91
+ serialized.mcp_name = [...rule.mcpName];
92
+ if (rule.profiles && rule.profiles.length > 0)
93
+ serialized.profiles = [...rule.profiles];
94
+ if (rule.cwdGlob && rule.cwdGlob.length > 0)
95
+ serialized.cwd_glob = [...rule.cwdGlob];
96
+ if (rule.argsPattern)
97
+ serialized.args_pattern = rule.argsPattern;
98
+ if (rule.checker)
99
+ serialized.checker = rule.checker;
100
+ if (rule.legacyRule)
101
+ serialized.legacy_rule = rule.legacyRule;
102
+ return serialized;
103
+ }
104
+ function parsePolicyToml(content) {
105
+ const parsed = TOML.parse(content);
106
+ const ruleEntries = Array.isArray(parsed.rules) ? parsed.rules : [];
107
+ const rules = [];
108
+ for (let index = 0; index < ruleEntries.length; index += 1) {
109
+ const normalized = normalizeRule(ruleEntries[index], index);
110
+ if (normalized) {
111
+ rules.push(normalized);
112
+ }
113
+ }
114
+ return {
115
+ rules,
116
+ allowedSources: normalizeStringArray(parsed.allowed_sources),
117
+ };
118
+ }
119
+ function buildLegacyPolicyRules(settingsManager) {
120
+ const rules = [];
121
+ for (const [index, raw] of settingsManager.getPermissionDenyRules().entries()) {
122
+ const normalized = raw.trim();
123
+ if (!normalized)
124
+ continue;
125
+ const [toolRaw, ...rest] = normalized.split(":");
126
+ const tool = toolRaw.trim() || "*";
127
+ const needle = rest.join(":").trim();
128
+ rules.push({
129
+ id: `legacy-deny-${index + 1}`,
130
+ enabled: true,
131
+ effect: "deny",
132
+ priority: 1000 - index,
133
+ tools: [tool],
134
+ checker: needle ? `summary_contains:${needle}` : undefined,
135
+ legacyRule: `${tool}:${needle}`,
136
+ });
137
+ }
138
+ for (const [index, raw] of settingsManager.getPermissionAllowRules().entries()) {
139
+ const normalized = raw.trim();
140
+ if (!normalized)
141
+ continue;
142
+ const [toolRaw, ...rest] = normalized.split(":");
143
+ const tool = toolRaw.trim() || "*";
144
+ const needle = rest.join(":").trim();
145
+ rules.push({
146
+ id: `legacy-allow-${index + 1}`,
147
+ enabled: true,
148
+ effect: "allow",
149
+ priority: 1000 - index,
150
+ tools: [tool],
151
+ checker: needle ? `summary_contains:${needle}` : undefined,
152
+ legacyRule: `${tool}:${needle}`,
153
+ });
154
+ }
155
+ return rules;
156
+ }
157
+ function loadPolicyFile(path) {
158
+ if (!existsSync(path)) {
159
+ return { rules: [], allowedSources: [] };
160
+ }
161
+ const content = readFileSync(path, "utf8");
162
+ return parsePolicyToml(content);
163
+ }
164
+ function normalizePolicyPath(path) {
165
+ if (path === "~")
166
+ return homedir();
167
+ if (path.startsWith("~/"))
168
+ return join(homedir(), path.slice(2));
169
+ return path;
170
+ }
171
+ function defaultAdminPolicyPath() {
172
+ if (process.platform === "win32") {
173
+ return "C:\\ProgramData\\iosm\\policy.toml";
174
+ }
175
+ return "/etc/iosm/policy.toml";
176
+ }
177
+ function buildDefaultRuleSet() {
178
+ return {
179
+ layer: "default",
180
+ rules: [],
181
+ allowedSources: [...DEFAULT_ALLOWED_SOURCES],
182
+ };
183
+ }
184
+ function getLayerRank(layer) {
185
+ switch (layer) {
186
+ case "admin":
187
+ return 0;
188
+ case "user":
189
+ return 1;
190
+ case "workspace":
191
+ return 2;
192
+ case "default":
193
+ return 3;
194
+ case "legacy":
195
+ return 4;
196
+ }
197
+ }
198
+ export class PolicyEngineV2 {
199
+ constructor(options) {
200
+ this.sources = [];
201
+ this.cwd = options.cwd;
202
+ this.agentDir = options.agentDir;
203
+ this.settingsManager = options.settingsManager;
204
+ this.adminPolicyPath = normalizePolicyPath(options.adminPolicyPath ?? defaultAdminPolicyPath());
205
+ this.refresh();
206
+ }
207
+ refresh() {
208
+ const userPath = join(this.agentDir, "policy.toml");
209
+ const workspacePath = join(this.cwd, CONFIG_DIR_NAME, "policy.toml");
210
+ const admin = loadPolicyFile(this.adminPolicyPath);
211
+ const user = loadPolicyFile(userPath);
212
+ const workspace = loadPolicyFile(workspacePath);
213
+ const legacyRules = buildLegacyPolicyRules(this.settingsManager);
214
+ this.sources = [
215
+ { layer: "admin", path: this.adminPolicyPath, rules: admin.rules, allowedSources: admin.allowedSources },
216
+ { layer: "user", path: userPath, rules: user.rules, allowedSources: user.allowedSources },
217
+ { layer: "workspace", path: workspacePath, rules: workspace.rules, allowedSources: workspace.allowedSources },
218
+ buildDefaultRuleSet(),
219
+ { layer: "legacy", rules: legacyRules, allowedSources: [] },
220
+ ];
221
+ }
222
+ getSources() {
223
+ return this.sources.map((source) => ({
224
+ layer: source.layer,
225
+ path: source.path,
226
+ rules: source.rules.map((rule) => ({ ...rule })),
227
+ allowedSources: [...source.allowedSources],
228
+ }));
229
+ }
230
+ getAllowedSourceHosts() {
231
+ const merged = new Set();
232
+ for (const source of this.sources) {
233
+ for (const host of source.allowedSources) {
234
+ merged.add(host.toLowerCase());
235
+ }
236
+ }
237
+ return [...merged];
238
+ }
239
+ getLegacyRules(effect) {
240
+ const values = [];
241
+ for (const source of this.sources) {
242
+ if (source.layer !== "user")
243
+ continue;
244
+ for (const rule of source.rules) {
245
+ if (rule.effect !== effect)
246
+ continue;
247
+ if (rule.legacyRule && rule.legacyRule.trim().length > 0) {
248
+ values.push(rule.legacyRule.trim());
249
+ }
250
+ }
251
+ }
252
+ return values;
253
+ }
254
+ setLegacyRules(effect, rawRules) {
255
+ const path = join(this.agentDir, "policy.toml");
256
+ const file = this.readMutablePolicy(path);
257
+ const keep = file.rules
258
+ .map((entry, index) => ({ entry, index }))
259
+ .filter(({ entry }) => {
260
+ const rule = normalizeRule(entry, 0);
261
+ if (!rule)
262
+ return true;
263
+ if (rule.effect !== effect)
264
+ return true;
265
+ return !rule.legacyRule;
266
+ })
267
+ .map(({ entry }) => entry);
268
+ const nextRules = [...keep];
269
+ let counter = 1;
270
+ for (const raw of rawRules) {
271
+ const normalized = raw.trim();
272
+ if (!normalized || !normalized.includes(":"))
273
+ continue;
274
+ const [toolRaw, ...rest] = normalized.split(":");
275
+ const tool = toolRaw.trim() || "*";
276
+ const needle = rest.join(":").trim();
277
+ const rule = {
278
+ id: `legacy-${effect}-${counter}`,
279
+ enabled: true,
280
+ effect,
281
+ priority: 1000 - counter,
282
+ tools: [tool],
283
+ checker: needle ? `summary_contains:${needle}` : undefined,
284
+ legacyRule: `${tool}:${needle}`,
285
+ };
286
+ nextRules.push(toTomlRule(rule));
287
+ counter += 1;
288
+ }
289
+ file.rules = nextRules;
290
+ this.writeMutablePolicy(path, file);
291
+ this.refresh();
292
+ }
293
+ addLegacyRule(effect, rawRule) {
294
+ const normalized = rawRule.trim();
295
+ if (!normalized || !normalized.includes(":"))
296
+ return false;
297
+ const existing = new Set(this.getLegacyRules(effect));
298
+ if (existing.has(normalized))
299
+ return false;
300
+ this.setLegacyRules(effect, [...existing, normalized]);
301
+ return true;
302
+ }
303
+ removeLegacyRule(effect, rawRule) {
304
+ const normalized = rawRule.trim();
305
+ if (!normalized)
306
+ return false;
307
+ const current = this.getLegacyRules(effect);
308
+ const next = current.filter((rule) => rule !== normalized);
309
+ if (next.length === current.length)
310
+ return false;
311
+ this.setLegacyRules(effect, next);
312
+ return true;
313
+ }
314
+ evaluate(request, context = {}) {
315
+ const decorated = [];
316
+ let order = 0;
317
+ for (const source of this.sources) {
318
+ for (const rule of source.rules) {
319
+ decorated.push({
320
+ layer: source.layer,
321
+ rule,
322
+ layerRank: getLayerRank(source.layer),
323
+ order: order++,
324
+ });
325
+ }
326
+ }
327
+ decorated.sort((a, b) => {
328
+ if (a.layerRank !== b.layerRank)
329
+ return a.layerRank - b.layerRank;
330
+ if (a.rule.priority !== b.rule.priority)
331
+ return b.rule.priority - a.rule.priority;
332
+ return a.order - b.order;
333
+ });
334
+ for (const entry of decorated) {
335
+ const rule = entry.rule;
336
+ if (!rule.enabled)
337
+ continue;
338
+ if (!this.matchesRule(rule, request, context))
339
+ continue;
340
+ return {
341
+ effect: rule.effect,
342
+ matched: true,
343
+ reason: `Matched policy rule ${rule.id} (${entry.layer})`,
344
+ rule,
345
+ layer: entry.layer,
346
+ };
347
+ }
348
+ return {
349
+ effect: "ask",
350
+ matched: false,
351
+ reason: "No policy rule matched.",
352
+ };
353
+ }
354
+ matchesRule(rule, request, context) {
355
+ if (!this.matchesTools(rule, request))
356
+ return false;
357
+ if (rule.toolSource && rule.toolSource.length > 0) {
358
+ const source = request.toolSource ?? "builtin";
359
+ if (!rule.toolSource.includes(source))
360
+ return false;
361
+ }
362
+ if (rule.mcpName && rule.mcpName.length > 0) {
363
+ const serverName = request.mcpServerName?.trim();
364
+ if (!serverName)
365
+ return false;
366
+ if (!rule.mcpName.some((candidate) => candidate === "*" || candidate === serverName))
367
+ return false;
368
+ }
369
+ if (rule.profiles && rule.profiles.length > 0) {
370
+ const profile = context.profile?.trim();
371
+ if (!profile)
372
+ return false;
373
+ if (!rule.profiles.some((candidate) => candidate === "*" || candidate === profile))
374
+ return false;
375
+ }
376
+ if (rule.cwdGlob && rule.cwdGlob.length > 0) {
377
+ if (!rule.cwdGlob.some((glob) => minimatch(request.cwd, glob, { dot: true })))
378
+ return false;
379
+ }
380
+ if (rule.argsPattern && !this.matchesArgsPattern(rule.argsPattern, request)) {
381
+ return false;
382
+ }
383
+ if (rule.checker && !this.matchesChecker(rule.checker, request)) {
384
+ return false;
385
+ }
386
+ return true;
387
+ }
388
+ matchesTools(rule, request) {
389
+ const toolCandidates = [request.toolName];
390
+ if (request.toolSource === "mcp") {
391
+ if (request.mcpToolName)
392
+ toolCandidates.push(request.mcpToolName);
393
+ }
394
+ return rule.tools.some((pattern) => {
395
+ if (pattern === "*")
396
+ return true;
397
+ return toolCandidates.some((toolName) => minimatch(toolName, pattern));
398
+ });
399
+ }
400
+ matchesArgsPattern(pattern, request) {
401
+ const haystack = `${request.summary}\n${this.serializeInput(request.input)}`;
402
+ try {
403
+ const regex = new RegExp(pattern, "i");
404
+ return regex.test(haystack);
405
+ }
406
+ catch {
407
+ return false;
408
+ }
409
+ }
410
+ matchesChecker(checker, request) {
411
+ const trimmed = checker.trim();
412
+ if (trimmed.length === 0)
413
+ return true;
414
+ if (trimmed.startsWith("summary_contains:")) {
415
+ const needle = trimmed.slice("summary_contains:".length).trim().toLowerCase();
416
+ if (!needle)
417
+ return true;
418
+ return request.summary.toLowerCase().includes(needle);
419
+ }
420
+ if (trimmed.startsWith("summary_regex:")) {
421
+ const pattern = trimmed.slice("summary_regex:".length).trim();
422
+ if (!pattern)
423
+ return true;
424
+ try {
425
+ return new RegExp(pattern, "i").test(request.summary);
426
+ }
427
+ catch {
428
+ return false;
429
+ }
430
+ }
431
+ if (trimmed.startsWith("legacy_substring:")) {
432
+ const needle = trimmed.slice("legacy_substring:".length).trim().toLowerCase();
433
+ return !needle || request.summary.toLowerCase().includes(needle);
434
+ }
435
+ if (trimmed.startsWith("tool_exact:")) {
436
+ const expected = trimmed.slice("tool_exact:".length).trim();
437
+ return !expected || request.toolName === expected;
438
+ }
439
+ if (trimmed.startsWith("args_regex:")) {
440
+ const pattern = trimmed.slice("args_regex:".length).trim();
441
+ try {
442
+ return new RegExp(pattern, "i").test(this.serializeInput(request.input));
443
+ }
444
+ catch {
445
+ return false;
446
+ }
447
+ }
448
+ return false;
449
+ }
450
+ serializeInput(input) {
451
+ try {
452
+ return JSON.stringify(input);
453
+ }
454
+ catch {
455
+ return "";
456
+ }
457
+ }
458
+ readMutablePolicy(path) {
459
+ if (!existsSync(path)) {
460
+ return {
461
+ version: 2,
462
+ allowed_sources: [...DEFAULT_ALLOWED_SOURCES],
463
+ rules: [],
464
+ };
465
+ }
466
+ const raw = readFileSync(path, "utf8");
467
+ const parsed = TOML.parse(raw);
468
+ const rules = Array.isArray(parsed.rules)
469
+ ? parsed.rules.filter((entry) => !!entry && typeof entry === "object")
470
+ : [];
471
+ return {
472
+ ...parsed,
473
+ version: typeof parsed.version === "number" ? parsed.version : 2,
474
+ allowed_sources: normalizeStringArray(parsed.allowed_sources),
475
+ rules,
476
+ };
477
+ }
478
+ writeMutablePolicy(path, file) {
479
+ const dir = dirname(path);
480
+ if (!existsSync(dir)) {
481
+ mkdirSync(dir, { recursive: true });
482
+ }
483
+ const content = TOML.stringify(file);
484
+ writeFileSync(path, content, "utf8");
485
+ }
486
+ }
487
+ const DANGEROUS_TOOL_NAMES = new Set(["bash", "edit", "write", "apply_patch", "git_write", "fs_ops", "db_run"]);
488
+ export function evaluatePermissionWithPolicy(engine, request, context) {
489
+ const policyDecision = engine.evaluate(request, { profile: context.profile });
490
+ const isMcpRequest = request.toolSource === "mcp";
491
+ const mcpApprovalMode = isMcpRequest ? (request.mcpApprovalMode ?? "auto") : "auto";
492
+ if (policyDecision.effect === "deny") {
493
+ return {
494
+ outcome: "deny",
495
+ decision: policyDecision,
496
+ reason: policyDecision.reason,
497
+ };
498
+ }
499
+ if (isMcpRequest && mcpApprovalMode === "prompt") {
500
+ return {
501
+ outcome: "ask",
502
+ decision: policyDecision,
503
+ reason: "MCP tool approval mode is prompt; confirmation required.",
504
+ };
505
+ }
506
+ if (isMcpRequest && mcpApprovalMode === "approve") {
507
+ if (!request.mcpServerTrusted) {
508
+ return {
509
+ outcome: "ask",
510
+ decision: policyDecision,
511
+ reason: "MCP server is untrusted. approvalMode=approve requires trusted server.",
512
+ };
513
+ }
514
+ return {
515
+ outcome: "allow",
516
+ decision: policyDecision,
517
+ reason: policyDecision.effect === "allow"
518
+ ? policyDecision.reason
519
+ : "MCP tool approval mode is approve on trusted server.",
520
+ };
521
+ }
522
+ if (policyDecision.effect === "allow") {
523
+ if (isMcpRequest) {
524
+ const explicitMcpRule = Boolean(policyDecision.rule?.mcpName && policyDecision.rule.mcpName.length > 0) &&
525
+ Boolean(policyDecision.rule?.tools && policyDecision.rule.tools.some((tool) => tool !== "*"));
526
+ if (!request.mcpServerTrusted) {
527
+ return {
528
+ outcome: "ask",
529
+ decision: policyDecision,
530
+ reason: "MCP server is untrusted. Explicit allow requires trusted server for bypass.",
531
+ };
532
+ }
533
+ if (!explicitMcpRule) {
534
+ return {
535
+ outcome: "ask",
536
+ decision: policyDecision,
537
+ reason: "MCP allow rule is not explicit (mcp_name + specific tool required).",
538
+ };
539
+ }
540
+ }
541
+ return {
542
+ outcome: "allow",
543
+ decision: policyDecision,
544
+ reason: policyDecision.reason,
545
+ };
546
+ }
547
+ if (isMcpRequest) {
548
+ return {
549
+ outcome: "ask",
550
+ decision: policyDecision,
551
+ reason: "MCP calls require explicit allow policy.",
552
+ };
553
+ }
554
+ if (context.strictExtensionToolEnforcement && request.toolSource === "extension" && context.permissionMode === "auto") {
555
+ if (request.requiredPermission === "read-only") {
556
+ return {
557
+ outcome: "allow",
558
+ decision: policyDecision,
559
+ reason: "Auto mode allows read-only extension tools.",
560
+ };
561
+ }
562
+ if (!request.requiredPermission) {
563
+ return {
564
+ outcome: "deny",
565
+ decision: policyDecision,
566
+ reason: "Extension tool lacks requiredPermission metadata.",
567
+ };
568
+ }
569
+ }
570
+ if (context.permissionMode === "yolo") {
571
+ return {
572
+ outcome: "allow",
573
+ decision: policyDecision,
574
+ reason: "Permission mode is yolo.",
575
+ };
576
+ }
577
+ if (context.permissionMode === "auto" &&
578
+ (request.toolName === "edit" || request.toolName === "write" || request.toolName === "apply_patch")) {
579
+ return {
580
+ outcome: "allow",
581
+ decision: policyDecision,
582
+ reason: "Auto mode allows edit/write/apply_patch.",
583
+ };
584
+ }
585
+ if (context.runtimeMode === "rpc" && !DANGEROUS_TOOL_NAMES.has(request.toolName)) {
586
+ return {
587
+ outcome: "allow",
588
+ decision: policyDecision,
589
+ reason: "RPC mode auto-approves non-dangerous tools.",
590
+ };
591
+ }
592
+ return {
593
+ outcome: "ask",
594
+ decision: policyDecision,
595
+ reason: "No policy allow/deny matched; confirmation required.",
596
+ };
597
+ }
598
+ export function buildLegacyRulePattern(rawRule) {
599
+ const normalized = rawRule.trim();
600
+ const [toolRaw, ...rest] = normalized.split(":");
601
+ const tool = toolRaw.trim() || "*";
602
+ const needle = rest.join(":").trim();
603
+ const checker = needle.length > 0 ? `summary_contains:${needle}` : "summary_contains:";
604
+ return `${tool}:${checker.replace(/^summary_contains:/, "")}`;
605
+ }
606
+ export function legacyRuleToChecker(rawRule) {
607
+ const normalized = rawRule.trim();
608
+ const [, ...rest] = normalized.split(":");
609
+ const needle = rest.join(":").trim();
610
+ if (!needle)
611
+ return undefined;
612
+ return `summary_contains:${escapeRegExp(needle)}`;
613
+ }
614
+ //# sourceMappingURL=engine.js.map