@kilocode/sdk 7.7.6 → 7.7.7
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/kilocode/permission.js +47 -4
- package/package.json +1 -1
|
@@ -1,3 +1,46 @@
|
|
|
1
|
+
const ATTEMPTS = 3;
|
|
2
|
+
// Mirrors the transient classifier in
|
|
3
|
+
// packages/kilo-vscode/src/services/cli-backend/retry.ts. The SDK cannot import
|
|
4
|
+
// that module, so keep the two lists in sync by hand, including the exact
|
|
5
|
+
// "terminated" match for undici pooled-connection drops. Only transport-level
|
|
6
|
+
// failures are retried. A status-less client error such as a response parse
|
|
7
|
+
// failure or a server-version mismatch is definitive and must not be replayed.
|
|
8
|
+
// Re-sending an identical rule set after a lost response is tolerable because
|
|
9
|
+
// duplicate patterns do not change the decision.
|
|
10
|
+
const TRANSIENT = [
|
|
11
|
+
"load failed",
|
|
12
|
+
"network connection was lost",
|
|
13
|
+
"network request failed",
|
|
14
|
+
"failed to fetch",
|
|
15
|
+
"fetch failed",
|
|
16
|
+
"econnreset",
|
|
17
|
+
"econnrefused",
|
|
18
|
+
"etimedout",
|
|
19
|
+
"socket hang up",
|
|
20
|
+
];
|
|
21
|
+
const TRANSIENT_EXACT = ["terminated"];
|
|
22
|
+
function transport(error) {
|
|
23
|
+
if (!error)
|
|
24
|
+
return false;
|
|
25
|
+
const message = String(error instanceof Error ? error.message : error)
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.trim();
|
|
28
|
+
if (TRANSIENT_EXACT.includes(message))
|
|
29
|
+
return true;
|
|
30
|
+
return TRANSIENT.some((entry) => message.includes(entry));
|
|
31
|
+
}
|
|
32
|
+
async function send(fn, budget) {
|
|
33
|
+
for (let attempt = 0;; attempt++) {
|
|
34
|
+
try {
|
|
35
|
+
return await fn();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (attempt >= ATTEMPTS - 1 || !transport(error) || budget() <= 0)
|
|
39
|
+
throw error;
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(100 * 2 ** attempt, budget())));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
1
44
|
/**
|
|
2
45
|
* Send a permission decision with one bounded wait across both requests.
|
|
3
46
|
* A timeout aborts only the client wait, so callers must reconcile an aborted
|
|
@@ -10,20 +53,20 @@ export async function respondToPermission(client, input, timeout = 15_000) {
|
|
|
10
53
|
const saved = input.approvedAlways.length > 0 || input.deniedAlways.length > 0;
|
|
11
54
|
try {
|
|
12
55
|
if (saved) {
|
|
13
|
-
await client.permission.saveAlwaysRules({
|
|
56
|
+
await send(() => client.permission.saveAlwaysRules({
|
|
14
57
|
requestID: input.requestID,
|
|
15
58
|
directory: input.directory,
|
|
16
59
|
approvedAlways: input.approvedAlways,
|
|
17
60
|
deniedAlways: input.deniedAlways,
|
|
18
|
-
}, { throwOnError: true, signal: AbortSignal.timeout(budget()) });
|
|
61
|
+
}, { throwOnError: true, signal: AbortSignal.timeout(budget()) }), budget);
|
|
19
62
|
}
|
|
20
|
-
await client.permission.reply({
|
|
63
|
+
await send(() => client.permission.reply({
|
|
21
64
|
requestID: input.requestID,
|
|
22
65
|
directory: input.directory,
|
|
23
66
|
reply: input.reply,
|
|
24
67
|
interactive: true,
|
|
25
68
|
...(input.message ? { message: input.message } : {}),
|
|
26
|
-
}, { throwOnError: true, signal: AbortSignal.timeout(budget()) });
|
|
69
|
+
}, { throwOnError: true, signal: AbortSignal.timeout(budget()) }), budget);
|
|
27
70
|
return { saved };
|
|
28
71
|
}
|
|
29
72
|
catch (error) {
|