@khalilgharbaoui/opencode-claude-code-plugin 0.27.2 → 0.28.1
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 +82 -2
- package/dist/index.d.ts +114 -2
- package/dist/index.js +403 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/claude-code-plugin/SKILL.md +30 -3
package/dist/index.js
CHANGED
|
@@ -931,6 +931,13 @@ function formatStreamTimeoutNote(silenceMs) {
|
|
|
931
931
|
${STREAM_TIMEOUT_MARKER} The Claude Code CLI produced output and then went silent for ${seconds}s without finishing the turn, so it was closed without a result. The answer above may be incomplete.
|
|
932
932
|
`;
|
|
933
933
|
}
|
|
934
|
+
var SILENT_TURN_MARKER = "\u258C **no reply:**";
|
|
935
|
+
function formatSilentTurnNote(hadReasoning) {
|
|
936
|
+
const what = hadReasoning ? "Claude Code finished the turn after thinking but never wrote an answer" : "Claude Code finished the turn without writing anything or calling a tool";
|
|
937
|
+
return `
|
|
938
|
+
${SILENT_TURN_MARKER} ${what}, so there is no reply above. Nothing failed and nothing is pending: send the message again, or rephrase it.
|
|
939
|
+
`;
|
|
940
|
+
}
|
|
934
941
|
|
|
935
942
|
// src/plan-mode-question.ts
|
|
936
943
|
var QUESTION_TOOL_NAME = "question";
|
|
@@ -1468,6 +1475,14 @@ function cliSupportsFastMode(v) {
|
|
|
1468
1475
|
if (!v) return false;
|
|
1469
1476
|
return gte(v, { major: 2, minor: 1, patch: 220 });
|
|
1470
1477
|
}
|
|
1478
|
+
function cliSupportsRestricted(v) {
|
|
1479
|
+
if (!v) return false;
|
|
1480
|
+
return gte(v, { major: 2, minor: 1, patch: 258 });
|
|
1481
|
+
}
|
|
1482
|
+
function cliSupportsPermissionPrompts(v) {
|
|
1483
|
+
if (!v) return false;
|
|
1484
|
+
return gte(v, { major: 2, minor: 1, patch: 263 });
|
|
1485
|
+
}
|
|
1471
1486
|
function cliSupportsSideQuestion(v) {
|
|
1472
1487
|
if (!v) return false;
|
|
1473
1488
|
return gte(v, { major: 2, minor: 1, patch: 258 });
|
|
@@ -1522,25 +1537,137 @@ import { EventEmitter } from "events";
|
|
|
1522
1537
|
import * as fs from "fs";
|
|
1523
1538
|
import * as os from "os";
|
|
1524
1539
|
import * as path2 from "path";
|
|
1525
|
-
var
|
|
1540
|
+
var PLUGIN_TMP_PREFIX = "opencode-claude-code-";
|
|
1541
|
+
var PID_TMP_DIR = path2.join(
|
|
1526
1542
|
os.tmpdir(),
|
|
1527
|
-
|
|
1543
|
+
`${PLUGIN_TMP_PREFIX}${process.pid}`
|
|
1528
1544
|
);
|
|
1545
|
+
var currentDir = null;
|
|
1546
|
+
var createdDirs = /* @__PURE__ */ new Set();
|
|
1529
1547
|
var registered = false;
|
|
1530
|
-
function
|
|
1531
|
-
|
|
1532
|
-
|
|
1548
|
+
function currentUid() {
|
|
1549
|
+
return typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
1550
|
+
}
|
|
1551
|
+
function untrustworthyReason(dir) {
|
|
1552
|
+
let stat;
|
|
1553
|
+
try {
|
|
1554
|
+
stat = fs.lstatSync(dir);
|
|
1555
|
+
} catch {
|
|
1556
|
+
return null;
|
|
1533
1557
|
}
|
|
1534
|
-
if (
|
|
1535
|
-
|
|
1536
|
-
|
|
1558
|
+
if (stat.isSymbolicLink()) return "path is a symlink";
|
|
1559
|
+
if (!stat.isDirectory()) return "path is not a directory";
|
|
1560
|
+
const uid = currentUid();
|
|
1561
|
+
if (uid !== void 0 && stat.uid !== uid) {
|
|
1562
|
+
return `directory is owned by uid ${stat.uid}, not ${uid}`;
|
|
1563
|
+
}
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
function ensureDir(dir) {
|
|
1567
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1568
|
+
try {
|
|
1569
|
+
fs.chmodSync(dir, 448);
|
|
1570
|
+
} catch {
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
function chooseDir() {
|
|
1574
|
+
const reason = untrustworthyReason(PID_TMP_DIR);
|
|
1575
|
+
if (!reason) return PID_TMP_DIR;
|
|
1576
|
+
const fallback = fs.mkdtempSync(path2.join(os.tmpdir(), PLUGIN_TMP_PREFIX));
|
|
1577
|
+
log.warn("refusing the pid-named scratch directory", {
|
|
1578
|
+
path: PID_TMP_DIR,
|
|
1579
|
+
reason,
|
|
1580
|
+
fallback
|
|
1581
|
+
});
|
|
1582
|
+
return fallback;
|
|
1583
|
+
}
|
|
1584
|
+
function registerExitCleanup() {
|
|
1585
|
+
if (registered) return;
|
|
1586
|
+
registered = true;
|
|
1587
|
+
process.on("exit", () => {
|
|
1588
|
+
for (const dir of createdDirs) {
|
|
1537
1589
|
try {
|
|
1538
|
-
fs.rmSync(
|
|
1590
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
1539
1591
|
} catch {
|
|
1540
1592
|
}
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
var PID_DIR_NAME = /^opencode-claude-code-(\d+)$/;
|
|
1597
|
+
function isProcessAlive(pid) {
|
|
1598
|
+
try {
|
|
1599
|
+
process.kill(pid, 0);
|
|
1600
|
+
return true;
|
|
1601
|
+
} catch (err) {
|
|
1602
|
+
return err.code === "EPERM";
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
function sweepStalePluginTmpDirs(keep) {
|
|
1606
|
+
const removed = [];
|
|
1607
|
+
if (process.env.OPENCODE_CLAUDE_CODE_NO_TMP_SWEEP === "1") return removed;
|
|
1608
|
+
const root = os.tmpdir();
|
|
1609
|
+
let entries;
|
|
1610
|
+
try {
|
|
1611
|
+
entries = fs.readdirSync(root);
|
|
1612
|
+
} catch {
|
|
1613
|
+
return removed;
|
|
1614
|
+
}
|
|
1615
|
+
const uid = currentUid();
|
|
1616
|
+
const keepPath = keep ? path2.resolve(keep) : null;
|
|
1617
|
+
for (const entry of entries) {
|
|
1618
|
+
const match = PID_DIR_NAME.exec(entry);
|
|
1619
|
+
if (!match) continue;
|
|
1620
|
+
const pid = Number(match[1]);
|
|
1621
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) continue;
|
|
1622
|
+
if (pid === process.pid) continue;
|
|
1623
|
+
const dir = path2.join(root, entry);
|
|
1624
|
+
if (keepPath && path2.resolve(dir) === keepPath) continue;
|
|
1625
|
+
let stat;
|
|
1626
|
+
try {
|
|
1627
|
+
stat = fs.lstatSync(dir);
|
|
1628
|
+
} catch {
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) continue;
|
|
1632
|
+
if (uid !== void 0 && stat.uid !== uid) continue;
|
|
1633
|
+
if (isProcessAlive(pid)) continue;
|
|
1634
|
+
try {
|
|
1635
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
1636
|
+
removed.push(dir);
|
|
1637
|
+
} catch (err) {
|
|
1638
|
+
log.debug("could not remove a stale scratch directory", {
|
|
1639
|
+
dir,
|
|
1640
|
+
error: String(err)
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
if (removed.length > 0) {
|
|
1645
|
+
log.info("removed stale plugin scratch directories", {
|
|
1646
|
+
count: removed.length,
|
|
1647
|
+
dirs: removed
|
|
1541
1648
|
});
|
|
1542
1649
|
}
|
|
1543
|
-
return
|
|
1650
|
+
return removed;
|
|
1651
|
+
}
|
|
1652
|
+
var swept = false;
|
|
1653
|
+
function sweepOnce(keep) {
|
|
1654
|
+
if (swept) return;
|
|
1655
|
+
swept = true;
|
|
1656
|
+
try {
|
|
1657
|
+
sweepStalePluginTmpDirs(keep);
|
|
1658
|
+
} catch (err) {
|
|
1659
|
+
log.warn("stale scratch directory sweep failed", { error: String(err) });
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
function pluginTmpDir() {
|
|
1663
|
+
if (currentDir === null || untrustworthyReason(currentDir) !== null) {
|
|
1664
|
+
currentDir = chooseDir();
|
|
1665
|
+
}
|
|
1666
|
+
ensureDir(currentDir);
|
|
1667
|
+
createdDirs.add(currentDir);
|
|
1668
|
+
registerExitCleanup();
|
|
1669
|
+
sweepOnce(currentDir);
|
|
1670
|
+
return currentDir;
|
|
1544
1671
|
}
|
|
1545
1672
|
|
|
1546
1673
|
// src/proxy-mcp.ts
|
|
@@ -2122,12 +2249,12 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2122
2249
|
});
|
|
2123
2250
|
let timer = null;
|
|
2124
2251
|
const result = await new Promise(
|
|
2125
|
-
(
|
|
2252
|
+
(resolve6, reject2) => {
|
|
2126
2253
|
const entry = {
|
|
2127
2254
|
id: callId,
|
|
2128
2255
|
toolName,
|
|
2129
2256
|
input,
|
|
2130
|
-
resolve:
|
|
2257
|
+
resolve: resolve6,
|
|
2131
2258
|
reject: reject2,
|
|
2132
2259
|
channel
|
|
2133
2260
|
};
|
|
@@ -2216,11 +2343,11 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2216
2343
|
}
|
|
2217
2344
|
}
|
|
2218
2345
|
});
|
|
2219
|
-
await new Promise((
|
|
2346
|
+
await new Promise((resolve6, reject2) => {
|
|
2220
2347
|
server2.once("error", reject2);
|
|
2221
2348
|
server2.listen(0, "127.0.0.1", () => {
|
|
2222
2349
|
server2.off("error", reject2);
|
|
2223
|
-
|
|
2350
|
+
resolve6();
|
|
2224
2351
|
});
|
|
2225
2352
|
});
|
|
2226
2353
|
const addr = server2.address();
|
|
@@ -2277,8 +2404,8 @@ async function createProxyMcpServer(tools = DEFAULT_PROXY_TOOLS, timeoutOverride
|
|
|
2277
2404
|
entry.reject(new Error(SERVER_CLOSED_MESSAGE));
|
|
2278
2405
|
}
|
|
2279
2406
|
pending.clear();
|
|
2280
|
-
await new Promise((
|
|
2281
|
-
server2.close(() =>
|
|
2407
|
+
await new Promise((resolve6) => {
|
|
2408
|
+
server2.close(() => resolve6());
|
|
2282
2409
|
});
|
|
2283
2410
|
if (configFilePath) {
|
|
2284
2411
|
try {
|
|
@@ -2433,10 +2560,10 @@ function resolveMcpProxyToolDefs(options) {
|
|
|
2433
2560
|
return { defs, coveredServers };
|
|
2434
2561
|
}
|
|
2435
2562
|
function readBody(req) {
|
|
2436
|
-
return new Promise((
|
|
2563
|
+
return new Promise((resolve6, reject) => {
|
|
2437
2564
|
const chunks = [];
|
|
2438
2565
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
2439
|
-
req.on("end", () =>
|
|
2566
|
+
req.on("end", () => resolve6(Buffer.concat(chunks).toString("utf8")));
|
|
2440
2567
|
req.on("error", reject);
|
|
2441
2568
|
});
|
|
2442
2569
|
}
|
|
@@ -2744,6 +2871,99 @@ function clearCompression(sessionKey2) {
|
|
|
2744
2871
|
compressions.delete(sessionKey2);
|
|
2745
2872
|
}
|
|
2746
2873
|
|
|
2874
|
+
// src/types.ts
|
|
2875
|
+
var READ_ONLY_PERMISSION_MODE = "read-only";
|
|
2876
|
+
|
|
2877
|
+
// src/permission-presets.ts
|
|
2878
|
+
var READ_ONLY_DISALLOWED_CLI_TOOLS = [
|
|
2879
|
+
"Bash",
|
|
2880
|
+
"Write",
|
|
2881
|
+
"Edit",
|
|
2882
|
+
"NotebookEdit",
|
|
2883
|
+
"REPL",
|
|
2884
|
+
"JavaScript",
|
|
2885
|
+
"WebFetch"
|
|
2886
|
+
];
|
|
2887
|
+
var READ_ONLY_DENIED_PROXY_TOOLS = /* @__PURE__ */ new Set([
|
|
2888
|
+
"bash",
|
|
2889
|
+
"write",
|
|
2890
|
+
"edit",
|
|
2891
|
+
"webfetch",
|
|
2892
|
+
"task",
|
|
2893
|
+
"task_batch"
|
|
2894
|
+
]);
|
|
2895
|
+
function dedupe(names) {
|
|
2896
|
+
const out = [];
|
|
2897
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2898
|
+
for (const name of names) {
|
|
2899
|
+
const trimmed = String(name).trim();
|
|
2900
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
2901
|
+
seen.add(trimmed);
|
|
2902
|
+
out.push(trimmed);
|
|
2903
|
+
}
|
|
2904
|
+
return out;
|
|
2905
|
+
}
|
|
2906
|
+
function resolveReadOnly(settings, defaultProxyTools) {
|
|
2907
|
+
const overridden = [];
|
|
2908
|
+
if (settings.permissionMode !== void 0) {
|
|
2909
|
+
overridden.push(
|
|
2910
|
+
`permissionMode: "${settings.permissionMode}" is dropped; read-only sends --restricted instead, which is stricter than every mode except plan and incompatible with bypassPermissions`
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
if (settings.skipPermissions === true) {
|
|
2914
|
+
overridden.push(
|
|
2915
|
+
'skipPermissions: forced to false; the CLI exits with "bypassPermissions not supported in restricted mode" when both are passed'
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
if (settings.controlRequestBehavior === "allow") {
|
|
2919
|
+
overridden.push(
|
|
2920
|
+
'controlRequestBehavior: forced to "deny"; read-only answers every permission request the same way the CLI does under --permission-prompts none'
|
|
2921
|
+
);
|
|
2922
|
+
}
|
|
2923
|
+
if (settings.controlRequestToolBehaviors !== void 0) {
|
|
2924
|
+
overridden.push(
|
|
2925
|
+
"controlRequestToolBehaviors: ignored; a per-tool allow would apply only on CLIs older than 2.1.263, so the preset would mean different things on different versions"
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
const requested = settings.proxyTools ?? defaultProxyTools;
|
|
2929
|
+
const proxyTools = dedupe(requested).filter(
|
|
2930
|
+
(name) => !READ_ONLY_DENIED_PROXY_TOOLS.has(name.toLowerCase())
|
|
2931
|
+
);
|
|
2932
|
+
const dropped = dedupe(requested).filter(
|
|
2933
|
+
(name) => READ_ONLY_DENIED_PROXY_TOOLS.has(name.toLowerCase())
|
|
2934
|
+
);
|
|
2935
|
+
if (dropped.length > 0) {
|
|
2936
|
+
overridden.push(
|
|
2937
|
+
`proxyTools: dropped ${dropped.join(", ")}; those execute in opencode, where no Claude CLI flag reaches them`
|
|
2938
|
+
);
|
|
2939
|
+
}
|
|
2940
|
+
return {
|
|
2941
|
+
preset: "read-only",
|
|
2942
|
+
permissionMode: READ_ONLY_PERMISSION_MODE,
|
|
2943
|
+
skipPermissions: false,
|
|
2944
|
+
proxyTools,
|
|
2945
|
+
extraDisallowedTools: dedupe([
|
|
2946
|
+
...READ_ONLY_DISALLOWED_CLI_TOOLS,
|
|
2947
|
+
...settings.extraDisallowedTools ?? []
|
|
2948
|
+
]),
|
|
2949
|
+
controlRequestBehavior: "deny",
|
|
2950
|
+
controlRequestToolBehaviors: void 0,
|
|
2951
|
+
overridden
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2954
|
+
function resolvePermissionPreset(settings, defaultProxyTools) {
|
|
2955
|
+
const preset = settings.permissionPreset;
|
|
2956
|
+
if (preset === void 0) return null;
|
|
2957
|
+
if (preset === "read-only") return resolveReadOnly(settings, defaultProxyTools);
|
|
2958
|
+
return { unknown: String(preset) };
|
|
2959
|
+
}
|
|
2960
|
+
function isUnknownPreset(resolved) {
|
|
2961
|
+
return resolved !== null && "unknown" in resolved;
|
|
2962
|
+
}
|
|
2963
|
+
function isReadOnlyPermissionMode(mode) {
|
|
2964
|
+
return mode === READ_ONLY_PERMISSION_MODE;
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2747
2967
|
// src/side-question.ts
|
|
2748
2968
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2749
2969
|
var MAX_HISTORY_EXCHANGES = 20;
|
|
@@ -2847,7 +3067,7 @@ async function requestSideQuestion(activeProcess, question, options) {
|
|
|
2847
3067
|
}
|
|
2848
3068
|
});
|
|
2849
3069
|
pendingProcesses.add(proc);
|
|
2850
|
-
return new Promise((
|
|
3070
|
+
return new Promise((resolve6, reject) => {
|
|
2851
3071
|
const event = `side-question:${requestId}`;
|
|
2852
3072
|
let settled = false;
|
|
2853
3073
|
let sent = false;
|
|
@@ -2902,7 +3122,7 @@ async function requestSideQuestion(activeProcess, question, options) {
|
|
|
2902
3122
|
}
|
|
2903
3123
|
settled = true;
|
|
2904
3124
|
cleanup();
|
|
2905
|
-
|
|
3125
|
+
resolve6({ response: result.response, synthetic: result.synthetic });
|
|
2906
3126
|
};
|
|
2907
3127
|
const timer = setTimeout(() => {
|
|
2908
3128
|
fail(new Error(`/btw timed out after ${timeoutMs}ms.`), true);
|
|
@@ -3067,16 +3287,16 @@ function isTurnInFlight(ap) {
|
|
|
3067
3287
|
}
|
|
3068
3288
|
function awaitTurnIdle(ap, timeoutMs) {
|
|
3069
3289
|
if (!ap.turnInFlight) return Promise.resolve(true);
|
|
3070
|
-
return new Promise((
|
|
3290
|
+
return new Promise((resolve6) => {
|
|
3071
3291
|
const wake = () => {
|
|
3072
3292
|
clearTimeout(timer);
|
|
3073
|
-
|
|
3293
|
+
resolve6(true);
|
|
3074
3294
|
};
|
|
3075
3295
|
const timer = setTimeout(() => {
|
|
3076
3296
|
const waiters = ap.turnIdleWaiters ?? [];
|
|
3077
3297
|
const at = waiters.indexOf(wake);
|
|
3078
3298
|
if (at >= 0) waiters.splice(at, 1);
|
|
3079
|
-
|
|
3299
|
+
resolve6(false);
|
|
3080
3300
|
}, timeoutMs);
|
|
3081
3301
|
(ap.turnIdleWaiters ??= []).push(wake);
|
|
3082
3302
|
});
|
|
@@ -3195,14 +3415,14 @@ function hasProcessExited(proc) {
|
|
|
3195
3415
|
}
|
|
3196
3416
|
function waitForProcessExit(proc, timeoutMs) {
|
|
3197
3417
|
if (hasProcessExited(proc)) return Promise.resolve(true);
|
|
3198
|
-
return new Promise((
|
|
3418
|
+
return new Promise((resolve6) => {
|
|
3199
3419
|
const onExit = () => {
|
|
3200
3420
|
clearTimeout(timer);
|
|
3201
|
-
|
|
3421
|
+
resolve6(true);
|
|
3202
3422
|
};
|
|
3203
3423
|
const timer = setTimeout(() => {
|
|
3204
3424
|
proc.off("exit", onExit);
|
|
3205
|
-
|
|
3425
|
+
resolve6(hasProcessExited(proc));
|
|
3206
3426
|
}, timeoutMs);
|
|
3207
3427
|
proc.once("exit", onExit);
|
|
3208
3428
|
});
|
|
@@ -3453,7 +3673,25 @@ function buildCliArgs(opts) {
|
|
|
3453
3673
|
if (model) {
|
|
3454
3674
|
args.push("--model", model);
|
|
3455
3675
|
}
|
|
3456
|
-
|
|
3676
|
+
const readOnly = isReadOnlyPermissionMode(permissionMode);
|
|
3677
|
+
if (readOnly) {
|
|
3678
|
+
if (cliSupportsRestricted(cliVersion ?? null)) {
|
|
3679
|
+
args.push("--restricted");
|
|
3680
|
+
} else {
|
|
3681
|
+
log.warn(
|
|
3682
|
+
'permissionPreset "read-only": this claude CLI has no --restricted (2.1.258+), so file reads are not confined to the working directory. The mutating tools are still refused via --disallowedTools and every permission request is denied.',
|
|
3683
|
+
{ cliVersion: cliVersion?.raw ?? "unknown" }
|
|
3684
|
+
);
|
|
3685
|
+
}
|
|
3686
|
+
if (cliSupportsPermissionPrompts(cliVersion ?? null)) {
|
|
3687
|
+
args.push("--permission-prompts", "none");
|
|
3688
|
+
} else {
|
|
3689
|
+
log.warn(
|
|
3690
|
+
`permissionPreset "read-only": this claude CLI has no --permission-prompts (2.1.263+), so permission requests are denied by the plugin's own can_use_tool handler instead of by the CLI. Same outcome, one layer fewer.`,
|
|
3691
|
+
{ cliVersion: cliVersion?.raw ?? "unknown" }
|
|
3692
|
+
);
|
|
3693
|
+
}
|
|
3694
|
+
} else if (permissionMode) {
|
|
3457
3695
|
args.push("--permission-mode", permissionMode);
|
|
3458
3696
|
}
|
|
3459
3697
|
if (includeSessionId) {
|
|
@@ -3490,7 +3728,7 @@ function buildCliArgs(opts) {
|
|
|
3490
3728
|
if (fastMode && cliSupportsFastMode(cliVersion ?? null)) {
|
|
3491
3729
|
args.push("--settings", JSON.stringify({ fastMode: true }));
|
|
3492
3730
|
}
|
|
3493
|
-
if (skipPermissions && permissionMode !== "plan") {
|
|
3731
|
+
if (skipPermissions && permissionMode !== "plan" && !readOnly) {
|
|
3494
3732
|
args.push("--dangerously-skip-permissions");
|
|
3495
3733
|
}
|
|
3496
3734
|
return args;
|
|
@@ -3642,7 +3880,7 @@ async function waitForSessionIdle(client, sessionID, options = {}) {
|
|
|
3642
3880
|
if (options.stop?.()) return true;
|
|
3643
3881
|
if (await sessionStatus(client, sessionID) !== "busy") return true;
|
|
3644
3882
|
if (Date.now() - started >= timeoutMs) return false;
|
|
3645
|
-
await new Promise((
|
|
3883
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
3646
3884
|
}
|
|
3647
3885
|
}
|
|
3648
3886
|
async function deliverAsideInline(client, sessionID, text, options = {}) {
|
|
@@ -3653,7 +3891,7 @@ async function deliverAsideInline(client, sessionID, text, options = {}) {
|
|
|
3653
3891
|
if (emitAsideInline(sessionID, text)) return true;
|
|
3654
3892
|
if (Date.now() - started >= timeoutMs) return false;
|
|
3655
3893
|
if (await sessionStatus(client, sessionID) !== "busy") return false;
|
|
3656
|
-
await new Promise((
|
|
3894
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
3657
3895
|
}
|
|
3658
3896
|
}
|
|
3659
3897
|
async function waitForAsideProcess(client, sessionID, options = {}) {
|
|
@@ -3674,7 +3912,7 @@ async function waitForAsideProcess(client, sessionID, options = {}) {
|
|
|
3674
3912
|
log.warn("btw: a turn is running but no claude process appeared for it", { sessionID, waitedMs });
|
|
3675
3913
|
return void 0;
|
|
3676
3914
|
}
|
|
3677
|
-
await new Promise((
|
|
3915
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
3678
3916
|
}
|
|
3679
3917
|
}
|
|
3680
3918
|
async function settleSessionBusy(client, sessionID, active, options = {}) {
|
|
@@ -3686,7 +3924,7 @@ async function settleSessionBusy(client, sessionID, active, options = {}) {
|
|
|
3686
3924
|
if (status === "busy") return true;
|
|
3687
3925
|
if (status === "unknown") return isProcessBusy(active);
|
|
3688
3926
|
if (Date.now() - started >= settleMs) return false;
|
|
3689
|
-
await new Promise((
|
|
3927
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
3690
3928
|
}
|
|
3691
3929
|
}
|
|
3692
3930
|
function errorText(error) {
|
|
@@ -3733,10 +3971,10 @@ async function handleBtwCommand(client, input, options = {}) {
|
|
|
3733
3971
|
let inlineDone = false;
|
|
3734
3972
|
let markInlineDelivered = () => {
|
|
3735
3973
|
};
|
|
3736
|
-
const inlineDelivered = new Promise((
|
|
3974
|
+
const inlineDelivered = new Promise((resolve6) => {
|
|
3737
3975
|
markInlineDelivered = () => {
|
|
3738
3976
|
inlineDone = true;
|
|
3739
|
-
|
|
3977
|
+
resolve6("inline");
|
|
3740
3978
|
};
|
|
3741
3979
|
});
|
|
3742
3980
|
if (isSideQuestionPending(active)) {
|
|
@@ -4270,7 +4508,7 @@ async function settleSessionRunState(sessionID, options = {}) {
|
|
|
4270
4508
|
if (state === "busy") return "busy";
|
|
4271
4509
|
if (state !== "unknown") last = state;
|
|
4272
4510
|
if (Date.now() - started >= windowMs) return last;
|
|
4273
|
-
await new Promise((
|
|
4511
|
+
await new Promise((resolve6) => setTimeout(resolve6, pollMs));
|
|
4274
4512
|
}
|
|
4275
4513
|
}
|
|
4276
4514
|
async function getRuntimeMcpStatus() {
|
|
@@ -4753,6 +4991,7 @@ var PLUGIN_NOTE_MARKERS = [
|
|
|
4753
4991
|
RESULT_ERROR_MARKER,
|
|
4754
4992
|
DOCTOR_MARKER,
|
|
4755
4993
|
STREAM_TIMEOUT_MARKER,
|
|
4994
|
+
SILENT_TURN_MARKER,
|
|
4756
4995
|
FAILOVER_MARKER,
|
|
4757
4996
|
ACCOUNT_BLOCK_MARKER
|
|
4758
4997
|
];
|
|
@@ -6530,7 +6769,7 @@ function spawnInteractiveProcess(opts) {
|
|
|
6530
6769
|
|
|
6531
6770
|
// src/prompts.ts
|
|
6532
6771
|
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
6533
|
-
import { homedir as homedir6
|
|
6772
|
+
import { homedir as homedir6 } from "os";
|
|
6534
6773
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
6535
6774
|
import { dirname as dirname5, join as join8 } from "path";
|
|
6536
6775
|
function readPromptFileIfPresent(path10) {
|
|
@@ -6650,9 +6889,9 @@ ${options.compressionSummary.trim()}`
|
|
|
6650
6889
|
if (includeMultiStepHint) parts.push(MULTI_STEP_TASK_HINT);
|
|
6651
6890
|
const content = parts.join("\n\n");
|
|
6652
6891
|
if (!content) return void 0;
|
|
6653
|
-
const path10 = join8(
|
|
6892
|
+
const path10 = join8(pluginTmpDir(), `opencode-cc-sys-${randomUUID5()}.md`);
|
|
6654
6893
|
try {
|
|
6655
|
-
writeFileSync4(path10, content, "utf8");
|
|
6894
|
+
writeFileSync4(path10, content, { encoding: "utf8", mode: 384 });
|
|
6656
6895
|
return path10;
|
|
6657
6896
|
} catch (err) {
|
|
6658
6897
|
log.warn("failed to write system prompt file", { error: String(err) });
|
|
@@ -6753,6 +6992,14 @@ function shouldAutoContinueIncompleteTurn(state, snapshot) {
|
|
|
6753
6992
|
}
|
|
6754
6993
|
return { continue: true, reason: "non-final-progress" };
|
|
6755
6994
|
}
|
|
6995
|
+
function isSilentTurn(snapshot) {
|
|
6996
|
+
if (snapshot.enabled === false) return false;
|
|
6997
|
+
if (snapshot.compactionMode) return false;
|
|
6998
|
+
if (snapshot.isError) return false;
|
|
6999
|
+
if (snapshot.aborted) return false;
|
|
7000
|
+
if (snapshot.sawQuestion) return false;
|
|
7001
|
+
return !snapshot.sawVisibleText && !snapshot.sawToolActivity && !snapshot.sawProxyActivity;
|
|
7002
|
+
}
|
|
6756
7003
|
function makeAutoContinueMessage() {
|
|
6757
7004
|
return JSON.stringify({
|
|
6758
7005
|
type: "user",
|
|
@@ -7786,7 +8033,7 @@ var ClaudeCodeLanguageModel = class {
|
|
|
7786
8033
|
const toolCalls = [];
|
|
7787
8034
|
const toolCallStreams = /* @__PURE__ */ new Map();
|
|
7788
8035
|
let gotPartialEvents = false;
|
|
7789
|
-
const result = await new Promise((
|
|
8036
|
+
const result = await new Promise((resolve6, reject) => {
|
|
7790
8037
|
const cleanup = () => {
|
|
7791
8038
|
try {
|
|
7792
8039
|
if (!proc.killed && proc.exitCode === null) proc.kill();
|
|
@@ -7941,7 +8188,7 @@ ${plan}
|
|
|
7941
8188
|
...turnStatsLogPayload(extractTurnStats(msg))
|
|
7942
8189
|
});
|
|
7943
8190
|
cleanup();
|
|
7944
|
-
|
|
8191
|
+
resolve6({
|
|
7945
8192
|
...resultMeta,
|
|
7946
8193
|
text: responseText,
|
|
7947
8194
|
thinking: thinkingText,
|
|
@@ -7953,7 +8200,7 @@ ${plan}
|
|
|
7953
8200
|
});
|
|
7954
8201
|
rl.on("close", () => {
|
|
7955
8202
|
cleanup();
|
|
7956
|
-
|
|
8203
|
+
resolve6({
|
|
7957
8204
|
...resultMeta,
|
|
7958
8205
|
text: responseText,
|
|
7959
8206
|
thinking: thinkingText,
|
|
@@ -8718,6 +8965,10 @@ ${plan}
|
|
|
8718
8965
|
let hadReasoningSinceContinue = false;
|
|
8719
8966
|
let hadToolActivitySinceContinue = false;
|
|
8720
8967
|
let hadProxyActivitySinceContinue = false;
|
|
8968
|
+
let sawVisibleText = false;
|
|
8969
|
+
let sawReasoning = false;
|
|
8970
|
+
let sawToolActivity = false;
|
|
8971
|
+
let sawProxyActivity = false;
|
|
8721
8972
|
let lastStopReason = null;
|
|
8722
8973
|
const autoContinueState = {
|
|
8723
8974
|
enabled: autoContinueEnabledFor(
|
|
@@ -9009,18 +9260,22 @@ ${plan}
|
|
|
9009
9260
|
const noteVisibleText = (text) => {
|
|
9010
9261
|
visibleTextSinceContinue += text;
|
|
9011
9262
|
lastVisibleTextSinceContinue += text;
|
|
9263
|
+
if (text.length > 0) sawVisibleText = true;
|
|
9012
9264
|
};
|
|
9013
9265
|
const resetLastVisibleTextBlock = () => {
|
|
9014
9266
|
lastVisibleTextSinceContinue = "";
|
|
9015
9267
|
};
|
|
9016
9268
|
const noteReasoning = () => {
|
|
9017
9269
|
hadReasoningSinceContinue = true;
|
|
9270
|
+
sawReasoning = true;
|
|
9018
9271
|
};
|
|
9019
9272
|
const noteToolActivity = () => {
|
|
9020
9273
|
hadToolActivitySinceContinue = true;
|
|
9274
|
+
sawToolActivity = true;
|
|
9021
9275
|
};
|
|
9022
9276
|
const noteProxyActivity = () => {
|
|
9023
9277
|
hadProxyActivitySinceContinue = true;
|
|
9278
|
+
sawProxyActivity = true;
|
|
9024
9279
|
};
|
|
9025
9280
|
const resetAutoContinueWindow = () => {
|
|
9026
9281
|
visibleTextSinceContinue = "";
|
|
@@ -9127,6 +9382,29 @@ ${plan}
|
|
|
9127
9382
|
});
|
|
9128
9383
|
}
|
|
9129
9384
|
}
|
|
9385
|
+
if (isSilentTurn({
|
|
9386
|
+
enabled: autoContinueState.enabled,
|
|
9387
|
+
compactionMode,
|
|
9388
|
+
sawVisibleText,
|
|
9389
|
+
sawToolActivity,
|
|
9390
|
+
sawProxyActivity,
|
|
9391
|
+
isError: msg.is_error === true || resultFailure !== void 0,
|
|
9392
|
+
aborted: autoContinueState.aborted,
|
|
9393
|
+
sawQuestion: autoContinueState.sawAskUserQuestion
|
|
9394
|
+
})) {
|
|
9395
|
+
log.notice("claude finished the turn without a reply", {
|
|
9396
|
+
sessionKey: sk,
|
|
9397
|
+
stopReason: lastStopReason,
|
|
9398
|
+
hadReasoning: sawReasoning,
|
|
9399
|
+
attempts: autoContinueState.attempts
|
|
9400
|
+
});
|
|
9401
|
+
controller.enqueue({
|
|
9402
|
+
type: "text-delta",
|
|
9403
|
+
id: startTextBlock(),
|
|
9404
|
+
delta: formatSilentTurnNote(sawReasoning)
|
|
9405
|
+
});
|
|
9406
|
+
endTextBlock();
|
|
9407
|
+
}
|
|
9130
9408
|
controller.enqueue({
|
|
9131
9409
|
type: "finish",
|
|
9132
9410
|
finishReason: resultFailure ? { unified: "error", raw: resultFailure } : toFinishReason2("stop"),
|
|
@@ -9432,7 +9710,7 @@ ${plan}
|
|
|
9432
9710
|
if (!skip) {
|
|
9433
9711
|
toolCallsById.set(tc.id, {
|
|
9434
9712
|
id: tc.id,
|
|
9435
|
-
name:
|
|
9713
|
+
name: mappedName,
|
|
9436
9714
|
input: parsedInput
|
|
9437
9715
|
});
|
|
9438
9716
|
if (!executed) skipResultForIds.add(tc.id);
|
|
@@ -9614,7 +9892,7 @@ ${plan}
|
|
|
9614
9892
|
if (!skip) {
|
|
9615
9893
|
toolCallsById.set(block2.id, {
|
|
9616
9894
|
id: block2.id,
|
|
9617
|
-
name:
|
|
9895
|
+
name: mappedName,
|
|
9618
9896
|
input: parsedInput
|
|
9619
9897
|
});
|
|
9620
9898
|
if (!executed) skipResultForIds.add(block2.id);
|
|
@@ -10202,14 +10480,15 @@ ${plan}
|
|
|
10202
10480
|
|
|
10203
10481
|
// src/cleanup-stale.ts
|
|
10204
10482
|
import {
|
|
10205
|
-
existsSync as
|
|
10483
|
+
existsSync as existsSync3,
|
|
10484
|
+
mkdirSync as mkdirSync4,
|
|
10206
10485
|
readFileSync as readFileSync6,
|
|
10207
10486
|
realpathSync as realpathSync2,
|
|
10208
10487
|
rmSync as rmSync3,
|
|
10209
10488
|
writeFileSync as writeFileSync5
|
|
10210
10489
|
} from "fs";
|
|
10211
10490
|
import { homedir as homedir7 } from "os";
|
|
10212
|
-
import { join as join9, resolve as
|
|
10491
|
+
import { dirname as dirname6, join as join9, resolve as resolve5 } from "path";
|
|
10213
10492
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10214
10493
|
var STALE_PACKAGE_NAME = "opencode-claude-code-plugin";
|
|
10215
10494
|
var SUSPECT_DESCRIPTION_TOKEN = "Claude Code";
|
|
@@ -10228,7 +10507,7 @@ function userOpencodeJsonPath() {
|
|
|
10228
10507
|
}
|
|
10229
10508
|
function userIntendsToUseUnscoped() {
|
|
10230
10509
|
const cfg = userOpencodeJsonPath();
|
|
10231
|
-
if (!
|
|
10510
|
+
if (!existsSync3(cfg)) return false;
|
|
10232
10511
|
try {
|
|
10233
10512
|
const json = JSON.parse(readFileSync6(cfg, "utf8"));
|
|
10234
10513
|
const plugins = json.plugin;
|
|
@@ -10243,17 +10522,57 @@ function userIntendsToUseUnscoped() {
|
|
|
10243
10522
|
function ourLoadedDir() {
|
|
10244
10523
|
try {
|
|
10245
10524
|
const filePath = fileURLToPath3(import.meta.url);
|
|
10246
|
-
return realpathSync2(
|
|
10525
|
+
return realpathSync2(resolve5(filePath, "..", ".."));
|
|
10526
|
+
} catch {
|
|
10527
|
+
return null;
|
|
10528
|
+
}
|
|
10529
|
+
}
|
|
10530
|
+
function ourVersion(dir) {
|
|
10531
|
+
if (!dir) return null;
|
|
10532
|
+
try {
|
|
10533
|
+
const pkg = JSON.parse(readFileSync6(join9(dir, "package.json"), "utf8"));
|
|
10534
|
+
return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
|
|
10535
|
+
} catch {
|
|
10536
|
+
return null;
|
|
10537
|
+
}
|
|
10538
|
+
}
|
|
10539
|
+
function markerPath() {
|
|
10540
|
+
const stateRoot = process.env.XDG_STATE_HOME ?? join9(homedir7(), ".local", "state");
|
|
10541
|
+
return join9(stateRoot, "opencode-claude-code-plugin", "cleanup-stale.json");
|
|
10542
|
+
}
|
|
10543
|
+
function markerVersion() {
|
|
10544
|
+
try {
|
|
10545
|
+
const marker = JSON.parse(readFileSync6(markerPath(), "utf8"));
|
|
10546
|
+
return typeof marker.version === "string" ? marker.version : null;
|
|
10247
10547
|
} catch {
|
|
10248
10548
|
return null;
|
|
10249
10549
|
}
|
|
10250
10550
|
}
|
|
10551
|
+
function writeMarker(version) {
|
|
10552
|
+
const file = markerPath();
|
|
10553
|
+
try {
|
|
10554
|
+
mkdirSync4(dirname6(file), { recursive: true });
|
|
10555
|
+
writeFileSync5(
|
|
10556
|
+
file,
|
|
10557
|
+
JSON.stringify({ version, at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n",
|
|
10558
|
+
"utf8"
|
|
10559
|
+
);
|
|
10560
|
+
} catch (err) {
|
|
10561
|
+
log.warn("cleanup-stale: could not record the sweep marker", {
|
|
10562
|
+
file,
|
|
10563
|
+
error: String(err)
|
|
10564
|
+
});
|
|
10565
|
+
}
|
|
10566
|
+
}
|
|
10251
10567
|
function cleanupStaleUnscopedInstall() {
|
|
10252
10568
|
if (alreadyRan) return;
|
|
10253
10569
|
alreadyRan = true;
|
|
10254
10570
|
if (process.env.OPENCODE_CLAUDE_CODE_PLUGIN_NO_CLEANUP === "1") return;
|
|
10255
10571
|
if (userIntendsToUseUnscoped()) return;
|
|
10256
10572
|
const ourDir = ourLoadedDir();
|
|
10573
|
+
const version = ourVersion(ourDir);
|
|
10574
|
+
const forced = process.env.OPENCODE_CLAUDE_CODE_PLUGIN_FORCE_CLEANUP === "1";
|
|
10575
|
+
if (version && !forced && markerVersion() === version) return;
|
|
10257
10576
|
for (const cacheRoot of candidateCacheRoots()) {
|
|
10258
10577
|
try {
|
|
10259
10578
|
cleanupOne(cacheRoot, ourDir);
|
|
@@ -10264,11 +10583,12 @@ function cleanupStaleUnscopedInstall() {
|
|
|
10264
10583
|
});
|
|
10265
10584
|
}
|
|
10266
10585
|
}
|
|
10586
|
+
if (version) writeMarker(version);
|
|
10267
10587
|
}
|
|
10268
10588
|
function cleanupOne(cacheRoot, ourDir) {
|
|
10269
|
-
if (!
|
|
10589
|
+
if (!existsSync3(cacheRoot)) return;
|
|
10270
10590
|
const stalePath = join9(cacheRoot, "node_modules", STALE_PACKAGE_NAME);
|
|
10271
|
-
if (!
|
|
10591
|
+
if (!existsSync3(stalePath)) return;
|
|
10272
10592
|
let realStalePath = stalePath;
|
|
10273
10593
|
try {
|
|
10274
10594
|
realStalePath = realpathSync2(stalePath);
|
|
@@ -10276,7 +10596,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
10276
10596
|
}
|
|
10277
10597
|
if (ourDir && realStalePath === ourDir) return;
|
|
10278
10598
|
const pkgJsonPath = join9(stalePath, "package.json");
|
|
10279
|
-
if (!
|
|
10599
|
+
if (!existsSync3(pkgJsonPath)) return;
|
|
10280
10600
|
let pkg = {};
|
|
10281
10601
|
try {
|
|
10282
10602
|
pkg = JSON.parse(readFileSync6(pkgJsonPath, "utf8"));
|
|
@@ -10296,7 +10616,7 @@ function cleanupOne(cacheRoot, ourDir) {
|
|
|
10296
10616
|
return;
|
|
10297
10617
|
}
|
|
10298
10618
|
const cachePkgJson = join9(cacheRoot, "package.json");
|
|
10299
|
-
if (!
|
|
10619
|
+
if (!existsSync3(cachePkgJson)) return;
|
|
10300
10620
|
try {
|
|
10301
10621
|
const cfg = JSON.parse(readFileSync6(cachePkgJson, "utf8"));
|
|
10302
10622
|
if (cfg?.dependencies?.[STALE_PACKAGE_NAME]) {
|
|
@@ -10781,6 +11101,28 @@ function warnIfPlanModeCannotExit(permissionMode) {
|
|
|
10781
11101
|
{ permissionMode, measuredOn: "claude-code 2.1.258" }
|
|
10782
11102
|
);
|
|
10783
11103
|
}
|
|
11104
|
+
function applyPermissionPreset(settings, defaultProxyTools) {
|
|
11105
|
+
const resolved = resolvePermissionPreset(settings, defaultProxyTools);
|
|
11106
|
+
if (resolved === null) return null;
|
|
11107
|
+
if (isUnknownPreset(resolved)) {
|
|
11108
|
+
log.warn(
|
|
11109
|
+
"unknown permissionPreset; no preset applied and every permission setting is left exactly as configured",
|
|
11110
|
+
{ permissionPreset: resolved.unknown, known: ["read-only"] }
|
|
11111
|
+
);
|
|
11112
|
+
return null;
|
|
11113
|
+
}
|
|
11114
|
+
log.notice(`permission preset "${resolved.preset}" applied`, {
|
|
11115
|
+
permissionMode: resolved.permissionMode,
|
|
11116
|
+
skipPermissions: resolved.skipPermissions,
|
|
11117
|
+
proxyTools: resolved.proxyTools,
|
|
11118
|
+
disallowedTools: resolved.extraDisallowedTools,
|
|
11119
|
+
controlRequestBehavior: resolved.controlRequestBehavior
|
|
11120
|
+
});
|
|
11121
|
+
for (const line of resolved.overridden) {
|
|
11122
|
+
log.notice(`permission preset "${resolved.preset}" overrode ${line}`);
|
|
11123
|
+
}
|
|
11124
|
+
return resolved;
|
|
11125
|
+
}
|
|
10784
11126
|
function createClaudeCode(settings = {}) {
|
|
10785
11127
|
if (settings.logging) {
|
|
10786
11128
|
configureLogger({
|
|
@@ -10791,10 +11133,11 @@ function createClaudeCode(settings = {}) {
|
|
|
10791
11133
|
});
|
|
10792
11134
|
}
|
|
10793
11135
|
warnIfAnthropicApiKey(settings.ignoreAnthropicApiKey);
|
|
10794
|
-
|
|
11136
|
+
const preset = applyPermissionPreset(settings, DEFAULT_PROXY_TOOL_NAMES);
|
|
11137
|
+
warnIfPlanModeCannotExit(preset ? void 0 : settings.permissionMode);
|
|
10795
11138
|
const cliPath = settings.cliPath ?? process.env.CLAUDE_CLI_PATH ?? "claude";
|
|
10796
11139
|
const providerName = settings.providerID ?? settings.name ?? "claude-code";
|
|
10797
|
-
const proxyTools = settings.proxyTools ?? [...DEFAULT_PROXY_TOOL_NAMES];
|
|
11140
|
+
const proxyTools = preset?.proxyTools ?? settings.proxyTools ?? [...DEFAULT_PROXY_TOOL_NAMES];
|
|
10798
11141
|
const createModel = (modelId) => {
|
|
10799
11142
|
return new ClaudeCodeLanguageModel(modelId, {
|
|
10800
11143
|
provider: providerName,
|
|
@@ -10807,18 +11150,19 @@ function createClaudeCode(settings = {}) {
|
|
|
10807
11150
|
baseCliPath: settings.baseCliPath ?? cliPath,
|
|
10808
11151
|
accountFailover: settings.accountFailover ?? "ask",
|
|
10809
11152
|
providerID: settings.providerID,
|
|
10810
|
-
skipPermissions: settings.skipPermissions ?? true,
|
|
10811
|
-
permissionMode: settings.permissionMode,
|
|
11153
|
+
skipPermissions: preset?.skipPermissions ?? settings.skipPermissions ?? true,
|
|
11154
|
+
permissionMode: preset?.permissionMode ?? settings.permissionMode,
|
|
11155
|
+
permissionPreset: preset?.preset,
|
|
10812
11156
|
mcpConfig: settings.mcpConfig,
|
|
10813
11157
|
strictMcpConfig: settings.strictMcpConfig,
|
|
10814
11158
|
bridgeOpencodeMcp: settings.bridgeOpencodeMcp ?? true,
|
|
10815
|
-
controlRequestBehavior: settings.controlRequestBehavior ?? "allow",
|
|
10816
|
-
controlRequestToolBehaviors: settings.controlRequestToolBehaviors,
|
|
11159
|
+
controlRequestBehavior: preset?.controlRequestBehavior ?? settings.controlRequestBehavior ?? "allow",
|
|
11160
|
+
controlRequestToolBehaviors: preset ? void 0 : settings.controlRequestToolBehaviors,
|
|
10817
11161
|
controlRequestDenyMessage: settings.controlRequestDenyMessage,
|
|
10818
11162
|
proxyTools,
|
|
10819
11163
|
proxyOpencodeTools: settings.proxyOpencodeTools,
|
|
10820
11164
|
stripContextReminders: settings.stripContextReminders === true,
|
|
10821
|
-
extraDisallowedTools: settings.extraDisallowedTools,
|
|
11165
|
+
extraDisallowedTools: preset?.extraDisallowedTools ?? settings.extraDisallowedTools,
|
|
10822
11166
|
proxyToolTimeoutMs: settings.proxyToolTimeoutMs,
|
|
10823
11167
|
planModeQuestion: settings.planModeQuestion ?? false,
|
|
10824
11168
|
webSearch: settings.webSearch,
|
|
@@ -11142,6 +11486,7 @@ export {
|
|
|
11142
11486
|
ClaudeCodeLanguageModel,
|
|
11143
11487
|
DEFAULT_PROXY_TOOL_NAMES,
|
|
11144
11488
|
_resetPlanModeWarningForTests,
|
|
11489
|
+
applyPermissionPreset,
|
|
11145
11490
|
bridgeOpencodeMcp,
|
|
11146
11491
|
buildAgentRegistry,
|
|
11147
11492
|
claudeCodeProviders,
|