@cuylabs/agent-core 0.9.0 → 0.11.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/README.md +33 -17
- package/dist/chunk-2O4MCSQS.js +780 -0
- package/dist/chunk-2TTOLHBT.js +198 -0
- package/dist/chunk-5FMSGQVX.js +281 -0
- package/dist/chunk-5NVVNXPQ.js +288 -0
- package/dist/{chunk-EKR6PKXU.js → chunk-6HZBHFOL.js} +3 -3
- package/dist/chunk-CJI7PVS2.js +58 -0
- package/dist/{chunk-WKHDSSXG.js → chunk-CMYN2RCB.js} +146 -46
- package/dist/chunk-FII65CN7.js +117 -0
- package/dist/{chunk-UHCJEM2E.js → chunk-ICZ66572.js} +13 -6
- package/dist/chunk-KYLPMBHD.js +316 -0
- package/dist/chunk-MXAP4UG6.js +2956 -0
- package/dist/{chunk-4QFNWPIF.js → chunk-N3VX7FEE.js} +35 -2
- package/dist/{chunk-MAZ5DY5B.js → chunk-NDZWXCBZ.js} +213 -78
- package/dist/{chunk-MHKK374K.js → chunk-Q742PSH3.js} +11 -27
- package/dist/{chunk-WGZAPU6N.js → chunk-QAL3OMI3.js} +15 -1
- package/dist/{chunk-UDCZ673N.js → chunk-RN6WZEUF.js} +27 -23
- package/dist/{chunk-ZXAKHMWH.js → chunk-ROTGCYDW.js} +22 -84
- package/dist/chunk-SPBFQXOT.js +0 -0
- package/dist/chunk-SSFBF3US.js +602 -0
- package/dist/chunk-SZ2XBPTW.js +8 -0
- package/dist/chunk-T4UIX5D7.js +115 -0
- package/dist/{chunk-IYWQOJMQ.js → chunk-TIHPYVAJ.js} +34 -34
- package/dist/{chunk-RKEW5WXI.js → chunk-TOTDGK3P.js} +1 -1
- package/dist/chunk-V4RFNEET.js +563 -0
- package/dist/chunk-VOUEJSW6.js +0 -0
- package/dist/{chunk-J4QDGZIA.js → chunk-WBPOZ7CL.js} +659 -275
- package/dist/chunk-X4VN4GIJ.js +185 -0
- package/dist/dispatch/index.d.ts +93 -0
- package/dist/dispatch/index.js +37 -0
- package/dist/events/index.d.ts +93 -0
- package/dist/events/index.js +6 -0
- package/dist/{runtime → execution}/index.d.ts +120 -35
- package/dist/{runtime → execution}/index.js +17 -11
- package/dist/index.d.ts +489 -115
- package/dist/index.js +1665 -462
- package/dist/inference/errors/index.js +1 -1
- package/dist/inference/index.d.ts +13 -21
- package/dist/inference/index.js +15 -12
- package/dist/instance-DzPiv6EK.d.ts +5723 -0
- package/dist/logger/index.d.ts +50 -0
- package/dist/logger/index.js +11 -0
- package/dist/mcp/index.d.ts +5 -9
- package/dist/mcp/index.js +2 -3
- package/dist/middleware/index.d.ts +10 -150
- package/dist/middleware/index.js +10 -2
- package/dist/model-messages-CJfwfzGe.d.ts +13 -0
- package/dist/models/index.d.ts +5 -2
- package/dist/models/index.js +2 -1
- package/dist/models/reasoning/index.js +2 -1
- package/dist/plugin/index.d.ts +55 -11
- package/dist/plugin/index.js +1 -1
- package/dist/profiles/index.d.ts +55 -0
- package/dist/{presets → profiles}/index.js +10 -10
- package/dist/prompt/index.d.ts +8 -13
- package/dist/safety/index.d.ts +109 -14
- package/dist/safety/index.js +59 -3
- package/dist/sandbox/index.d.ts +81 -0
- package/dist/sandbox/index.js +1 -0
- package/dist/skill/index.d.ts +10 -8
- package/dist/skill/index.js +2 -2
- package/dist/storage/index.d.ts +12 -4
- package/dist/storage/index.js +1 -1
- package/dist/subagents/index.d.ts +177 -0
- package/dist/subagents/index.js +78 -0
- package/dist/team/index.d.ts +544 -0
- package/dist/team/index.js +41 -0
- package/dist/tool/host/index.d.ts +41 -0
- package/dist/tool/host/index.js +10 -0
- package/dist/tool/index.d.ts +111 -21
- package/dist/tool/index.js +20 -13
- package/dist/{types-VQgymC1N.d.ts → types-Bj_J8u_W.d.ts} +44 -64
- package/dist/{types-CHiPh8U2.d.ts → types-C_LCeYNg.d.ts} +7 -7
- package/dist/types-RSCv7nQ4.d.ts +59 -0
- package/package.json +46 -47
- package/dist/builder-BgZ_j4Vs.d.ts +0 -35
- package/dist/chunk-5ARZJWD2.js +0 -259
- package/dist/chunk-DXFBQMXP.js +0 -53
- package/dist/chunk-H3FUYU52.js +0 -81
- package/dist/chunk-JLXG2SH7.js +0 -905
- package/dist/chunk-N7P4PN3O.js +0 -84
- package/dist/chunk-OFDKHNCX.js +0 -727
- package/dist/chunk-SDSBEQXG.js +0 -157
- package/dist/chunk-VEKUXUVF.js +0 -41
- package/dist/chunk-VNQBHPCT.js +0 -398
- package/dist/chunk-WWYYNWEW.js +0 -259
- package/dist/context/index.d.ts +0 -259
- package/dist/context/index.js +0 -26
- package/dist/events-CE72w8W4.d.ts +0 -149
- package/dist/host/index.d.ts +0 -45
- package/dist/host/index.js +0 -8
- package/dist/index-DQuTZ8xL.d.ts +0 -1335
- package/dist/messages-BYWGn8TY.d.ts +0 -110
- package/dist/presets/index.d.ts +0 -53
- package/dist/registry-DwYqsQkX.d.ts +0 -164
- package/dist/runner-CI-XeR16.d.ts +0 -91
- package/dist/scope/index.d.ts +0 -10
- package/dist/scope/index.js +0 -14
- package/dist/session-manager-KbYt2WUh.d.ts +0 -282
- package/dist/signal/index.d.ts +0 -28
- package/dist/signal/index.js +0 -6
- package/dist/sub-agent/index.d.ts +0 -24
- package/dist/sub-agent/index.js +0 -32
- package/dist/tool-CZWN3KbO.d.ts +0 -141
- package/dist/tool-DkhSCV2Y.d.ts +0 -145
- package/dist/tracker-DClqYqTj.d.ts +0 -96
- package/dist/tracking/index.d.ts +0 -111
- package/dist/tracking/index.js +0 -20
- package/dist/types-BfNpU8NS.d.ts +0 -270
- package/dist/types-BlOKk-Bb.d.ts +0 -330
- package/dist/types-BlZwmnuW.d.ts +0 -50
- package/dist/types-CQL-SvTn.d.ts +0 -29
- package/dist/types-CWm-7rvB.d.ts +0 -55
- package/dist/types-DTSkxakL.d.ts +0 -651
- package/dist/types-DmDwi2zI.d.ts +0 -339
- package/dist/types-YuWV4ag7.d.ts +0 -72
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import {
|
|
2
|
+
describeApprovalOperation,
|
|
3
|
+
extractApprovalPatterns,
|
|
4
|
+
matchApprovalPattern,
|
|
5
|
+
resolveCapability
|
|
6
|
+
} from "./chunk-FII65CN7.js";
|
|
7
|
+
|
|
8
|
+
// src/safety/risk.ts
|
|
9
|
+
var DEFAULT_TOOL_RISKS = {
|
|
10
|
+
read: "safe",
|
|
11
|
+
read_file: "safe",
|
|
12
|
+
grep: "safe",
|
|
13
|
+
glob: "safe",
|
|
14
|
+
list_dir: "safe",
|
|
15
|
+
plan: "safe",
|
|
16
|
+
invoke_agent: "safe",
|
|
17
|
+
wait_agent: "safe",
|
|
18
|
+
close_agent: "safe",
|
|
19
|
+
skill: "safe",
|
|
20
|
+
skill_resource: "safe",
|
|
21
|
+
write: "moderate",
|
|
22
|
+
write_file: "moderate",
|
|
23
|
+
edit: "moderate",
|
|
24
|
+
edit_file: "moderate",
|
|
25
|
+
create_file: "moderate",
|
|
26
|
+
bash: "dangerous",
|
|
27
|
+
shell: "dangerous",
|
|
28
|
+
delete_file: "dangerous",
|
|
29
|
+
remove: "dangerous"
|
|
30
|
+
};
|
|
31
|
+
function getToolRisk(tool, customRisks, declaredRisk) {
|
|
32
|
+
if (customRisks?.[tool]) {
|
|
33
|
+
return customRisks[tool];
|
|
34
|
+
}
|
|
35
|
+
if (declaredRisk) {
|
|
36
|
+
return declaredRisk;
|
|
37
|
+
}
|
|
38
|
+
if (DEFAULT_TOOL_RISKS[tool]) {
|
|
39
|
+
return DEFAULT_TOOL_RISKS[tool];
|
|
40
|
+
}
|
|
41
|
+
return "moderate";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/safety/policy.ts
|
|
45
|
+
var DEFAULT_CASCADE_POLICY = {
|
|
46
|
+
remember: "matching-pending",
|
|
47
|
+
deny: "matching-pending"
|
|
48
|
+
};
|
|
49
|
+
var DEFAULT_REMEMBER_SCOPE = "session";
|
|
50
|
+
function createConditionalApprovalRule(rule, when) {
|
|
51
|
+
return {
|
|
52
|
+
...rule,
|
|
53
|
+
when
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function allApprovalConditions(...conditions) {
|
|
57
|
+
return (context) => conditions.every((condition) => condition(context));
|
|
58
|
+
}
|
|
59
|
+
function anyApprovalConditions(...conditions) {
|
|
60
|
+
return (context) => conditions.some((condition) => condition(context));
|
|
61
|
+
}
|
|
62
|
+
function matchApprovalSessions(...sessionIds) {
|
|
63
|
+
const allowed = new Set(sessionIds.map((sessionId) => sessionId.trim()).filter(Boolean));
|
|
64
|
+
return (context) => allowed.has(context.sessionId);
|
|
65
|
+
}
|
|
66
|
+
function matchApprovalRisks(...risks) {
|
|
67
|
+
const allowed = new Set(risks);
|
|
68
|
+
return (context) => allowed.has(context.risk);
|
|
69
|
+
}
|
|
70
|
+
function getPathValue(input, path) {
|
|
71
|
+
const segments = path.split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
72
|
+
let current = input;
|
|
73
|
+
for (const segment of segments) {
|
|
74
|
+
if (!current || typeof current !== "object" || !(segment in current)) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
current = current[segment];
|
|
78
|
+
}
|
|
79
|
+
return current;
|
|
80
|
+
}
|
|
81
|
+
function matchApprovalArgValue(path, predicate) {
|
|
82
|
+
return (context) => predicate(getPathValue(context.args, path), context);
|
|
83
|
+
}
|
|
84
|
+
function matchApprovalNumericArg(path, thresholds) {
|
|
85
|
+
return matchApprovalArgValue(path, (value) => {
|
|
86
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
if (thresholds.gt !== void 0 && !(value > thresholds.gt)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (thresholds.gte !== void 0 && !(value >= thresholds.gte)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (thresholds.lt !== void 0 && !(value < thresholds.lt)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (thresholds.lte !== void 0 && !(value <= thresholds.lte)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function matchApprovalStringPrefixes(path, ...prefixes) {
|
|
105
|
+
const normalized = prefixes.map((prefix) => prefix.trim()).filter(Boolean);
|
|
106
|
+
return matchApprovalArgValue(path, (value) => {
|
|
107
|
+
if (typeof value !== "string") {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const lower = value.toLowerCase();
|
|
111
|
+
return normalized.some((prefix) => lower.startsWith(prefix.toLowerCase()));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function normalizeApprovalCascadePolicy(input) {
|
|
115
|
+
return {
|
|
116
|
+
remember: input?.remember ?? DEFAULT_CASCADE_POLICY.remember,
|
|
117
|
+
deny: input?.deny ?? DEFAULT_CASCADE_POLICY.deny
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function normalizeRememberScopes(scopes) {
|
|
121
|
+
const allowed = /* @__PURE__ */ new Set(["session", "project", "user"]);
|
|
122
|
+
const normalized = (scopes ?? [DEFAULT_REMEMBER_SCOPE]).filter(
|
|
123
|
+
(scope) => allowed.has(scope)
|
|
124
|
+
);
|
|
125
|
+
return normalized.length > 0 ? [...new Set(normalized)] : [DEFAULT_REMEMBER_SCOPE];
|
|
126
|
+
}
|
|
127
|
+
function selectRememberScope(decision, request, fallback = DEFAULT_REMEMBER_SCOPE) {
|
|
128
|
+
const allowed = normalizeRememberScopes(
|
|
129
|
+
request.rememberScopes ?? [request.defaultRememberScope ?? fallback]
|
|
130
|
+
);
|
|
131
|
+
const requested = decision.rememberScope ?? request.defaultRememberScope ?? fallback;
|
|
132
|
+
return allowed.includes(requested) ? requested : allowed[0] ?? fallback;
|
|
133
|
+
}
|
|
134
|
+
function createRememberedApprovalRules(request, source = request.defaultRememberScope ?? DEFAULT_REMEMBER_SCOPE) {
|
|
135
|
+
const scopeCondition = source === "session" ? matchApprovalSessions(request.sessionId) : void 0;
|
|
136
|
+
return [...new Set(request.patterns)].map((pattern) => ({
|
|
137
|
+
pattern,
|
|
138
|
+
tool: request.tool,
|
|
139
|
+
action: "allow",
|
|
140
|
+
source,
|
|
141
|
+
...scopeCondition ? { when: scopeCondition } : {}
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
function requestsSharePatterns(left, right) {
|
|
145
|
+
return left.some(
|
|
146
|
+
(leftPattern) => right.some(
|
|
147
|
+
(rightPattern) => matchApprovalPattern(leftPattern, rightPattern) || matchApprovalPattern(rightPattern, leftPattern)
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function approvalRequestsOverlap(left, right) {
|
|
152
|
+
if (left.sessionId !== right.sessionId) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (left.tool !== right.tool) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return requestsSharePatterns(left.patterns, right.patterns);
|
|
159
|
+
}
|
|
160
|
+
function shouldCascadeApprovalDecision(resolved, candidate, decision) {
|
|
161
|
+
if (resolved.id === candidate.id) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const cascade = normalizeApprovalCascadePolicy(resolved.cascade);
|
|
165
|
+
const mode = decision.action === "remember" ? cascade.remember : decision.action === "deny" ? cascade.deny : "none";
|
|
166
|
+
if (mode === "none") {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return approvalRequestsOverlap(resolved, candidate);
|
|
170
|
+
}
|
|
171
|
+
function formatApprovalDeniedReason(tool, detail) {
|
|
172
|
+
const trimmed = detail?.trim();
|
|
173
|
+
return trimmed ? `Approval denied for ${tool}: ${trimmed}` : `Tool call denied: ${tool}`;
|
|
174
|
+
}
|
|
175
|
+
function createApprovalCorrection(tool, detail) {
|
|
176
|
+
const reason = formatApprovalDeniedReason(tool, detail);
|
|
177
|
+
const trimmed = detail?.trim();
|
|
178
|
+
return {
|
|
179
|
+
kind: "approval-denied",
|
|
180
|
+
tool,
|
|
181
|
+
reason,
|
|
182
|
+
...trimmed ? { feedback: trimmed } : {}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function buildApprovalRuleContext(sessionId, tool, args, risk, patternExtractor) {
|
|
186
|
+
return {
|
|
187
|
+
sessionId,
|
|
188
|
+
tool,
|
|
189
|
+
args,
|
|
190
|
+
risk,
|
|
191
|
+
patterns: patternExtractor ? patternExtractor(args) : extractApprovalPatterns(tool, args)
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/safety/errors.ts
|
|
196
|
+
var ApprovalDeniedError = class extends Error {
|
|
197
|
+
constructor(tool, args, detail) {
|
|
198
|
+
super(formatApprovalDeniedReason(tool, detail));
|
|
199
|
+
this.tool = tool;
|
|
200
|
+
this.args = args;
|
|
201
|
+
this.name = "ApprovalDeniedError";
|
|
202
|
+
this.feedback = detail;
|
|
203
|
+
}
|
|
204
|
+
feedback;
|
|
205
|
+
};
|
|
206
|
+
var ApprovalTimeoutError = class extends Error {
|
|
207
|
+
constructor(tool, timeoutMs) {
|
|
208
|
+
super(`Approval timeout after ${timeoutMs}ms for: ${tool}`);
|
|
209
|
+
this.tool = tool;
|
|
210
|
+
this.timeoutMs = timeoutMs;
|
|
211
|
+
this.name = "ApprovalTimeoutError";
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// src/safety/handler.ts
|
|
216
|
+
var SOURCE_PRIORITY = {
|
|
217
|
+
user: 0,
|
|
218
|
+
project: 1,
|
|
219
|
+
session: 2,
|
|
220
|
+
cli: 3
|
|
221
|
+
};
|
|
222
|
+
function findMatchingRule(rules, context) {
|
|
223
|
+
let best;
|
|
224
|
+
let bestPriority = -1;
|
|
225
|
+
for (const rule of rules) {
|
|
226
|
+
const toolMatches = rule.tool === "*" || matchApprovalPattern(rule.tool, context.tool);
|
|
227
|
+
const patternMatches = context.patterns.some(
|
|
228
|
+
(pattern) => matchApprovalPattern(rule.pattern, pattern)
|
|
229
|
+
);
|
|
230
|
+
const conditionMatches = rule.when ? rule.when(context) : true;
|
|
231
|
+
if (!toolMatches || !patternMatches || !conditionMatches) continue;
|
|
232
|
+
const sourcePriority = rule.source ? SOURCE_PRIORITY[rule.source] : SOURCE_PRIORITY.session;
|
|
233
|
+
const denyBoost = rule.action === "deny" ? 0.5 : 0;
|
|
234
|
+
const priority = sourcePriority + denyBoost;
|
|
235
|
+
if (priority > bestPriority) {
|
|
236
|
+
best = rule;
|
|
237
|
+
bestPriority = priority;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return best;
|
|
241
|
+
}
|
|
242
|
+
function normalizeApprovalDecision(value) {
|
|
243
|
+
if (typeof value === "string") {
|
|
244
|
+
return { action: value };
|
|
245
|
+
}
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
function createApprovalHandler(config = {}) {
|
|
249
|
+
const {
|
|
250
|
+
defaultAction = "ask",
|
|
251
|
+
timeout = 5 * 60 * 1e3,
|
|
252
|
+
onRequest,
|
|
253
|
+
rememberScope = "session",
|
|
254
|
+
allowedRememberScopes = ["session"],
|
|
255
|
+
cascade
|
|
256
|
+
} = config;
|
|
257
|
+
let requestCounter = 0;
|
|
258
|
+
const initialRuleCount = config.rules?.length ?? 0;
|
|
259
|
+
const rules = [...config.rules ?? []];
|
|
260
|
+
const activeRequests = /* @__PURE__ */ new Set();
|
|
261
|
+
async function request(sessionId, tool, args, customRisks, capabilities, patternExtractor, extraRules = []) {
|
|
262
|
+
const evaluation = evaluate(
|
|
263
|
+
sessionId,
|
|
264
|
+
tool,
|
|
265
|
+
args,
|
|
266
|
+
customRisks,
|
|
267
|
+
capabilities,
|
|
268
|
+
patternExtractor,
|
|
269
|
+
extraRules
|
|
270
|
+
);
|
|
271
|
+
switch (evaluation.action) {
|
|
272
|
+
case "allow":
|
|
273
|
+
return;
|
|
274
|
+
case "deny":
|
|
275
|
+
throw new ApprovalDeniedError(tool, args, evaluation.reason);
|
|
276
|
+
case "request":
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
if (!onRequest) {
|
|
280
|
+
if (defaultAction === "allow") {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (defaultAction === "deny") {
|
|
284
|
+
throw new ApprovalDeniedError(tool, args);
|
|
285
|
+
}
|
|
286
|
+
throw new ApprovalDeniedError(tool, args, "No approval handler configured");
|
|
287
|
+
}
|
|
288
|
+
const ac = new AbortController();
|
|
289
|
+
activeRequests.add(ac);
|
|
290
|
+
let timeoutId;
|
|
291
|
+
try {
|
|
292
|
+
const resolution = normalizeApprovalDecision(await Promise.race([
|
|
293
|
+
onRequest(evaluation.request),
|
|
294
|
+
new Promise((_, reject) => {
|
|
295
|
+
timeoutId = setTimeout(() => {
|
|
296
|
+
reject(new ApprovalTimeoutError(tool, timeout));
|
|
297
|
+
}, timeout);
|
|
298
|
+
ac.signal.addEventListener("abort", () => {
|
|
299
|
+
clearTimeout(timeoutId);
|
|
300
|
+
reject(new Error("Cancelled"));
|
|
301
|
+
}, { once: true });
|
|
302
|
+
})
|
|
303
|
+
]));
|
|
304
|
+
switch (resolution.action) {
|
|
305
|
+
case "allow":
|
|
306
|
+
return;
|
|
307
|
+
case "deny":
|
|
308
|
+
throw new ApprovalDeniedError(tool, args, resolution.feedback);
|
|
309
|
+
case "remember": {
|
|
310
|
+
const scope = selectRememberScope(
|
|
311
|
+
resolution,
|
|
312
|
+
evaluation.request,
|
|
313
|
+
rememberScope
|
|
314
|
+
);
|
|
315
|
+
rules.push(
|
|
316
|
+
...createRememberedApprovalRules(evaluation.request, scope)
|
|
317
|
+
);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} finally {
|
|
322
|
+
clearTimeout(timeoutId);
|
|
323
|
+
activeRequests.delete(ac);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function evaluate(sessionId, tool, args, customRisks, capabilities, patternExtractor, extraRules = []) {
|
|
327
|
+
if (resolveCapability(capabilities?.readOnly, args, false)) {
|
|
328
|
+
return { action: "allow" };
|
|
329
|
+
}
|
|
330
|
+
const risk = getToolRisk(tool, customRisks, capabilities?.riskLevel);
|
|
331
|
+
const ruleContext = buildApprovalRuleContext(
|
|
332
|
+
sessionId,
|
|
333
|
+
tool,
|
|
334
|
+
args,
|
|
335
|
+
risk,
|
|
336
|
+
patternExtractor
|
|
337
|
+
);
|
|
338
|
+
const patterns = ruleContext.patterns;
|
|
339
|
+
const matchingRule = findMatchingRule(
|
|
340
|
+
[...extraRules, ...rules],
|
|
341
|
+
ruleContext
|
|
342
|
+
);
|
|
343
|
+
if (matchingRule) {
|
|
344
|
+
if (matchingRule.action === "allow") {
|
|
345
|
+
return { action: "allow" };
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
action: "deny",
|
|
349
|
+
reason: matchingRule.reason ?? `Denied by rule: ${matchingRule.pattern}`
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
if (risk === "safe" && defaultAction !== "deny") {
|
|
353
|
+
return { action: "allow" };
|
|
354
|
+
}
|
|
355
|
+
if (defaultAction === "allow") {
|
|
356
|
+
return { action: "allow" };
|
|
357
|
+
}
|
|
358
|
+
if (defaultAction === "deny") {
|
|
359
|
+
return {
|
|
360
|
+
action: "deny",
|
|
361
|
+
reason: formatApprovalDeniedReason(tool)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const id = `approval-${++requestCounter}-${Date.now()}`;
|
|
365
|
+
const requestData = {
|
|
366
|
+
id,
|
|
367
|
+
sessionId,
|
|
368
|
+
tool,
|
|
369
|
+
args,
|
|
370
|
+
description: describeApprovalOperation(tool, args),
|
|
371
|
+
risk,
|
|
372
|
+
patterns,
|
|
373
|
+
rememberScopes: normalizeRememberScopes(allowedRememberScopes),
|
|
374
|
+
defaultRememberScope: rememberScope,
|
|
375
|
+
cascade: normalizeApprovalCascadePolicy(cascade),
|
|
376
|
+
timestamp: Date.now()
|
|
377
|
+
};
|
|
378
|
+
return { action: "request", request: requestData };
|
|
379
|
+
}
|
|
380
|
+
function cancelAll(_reason) {
|
|
381
|
+
for (const ac of activeRequests) {
|
|
382
|
+
ac.abort();
|
|
383
|
+
}
|
|
384
|
+
activeRequests.clear();
|
|
385
|
+
}
|
|
386
|
+
function addRule(rule) {
|
|
387
|
+
rules.push(rule);
|
|
388
|
+
}
|
|
389
|
+
function getRules() {
|
|
390
|
+
return rules;
|
|
391
|
+
}
|
|
392
|
+
function clearSessionRules() {
|
|
393
|
+
rules.splice(initialRuleCount);
|
|
394
|
+
}
|
|
395
|
+
function clearBySource(source) {
|
|
396
|
+
for (let i = rules.length - 1; i >= 0; i--) {
|
|
397
|
+
if (rules[i].source === source) {
|
|
398
|
+
rules.splice(i, 1);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
evaluate,
|
|
404
|
+
request,
|
|
405
|
+
cancelAll,
|
|
406
|
+
addRule,
|
|
407
|
+
getRules,
|
|
408
|
+
clearSessionRules,
|
|
409
|
+
clearBySource
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/safety/presets.ts
|
|
414
|
+
function withOverrides(defaultAction, options = {}) {
|
|
415
|
+
return {
|
|
416
|
+
defaultAction,
|
|
417
|
+
...options.onRequest ? { onRequest: options.onRequest } : {},
|
|
418
|
+
...options.rules ? { rules: [...options.rules] } : {},
|
|
419
|
+
...options.timeout !== void 0 ? { timeout: options.timeout } : {},
|
|
420
|
+
...options.rememberScope ? { rememberScope: options.rememberScope } : {},
|
|
421
|
+
...options.allowedRememberScopes ? { allowedRememberScopes: [...options.allowedRememberScopes] } : {},
|
|
422
|
+
...options.cascade ? { cascade: { ...options.cascade } } : {}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function createInteractiveApprovalPolicy(options = {}) {
|
|
426
|
+
return withOverrides("ask", options);
|
|
427
|
+
}
|
|
428
|
+
function createHeadlessDenyApprovalPolicy(options = {}) {
|
|
429
|
+
return withOverrides("deny", options);
|
|
430
|
+
}
|
|
431
|
+
function createDangerouslyAllowAllApprovalPolicy(options = {}) {
|
|
432
|
+
return withOverrides("allow", options);
|
|
433
|
+
}
|
|
434
|
+
function createRiskTierApprovalPolicy(options = {}) {
|
|
435
|
+
const {
|
|
436
|
+
safe = "allow",
|
|
437
|
+
moderate = "ask",
|
|
438
|
+
dangerous = "ask",
|
|
439
|
+
source = "user",
|
|
440
|
+
rules = [],
|
|
441
|
+
...rest
|
|
442
|
+
} = options;
|
|
443
|
+
const generatedRules = [];
|
|
444
|
+
for (const [risk, action] of [
|
|
445
|
+
["safe", safe],
|
|
446
|
+
["moderate", moderate],
|
|
447
|
+
["dangerous", dangerous]
|
|
448
|
+
]) {
|
|
449
|
+
if (action === "ask") {
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
generatedRules.push(
|
|
453
|
+
createConditionalApprovalRule(
|
|
454
|
+
{
|
|
455
|
+
pattern: "*",
|
|
456
|
+
tool: "*",
|
|
457
|
+
action,
|
|
458
|
+
source
|
|
459
|
+
},
|
|
460
|
+
matchApprovalRisks(risk)
|
|
461
|
+
)
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
return withOverrides("ask", {
|
|
465
|
+
...rest,
|
|
466
|
+
rules: [...generatedRules, ...rules]
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
function createTrustedSessionApprovalPolicy(options) {
|
|
470
|
+
const {
|
|
471
|
+
sessionIds,
|
|
472
|
+
tools = ["*"],
|
|
473
|
+
patterns = ["*"],
|
|
474
|
+
risks = ["moderate", "dangerous"],
|
|
475
|
+
source = "user",
|
|
476
|
+
rules = [],
|
|
477
|
+
...rest
|
|
478
|
+
} = options;
|
|
479
|
+
const generatedRules = [];
|
|
480
|
+
for (const tool of tools) {
|
|
481
|
+
for (const pattern of patterns) {
|
|
482
|
+
generatedRules.push(
|
|
483
|
+
createConditionalApprovalRule(
|
|
484
|
+
{
|
|
485
|
+
pattern,
|
|
486
|
+
tool,
|
|
487
|
+
action: "allow",
|
|
488
|
+
source
|
|
489
|
+
},
|
|
490
|
+
(context) => matchApprovalSessions(...sessionIds)(context) && matchApprovalRisks(...risks)(context)
|
|
491
|
+
)
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return withOverrides("ask", {
|
|
496
|
+
...rest,
|
|
497
|
+
rules: [...generatedRules, ...rules]
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
function createThresholdApprovalRule(options) {
|
|
501
|
+
const {
|
|
502
|
+
tool,
|
|
503
|
+
pattern = "*",
|
|
504
|
+
action,
|
|
505
|
+
argPath,
|
|
506
|
+
source,
|
|
507
|
+
reason,
|
|
508
|
+
...thresholds
|
|
509
|
+
} = options;
|
|
510
|
+
return createConditionalApprovalRule(
|
|
511
|
+
{
|
|
512
|
+
pattern,
|
|
513
|
+
tool,
|
|
514
|
+
action,
|
|
515
|
+
...source ? { source } : {},
|
|
516
|
+
...reason ? { reason } : {}
|
|
517
|
+
},
|
|
518
|
+
matchApprovalNumericArg(argPath, thresholds)
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
function createApprovalPolicyPreset(name, options = {}) {
|
|
522
|
+
switch (name) {
|
|
523
|
+
case "interactive":
|
|
524
|
+
return createInteractiveApprovalPolicy(options);
|
|
525
|
+
case "headless-deny":
|
|
526
|
+
return createHeadlessDenyApprovalPolicy(options);
|
|
527
|
+
case "dangerously-allow-all":
|
|
528
|
+
return createDangerouslyAllowAllApprovalPolicy(options);
|
|
529
|
+
case "risk-tier":
|
|
530
|
+
return createRiskTierApprovalPolicy(options);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export {
|
|
535
|
+
getToolRisk,
|
|
536
|
+
createConditionalApprovalRule,
|
|
537
|
+
allApprovalConditions,
|
|
538
|
+
anyApprovalConditions,
|
|
539
|
+
matchApprovalSessions,
|
|
540
|
+
matchApprovalRisks,
|
|
541
|
+
matchApprovalArgValue,
|
|
542
|
+
matchApprovalNumericArg,
|
|
543
|
+
matchApprovalStringPrefixes,
|
|
544
|
+
normalizeApprovalCascadePolicy,
|
|
545
|
+
normalizeRememberScopes,
|
|
546
|
+
selectRememberScope,
|
|
547
|
+
createRememberedApprovalRules,
|
|
548
|
+
approvalRequestsOverlap,
|
|
549
|
+
shouldCascadeApprovalDecision,
|
|
550
|
+
formatApprovalDeniedReason,
|
|
551
|
+
createApprovalCorrection,
|
|
552
|
+
buildApprovalRuleContext,
|
|
553
|
+
ApprovalDeniedError,
|
|
554
|
+
ApprovalTimeoutError,
|
|
555
|
+
createApprovalHandler,
|
|
556
|
+
createInteractiveApprovalPolicy,
|
|
557
|
+
createHeadlessDenyApprovalPolicy,
|
|
558
|
+
createDangerouslyAllowAllApprovalPolicy,
|
|
559
|
+
createRiskTierApprovalPolicy,
|
|
560
|
+
createTrustedSessionApprovalPolicy,
|
|
561
|
+
createThresholdApprovalRule,
|
|
562
|
+
createApprovalPolicyPreset
|
|
563
|
+
};
|
|
File without changes
|