@lytharalab/dsh-cons-auto-review 1.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/CHANGELOG.md +38 -0
- package/LICENSE +22 -0
- package/README.md +126 -0
- package/THIRD_PARTY_LICENSE.txt +21 -0
- package/THIRD_PARTY_NOTICES.md +10 -0
- package/cordis.patch.yml +5 -0
- package/docs/COMPATIBILITY.md +43 -0
- package/docs/CONFIGURATION.md +167 -0
- package/docs/RELEASING.md +108 -0
- package/examples/defaults.json +121 -0
- package/examples/desktop-config.patch.yml +30 -0
- package/examples/separate-review-model.patch.yml +12 -0
- package/examples/strict.patch.yml +13 -0
- package/icon.svg +1 -0
- package/locale/en.json +1 -0
- package/locale/zh.json +1 -0
- package/package.json +108 -0
- package/src/async.js +66 -0
- package/src/audit.js +125 -0
- package/src/config.js +240 -0
- package/src/containment.js +49 -0
- package/src/deterministic.js +58 -0
- package/src/engine.js +365 -0
- package/src/index.d.ts +110 -0
- package/src/index.js +396 -0
- package/src/paths.js +94 -0
- package/src/permission-adapter.js +75 -0
- package/src/permission-presets.js +77 -0
- package/src/presentation.js +48 -0
- package/src/preset-defaults.js +85 -0
- package/src/shell.js +197 -0
- package/src/upstream-reviewer.js +578 -0
package/src/engine.js
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/** Deterministic-first authorization, configurable LLM fallback, and audit. */
|
|
2
|
+
import { deterministic } from "./deterministic.js";
|
|
3
|
+
import {
|
|
4
|
+
snapshotAutoReview,
|
|
5
|
+
reviewUserText,
|
|
6
|
+
readDecision,
|
|
7
|
+
REVIEW_POLICY,
|
|
8
|
+
chineseReviewPolicy,
|
|
9
|
+
isChineseReason,
|
|
10
|
+
readChineseReason,
|
|
11
|
+
CHINESE_TRANSLATION_POLICY,
|
|
12
|
+
ReviewResponseError,
|
|
13
|
+
} from "./upstream-reviewer.js";
|
|
14
|
+
import { ReviewQueue, abortable, delay } from "./async.js";
|
|
15
|
+
import { Audit, hash } from "./audit.js";
|
|
16
|
+
import { approvalText, reviewFailureMessage, bounded } from "./presentation.js";
|
|
17
|
+
export class ReviewEngine {
|
|
18
|
+
queue = new ReviewQueue();
|
|
19
|
+
audit = new Audit();
|
|
20
|
+
cache = new Map();
|
|
21
|
+
lifecycle = new AbortController();
|
|
22
|
+
constructor(ctx, config, options = {}) {
|
|
23
|
+
this.ctx = ctx;
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.snapshot = options.snapshot ?? snapshotAutoReview;
|
|
26
|
+
this.read = options.read ?? readDecision;
|
|
27
|
+
}
|
|
28
|
+
clearCache() {
|
|
29
|
+
this.cache.clear();
|
|
30
|
+
}
|
|
31
|
+
async stop() {
|
|
32
|
+
this.lifecycle.abort(new Error("DCAR plugin disposed"));
|
|
33
|
+
await this.audit.flush();
|
|
34
|
+
}
|
|
35
|
+
async review(exec, c = this.config) {
|
|
36
|
+
const start = performance.now(),
|
|
37
|
+
signal = AbortSignal.any([exec.signal, this.lifecycle.signal]);
|
|
38
|
+
let result,
|
|
39
|
+
layer = "program",
|
|
40
|
+
code,
|
|
41
|
+
reason,
|
|
42
|
+
cacheHit = false,
|
|
43
|
+
error = false;
|
|
44
|
+
const trace = {
|
|
45
|
+
reviewAttempts: 0,
|
|
46
|
+
tokenRetries: 0,
|
|
47
|
+
translationAttempts: 0,
|
|
48
|
+
lastMaxTokens: 0,
|
|
49
|
+
};
|
|
50
|
+
let displayReason;
|
|
51
|
+
try {
|
|
52
|
+
signal.throwIfAborted();
|
|
53
|
+
let first;
|
|
54
|
+
try {
|
|
55
|
+
first = await deterministic(exec, this.ctx, c, signal);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
if (signal.aborted) throw e;
|
|
58
|
+
first = {
|
|
59
|
+
kind: "escalate",
|
|
60
|
+
code: "PROGRAM_CHECK_ERROR",
|
|
61
|
+
reason: String(e.message ?? e),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
code = first.code;
|
|
65
|
+
reason = first.reason;
|
|
66
|
+
if (first.kind === "allow") result = { kind: "allow" };
|
|
67
|
+
else {
|
|
68
|
+
layer = "llm";
|
|
69
|
+
const timer = new AbortController();
|
|
70
|
+
const timeout = setTimeout(
|
|
71
|
+
() =>
|
|
72
|
+
timer.abort(
|
|
73
|
+
new ReviewResponseError(
|
|
74
|
+
"DCAR_REVIEW_TIMEOUT",
|
|
75
|
+
`DCAR review timed out after ${c.review.timeoutMs} ms`,
|
|
76
|
+
),
|
|
77
|
+
),
|
|
78
|
+
c.review.timeoutMs,
|
|
79
|
+
);
|
|
80
|
+
const reviewSignal = AbortSignal.any([signal, timer.signal]);
|
|
81
|
+
let release;
|
|
82
|
+
try {
|
|
83
|
+
release = await this.queue.acquire(
|
|
84
|
+
c.review.concurrency,
|
|
85
|
+
c.review.maxQueued,
|
|
86
|
+
reviewSignal,
|
|
87
|
+
);
|
|
88
|
+
const snapshot = this.snapshot(exec.agent, exec);
|
|
89
|
+
const text = reviewUserText(snapshot);
|
|
90
|
+
if (text.length > c.review.maxInputChars)
|
|
91
|
+
throw new ReviewResponseError(
|
|
92
|
+
"DCAR_REVIEW_INPUT",
|
|
93
|
+
"DCAR review input exceeds maxInputChars; no authorization context was truncated",
|
|
94
|
+
);
|
|
95
|
+
const options = {
|
|
96
|
+
provider: c.review.provider || snapshot.provider,
|
|
97
|
+
model: c.review.model || snapshot.model,
|
|
98
|
+
system:
|
|
99
|
+
REVIEW_POLICY +
|
|
100
|
+
(c.review.allowAsk
|
|
101
|
+
? ""
|
|
102
|
+
: "\nDo not return ask; return allow or deny.") +
|
|
103
|
+
(c.review.policyAppend
|
|
104
|
+
? `\n\nADMINISTRATOR_POLICY\n${c.review.policyAppend}`
|
|
105
|
+
: "") +
|
|
106
|
+
chineseReviewPolicy(c.review.maxReasonChars),
|
|
107
|
+
messages: [{ role: "user", content: [{ type: "text", text }] }],
|
|
108
|
+
temperature: c.review.temperature,
|
|
109
|
+
maxTokens: Math.min(c.review.maxTokens, c.review.maxTokensLimit),
|
|
110
|
+
signal: reviewSignal,
|
|
111
|
+
};
|
|
112
|
+
if (c.review.reasoningEffort)
|
|
113
|
+
options.reasoningEffort = c.review.reasoningEffort;
|
|
114
|
+
else if (
|
|
115
|
+
c.review.preferLowReasoning &&
|
|
116
|
+
this.ctx.llm.resolveModelInfo
|
|
117
|
+
) {
|
|
118
|
+
const info = await abortable(
|
|
119
|
+
this.ctx.llm.resolveModelInfo(
|
|
120
|
+
options.provider,
|
|
121
|
+
options.model,
|
|
122
|
+
reviewSignal,
|
|
123
|
+
),
|
|
124
|
+
reviewSignal,
|
|
125
|
+
);
|
|
126
|
+
if (info.reasoning?.efforts.some((effort) => effort.id === "low"))
|
|
127
|
+
options.reasoningEffort = "low";
|
|
128
|
+
}
|
|
129
|
+
// Include the entire authorization snapshot and current policy: no tool-name/prefix grants.
|
|
130
|
+
const key = hash({
|
|
131
|
+
session: exec.agent.session.id,
|
|
132
|
+
snapshot,
|
|
133
|
+
route: { provider: options.provider, model: options.model },
|
|
134
|
+
policy: options.system,
|
|
135
|
+
reasoning: options.reasoningEffort,
|
|
136
|
+
maxTokens: options.maxTokens,
|
|
137
|
+
tokenRecovery: [
|
|
138
|
+
c.review.maxTokensLimit,
|
|
139
|
+
c.review.tokenLimitRetries,
|
|
140
|
+
c.review.tokenGrowthFactor,
|
|
141
|
+
],
|
|
142
|
+
temperature: options.temperature,
|
|
143
|
+
});
|
|
144
|
+
let decision;
|
|
145
|
+
const cached = c.cache.enabled ? this.cache.get(key) : undefined;
|
|
146
|
+
if (cached && cached.expires > Date.now()) {
|
|
147
|
+
decision = cached.decision;
|
|
148
|
+
cacheHit = true;
|
|
149
|
+
} else {
|
|
150
|
+
if (cached) this.cache.delete(key);
|
|
151
|
+
let retries = 0,
|
|
152
|
+
budget = options.maxTokens,
|
|
153
|
+
tokenRetryPending = false;
|
|
154
|
+
for (;;) {
|
|
155
|
+
reviewSignal.throwIfAborted();
|
|
156
|
+
if (tokenRetryPending) {
|
|
157
|
+
trace.tokenRetries++;
|
|
158
|
+
tokenRetryPending = false;
|
|
159
|
+
}
|
|
160
|
+
this.audit.request(String(exec.agent.session.id));
|
|
161
|
+
trace.reviewAttempts++;
|
|
162
|
+
trace.lastMaxTokens = budget;
|
|
163
|
+
try {
|
|
164
|
+
decision = await abortable(
|
|
165
|
+
this.read(
|
|
166
|
+
this.ctx.llm.stream({ ...options, maxTokens: budget }),
|
|
167
|
+
),
|
|
168
|
+
reviewSignal,
|
|
169
|
+
);
|
|
170
|
+
break;
|
|
171
|
+
} catch (e) {
|
|
172
|
+
if (reviewSignal.aborted) throw reviewSignal.reason ?? e;
|
|
173
|
+
if (e.code === "DCAR_MAX_TOKENS") {
|
|
174
|
+
if (
|
|
175
|
+
trace.tokenRetries >= c.review.tokenLimitRetries ||
|
|
176
|
+
budget >= c.review.maxTokensLimit
|
|
177
|
+
)
|
|
178
|
+
throw e;
|
|
179
|
+
budget = Math.min(
|
|
180
|
+
c.review.maxTokensLimit,
|
|
181
|
+
budget * c.review.tokenGrowthFactor,
|
|
182
|
+
);
|
|
183
|
+
tokenRetryPending = true;
|
|
184
|
+
} else {
|
|
185
|
+
if (retries >= c.review.retries) throw e;
|
|
186
|
+
retries++;
|
|
187
|
+
}
|
|
188
|
+
await delay(c.review.retryDelayMs, reviewSignal);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (c.cache.enabled && decision.decision === "allow") {
|
|
192
|
+
this.cache.set(key, {
|
|
193
|
+
decision,
|
|
194
|
+
expires: Date.now() + c.cache.ttlMs,
|
|
195
|
+
});
|
|
196
|
+
while (this.cache.size > c.cache.maxEntries)
|
|
197
|
+
this.cache.delete(this.cache.keys().next().value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
reason =
|
|
201
|
+
decision.reason ?? `LLM ${decision.risk}: ${decision.decision}`;
|
|
202
|
+
const behavior =
|
|
203
|
+
decision.decision === "ask"
|
|
204
|
+
? c.review.allowAsk
|
|
205
|
+
? "ask"
|
|
206
|
+
: "deny"
|
|
207
|
+
: c.review.onDeny;
|
|
208
|
+
if (
|
|
209
|
+
decision.decision !== "allow" &&
|
|
210
|
+
behavior === "ask" &&
|
|
211
|
+
this.ctx.approval.overrideOf(exec.agent.session) !== "never"
|
|
212
|
+
)
|
|
213
|
+
displayReason =
|
|
214
|
+
`审查结论:${decision.decision === "deny" ? "拒绝" : "需要人工确认"}(风险等级:${decision.risk === "high" ? "高" : "中"})。\n` +
|
|
215
|
+
(await this.chineseReason(
|
|
216
|
+
exec,
|
|
217
|
+
decision,
|
|
218
|
+
options,
|
|
219
|
+
c,
|
|
220
|
+
reviewSignal,
|
|
221
|
+
trace,
|
|
222
|
+
));
|
|
223
|
+
result =
|
|
224
|
+
decision.decision === "allow"
|
|
225
|
+
? { kind: "allow" }
|
|
226
|
+
: this.nonAllow(
|
|
227
|
+
exec,
|
|
228
|
+
behavior,
|
|
229
|
+
reason,
|
|
230
|
+
"DCAR_REVIEW_DENIED",
|
|
231
|
+
c,
|
|
232
|
+
displayReason,
|
|
233
|
+
);
|
|
234
|
+
} finally {
|
|
235
|
+
clearTimeout(timeout);
|
|
236
|
+
release?.();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} catch (e) {
|
|
240
|
+
if (signal.aborted) {
|
|
241
|
+
result = { kind: "cancel" };
|
|
242
|
+
reason = String(e.message ?? e);
|
|
243
|
+
} else {
|
|
244
|
+
error = true;
|
|
245
|
+
layer = "llm";
|
|
246
|
+
reason = String(e.message ?? e);
|
|
247
|
+
trace.errorCode = e.code ?? "DCAR_REVIEW_ERROR";
|
|
248
|
+
displayReason = reviewFailureMessage(e, c, trace);
|
|
249
|
+
code ??= "REVIEW_ERROR";
|
|
250
|
+
result = this.nonAllow(
|
|
251
|
+
exec,
|
|
252
|
+
c.review.onError,
|
|
253
|
+
reason,
|
|
254
|
+
"DCAR_REVIEW_FAILED",
|
|
255
|
+
c,
|
|
256
|
+
displayReason,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (signal.aborted) result = { kind: "cancel" };
|
|
261
|
+
this.audit.record(
|
|
262
|
+
exec,
|
|
263
|
+
{
|
|
264
|
+
layer,
|
|
265
|
+
decision: result.kind,
|
|
266
|
+
code,
|
|
267
|
+
reason,
|
|
268
|
+
error,
|
|
269
|
+
cacheHit,
|
|
270
|
+
durationMs: Math.round(performance.now() - start),
|
|
271
|
+
...trace,
|
|
272
|
+
displayReason,
|
|
273
|
+
},
|
|
274
|
+
c,
|
|
275
|
+
);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
async chineseReason(exec, decision, options, c, signal, trace) {
|
|
279
|
+
const original = decision.reason?.trim();
|
|
280
|
+
if (isChineseReason(original)) return bounded(original, c.review.maxReasonChars);
|
|
281
|
+
if (original && original.length <= c.review.maxInputChars) {
|
|
282
|
+
for (
|
|
283
|
+
let attempt = 0;
|
|
284
|
+
attempt < c.review.chineseReasonRetries;
|
|
285
|
+
attempt++
|
|
286
|
+
) {
|
|
287
|
+
if (signal.aborted) break;
|
|
288
|
+
this.audit.request(String(exec.agent.session.id));
|
|
289
|
+
trace.translationAttempts++;
|
|
290
|
+
try {
|
|
291
|
+
return await abortable(
|
|
292
|
+
readChineseReason(
|
|
293
|
+
this.ctx.llm.stream({
|
|
294
|
+
...options,
|
|
295
|
+
system:
|
|
296
|
+
CHINESE_TRANSLATION_POLICY +
|
|
297
|
+
`\n说明最多 ${c.review.maxReasonChars} 字符。`,
|
|
298
|
+
messages: [
|
|
299
|
+
{
|
|
300
|
+
role: "user",
|
|
301
|
+
content: [
|
|
302
|
+
{
|
|
303
|
+
type: "text",
|
|
304
|
+
text: JSON.stringify({ reason: original }),
|
|
305
|
+
},
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
maxTokens: Math.min(options.maxTokens, 2048),
|
|
310
|
+
}),
|
|
311
|
+
c.review.maxReasonChars,
|
|
312
|
+
),
|
|
313
|
+
signal,
|
|
314
|
+
);
|
|
315
|
+
} catch {
|
|
316
|
+
// Translation cannot change an already validated risk/decision or grant permission.
|
|
317
|
+
if (signal.aborted) break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (original)
|
|
322
|
+
return (
|
|
323
|
+
"审查模型没有提供可用的中文说明,请核对本次操作的目标和授权范围后决定。" +
|
|
324
|
+
(c.commands.enabled && c.audit.enabled && c.audit.includeReasons
|
|
325
|
+
? `原始审查说明的记录可通过 /${c.commands.name} history 查看。`
|
|
326
|
+
: "")
|
|
327
|
+
);
|
|
328
|
+
return decision.decision === "ask"
|
|
329
|
+
? "审查模型需要你确认本次操作,但未给出具体说明。请核对操作内容、目标及授权范围。"
|
|
330
|
+
: "审查模型未批准本次操作,且未给出具体说明。请核对操作内容、目标及授权范围。";
|
|
331
|
+
}
|
|
332
|
+
nonAllow(
|
|
333
|
+
exec,
|
|
334
|
+
behavior,
|
|
335
|
+
reason,
|
|
336
|
+
code,
|
|
337
|
+
c = this.config,
|
|
338
|
+
chineseExplanation = "请核对本次操作的目标及授权范围。",
|
|
339
|
+
) {
|
|
340
|
+
const never = this.ctx.approval.overrideOf(exec.agent.session) === "never";
|
|
341
|
+
if (behavior === "ask" && !never) {
|
|
342
|
+
const text = approvalText(exec, c, chineseExplanation);
|
|
343
|
+
return {
|
|
344
|
+
kind: "ask",
|
|
345
|
+
reason: text,
|
|
346
|
+
displayReason: {
|
|
347
|
+
en: text,
|
|
348
|
+
zh: text,
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
kind: "deny",
|
|
354
|
+
reason: `CAutoR did not authorize tool "${exec.name}"; body not executed: ${reason}`,
|
|
355
|
+
info: {
|
|
356
|
+
name:
|
|
357
|
+
code === "DCAR_REVIEW_FAILED"
|
|
358
|
+
? "DCARReviewFailedError"
|
|
359
|
+
: "DCARReviewDeniedError",
|
|
360
|
+
code,
|
|
361
|
+
reason,
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import type z from "@deepseek-ai/schemastery";
|
|
3
|
+
export interface DCARConfig {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
inheritSubagents: boolean;
|
|
6
|
+
rules: {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
allowOutsideReads: boolean;
|
|
9
|
+
includeTempRoots: boolean;
|
|
10
|
+
additionalWritableRoots: string[];
|
|
11
|
+
protectedPaths: string[];
|
|
12
|
+
safeEnvironmentFiles: string[];
|
|
13
|
+
readTools: string[];
|
|
14
|
+
searchTools: string[];
|
|
15
|
+
writeTools: string[];
|
|
16
|
+
harmlessTools: string[];
|
|
17
|
+
alwaysReviewTools: string[];
|
|
18
|
+
trustedTools: string[];
|
|
19
|
+
customTools: {
|
|
20
|
+
name: string;
|
|
21
|
+
effect: "read" | "write";
|
|
22
|
+
pathFields: string[];
|
|
23
|
+
}[];
|
|
24
|
+
maxArgumentBytes: number;
|
|
25
|
+
};
|
|
26
|
+
shell: {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
tools: string[];
|
|
29
|
+
readCommands: string[];
|
|
30
|
+
workspaceMutationCommands: string[];
|
|
31
|
+
trustedCommands: { executable: string; args: string[] }[];
|
|
32
|
+
maxCommandChars: number;
|
|
33
|
+
};
|
|
34
|
+
review: {
|
|
35
|
+
provider: string;
|
|
36
|
+
model: string;
|
|
37
|
+
reasoningEffort: string;
|
|
38
|
+
preferLowReasoning: boolean;
|
|
39
|
+
timeoutMs: number;
|
|
40
|
+
maxTokens: number;
|
|
41
|
+
maxTokensLimit: number;
|
|
42
|
+
tokenLimitRetries: number;
|
|
43
|
+
tokenGrowthFactor: number;
|
|
44
|
+
chineseReasonRetries: number;
|
|
45
|
+
maxReasonChars: number;
|
|
46
|
+
showAction: boolean;
|
|
47
|
+
maxActionChars: number;
|
|
48
|
+
temperature: number;
|
|
49
|
+
retries: number;
|
|
50
|
+
retryDelayMs: number;
|
|
51
|
+
concurrency: number;
|
|
52
|
+
maxQueued: number;
|
|
53
|
+
onError: "ask" | "deny";
|
|
54
|
+
onDeny: "ask" | "deny";
|
|
55
|
+
policyAppend: string;
|
|
56
|
+
maxInputChars: number;
|
|
57
|
+
allowAsk: boolean;
|
|
58
|
+
};
|
|
59
|
+
cache: { enabled: boolean; ttlMs: number; maxEntries: number };
|
|
60
|
+
audit: {
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
file: string;
|
|
63
|
+
maxBytes: number;
|
|
64
|
+
historyLimit: number;
|
|
65
|
+
includeArguments: boolean;
|
|
66
|
+
includeReasons: boolean;
|
|
67
|
+
};
|
|
68
|
+
commands: { enabled: boolean; name: string; allowSessionConfig: boolean };
|
|
69
|
+
lifecycle: { fallbackPreset: string };
|
|
70
|
+
}
|
|
71
|
+
export type PartialConfig = {
|
|
72
|
+
[K in keyof DCARConfig]?: DCARConfig[K] extends object
|
|
73
|
+
? Partial<DCARConfig[K]>
|
|
74
|
+
: DCARConfig[K];
|
|
75
|
+
};
|
|
76
|
+
export const name: "@lytharalab/dsh-cons-auto-review";
|
|
77
|
+
export const PRESET: "CAutoR";
|
|
78
|
+
export const inject: string[];
|
|
79
|
+
export const Config: z<DCARConfig>;
|
|
80
|
+
export const DEFAULTS: Readonly<DCARConfig>;
|
|
81
|
+
export function resolveConfig(input?: PartialConfig): Readonly<DCARConfig>;
|
|
82
|
+
export function apply(ctx: Context, config?: PartialConfig): void;
|
|
83
|
+
/** Low-level review engine; DSH normally instantiates this through apply(). */
|
|
84
|
+
export class ReviewEngine {
|
|
85
|
+
constructor(
|
|
86
|
+
ctx: Context,
|
|
87
|
+
config: Readonly<DCARConfig>,
|
|
88
|
+
options?: { snapshot?: Function; read?: Function },
|
|
89
|
+
);
|
|
90
|
+
review(
|
|
91
|
+
exec: {
|
|
92
|
+
agent: { session: { id: string; header: { cwd: string } } };
|
|
93
|
+
name: string;
|
|
94
|
+
arguments: unknown;
|
|
95
|
+
callId: string;
|
|
96
|
+
signal: AbortSignal;
|
|
97
|
+
},
|
|
98
|
+
config?: Readonly<DCARConfig>,
|
|
99
|
+
): Promise<
|
|
100
|
+
| { kind: "allow" | "cancel" }
|
|
101
|
+
| { kind: "ask"; reason: string; displayReason: Record<string, string> }
|
|
102
|
+
| {
|
|
103
|
+
kind: "deny";
|
|
104
|
+
reason: string;
|
|
105
|
+
info: { name: string; code: string; reason: string };
|
|
106
|
+
}
|
|
107
|
+
>;
|
|
108
|
+
clearCache(): void;
|
|
109
|
+
stop(): Promise<void>;
|
|
110
|
+
}
|