@wrongstack/core 0.276.4 → 0.277.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/dist/{agent-bridge-D7A-eu3C.d.ts → agent-bridge-BFJ2ODzI.d.ts} +1 -1
- package/dist/{agent-subagent-runner-CEuw4ATz.d.ts → agent-subagent-runner-BimKihiC.d.ts} +7 -7
- package/dist/{brain-BLOyN5ZP.d.ts → brain-CCfuEOdp.d.ts} +1 -1
- package/dist/{compactor-DcBpaJsI.d.ts → compactor-D3BGw26y.d.ts} +1 -1
- package/dist/{config-Bf5mj-ad.d.ts → config-DAOjriz9.d.ts} +1 -1
- package/dist/{context-CLnUMW5g.d.ts → context-DPlA6kid.d.ts} +5 -6
- package/dist/coordination/index.d.ts +17 -17
- package/dist/coordination/index.js +38 -14
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +27 -27
- package/dist/defaults/index.js +177 -93
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +15 -15
- package/dist/execution/index.js +13 -1
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{global-mailbox-Iqfkgmwu.d.ts → global-mailbox-Dr4cTKqL.d.ts} +1 -1
- package/dist/{goal-store-DGb6b5Ed.d.ts → goal-store-C1uH4srH.d.ts} +1 -1
- package/dist/hq/index.d.ts +5 -5
- package/dist/{index-Cn0NOshr.d.ts → index-DJXj-dcr.d.ts} +5 -5
- package/dist/{index-L4RZN9jJ.d.ts → index-cMEmzCVN.d.ts} +23 -5
- package/dist/index.d.ts +41 -41
- package/dist/index.js +220 -111
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +4 -1
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +11 -11
- package/dist/{mcp-servers-CuZGf9fI.d.ts → mcp-servers-CFb60-pH.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/{models-registry-8XOdxWQu.d.ts → models-registry-5Ufn7f2m.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CiRtKVTk.d.ts → multi-agent-coordinator-CcrcncvG.d.ts} +1 -1
- package/dist/{null-fleet-bus-d9G-bVy9.d.ts → null-fleet-bus-C9KsYyrI.d.ts} +13 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-BhIb6mtd.d.ts → path-resolver-CEeX9I7O.d.ts} +3 -3
- package/dist/{permission-BCbQDR2s.d.ts → permission-DbsGOA1C.d.ts} +7 -6
- package/dist/{permission-policy-C0ikndX_.d.ts → permission-policy-BpEea3r7.d.ts} +12 -14
- package/dist/{pipeline-Dl6XbfE7.d.ts → pipeline-CEjBjzVA.d.ts} +2 -2
- package/dist/{provider-model-resolve-B70epO19.d.ts → provider-model-resolve-BpfXp3Jj.d.ts} +3 -3
- package/dist/{provider-runner-DZ808MSM.d.ts → provider-runner-CnOSr5BN.d.ts} +3 -3
- package/dist/{retry-policy-Dt3_z8Aj.d.ts → retry-policy-Git9WF6d.d.ts} +1 -1
- package/dist/sdd/index.d.ts +9 -9
- package/dist/{secret-vault-BUJ2d1gB.d.ts → secret-vault-DDSMHqIm.d.ts} +1 -1
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +173 -94
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-BCkWgdwy.d.ts → selector-Cq72C0Oy.d.ts} +1 -1
- package/dist/{session-event-bridge-CMvIO59_.d.ts → session-event-bridge-DG94B3Bk.d.ts} +1 -1
- package/dist/{session-reader-C8aiChUu.d.ts → session-reader-BzT-iMQT.d.ts} +1 -1
- package/dist/storage/index.d.ts +11 -11
- package/dist/{strategy-compactor-DI1OHVbB.d.ts → strategy-compactor-Bt_ZH6R0.d.ts} +10 -10
- package/dist/{todos-checkpoint-Ddd2CGr0.d.ts → todos-checkpoint-CH1pcua9.d.ts} +5 -5
- package/dist/{tool-executor-Bmd5Ygoo.d.ts → tool-executor-SVFq7IOR.d.ts} +9 -9
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.js +5 -6
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +19 -19
- package/dist/types/index.js +13 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +17 -3
- package/dist/utils/index.js +5 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/{worktree-manager-DBdl_5rs.d.ts → worktree-manager-C4YIf1Fa.d.ts} +1 -1
- package/instructions/leader-after-task.md +6 -0
- package/package.json +2 -2
- package/skills/output-standards/SKILL.md +1 -0
- package/skills/research-web/SKILL.md +1 -1
package/dist/security/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
|
|
2
2
|
import * as fs2 from 'fs';
|
|
3
3
|
import * as fs from 'fs/promises';
|
|
4
|
-
import * as
|
|
4
|
+
import * as path from 'path';
|
|
5
5
|
|
|
6
6
|
// src/security/secret-scrubber.ts
|
|
7
7
|
var PATTERNS = [
|
|
@@ -192,9 +192,9 @@ var DefaultSecretScrubber = class {
|
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
194
|
async function atomicWrite(targetPath, content, opts = {}) {
|
|
195
|
-
const dir =
|
|
195
|
+
const dir = path.dirname(targetPath);
|
|
196
196
|
await fs.mkdir(dir, { recursive: true });
|
|
197
|
-
const tmp =
|
|
197
|
+
const tmp = path.join(dir, `.${path.basename(targetPath)}.${randomBytes(6).toString("hex")}.tmp`);
|
|
198
198
|
try {
|
|
199
199
|
if (typeof content === "string") {
|
|
200
200
|
await fs.writeFile(tmp, content, { flag: "wx", encoding: opts.encoding ?? "utf8" });
|
|
@@ -748,7 +748,7 @@ var DefaultSecretVault = class {
|
|
|
748
748
|
const oldVersion = this._keyVersion;
|
|
749
749
|
const newKey = randomBytes(KEY_BYTES);
|
|
750
750
|
const newVersion = oldVersion + 1;
|
|
751
|
-
fs2.mkdirSync(
|
|
751
|
+
fs2.mkdirSync(path.dirname(this.keyFile), { recursive: true });
|
|
752
752
|
const passphrase = getVaultPassphrase();
|
|
753
753
|
if (passphrase) {
|
|
754
754
|
fs2.writeFileSync(this.keyFile, wrapDataKey(newKey, newVersion, passphrase), { mode: 384 });
|
|
@@ -832,7 +832,7 @@ var DefaultSecretVault = class {
|
|
|
832
832
|
} catch (err) {
|
|
833
833
|
if (err.code !== "ENOENT") throw err;
|
|
834
834
|
}
|
|
835
|
-
fs2.mkdirSync(
|
|
835
|
+
fs2.mkdirSync(path.dirname(this.keyFile), { recursive: true });
|
|
836
836
|
const key = randomBytes(KEY_BYTES);
|
|
837
837
|
const passphrase = getVaultPassphrase();
|
|
838
838
|
const initialBytes = passphrase ? wrapDataKey(key, 1, passphrase) : key;
|
|
@@ -931,7 +931,7 @@ async function rewriteConfigEncrypted(configPath, vault, patch) {
|
|
|
931
931
|
}
|
|
932
932
|
const merged = deepMerge(current, patch ?? {});
|
|
933
933
|
const encrypted = encryptConfigSecrets(merged, vault);
|
|
934
|
-
await fs.mkdir(
|
|
934
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
935
935
|
await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
936
936
|
await restrictFilePermissions(configPath);
|
|
937
937
|
}
|
|
@@ -1101,6 +1101,8 @@ var ToolCapabilities = {
|
|
|
1101
1101
|
SHELL_ARBITRARY: "shell.arbitrary",
|
|
1102
1102
|
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
1103
1103
|
SHELL_RESTRICTED: "shell.restricted",
|
|
1104
|
+
/** Can run a restricted project formatter/linter-style command. */
|
|
1105
|
+
SHELL_EXEC: "shell.exec",
|
|
1104
1106
|
/** Can read files inside the project (and possibly outside via symlinks if not guarded). */
|
|
1105
1107
|
FS_READ: "fs.read",
|
|
1106
1108
|
/** Can write / modify / delete files inside the project. */
|
|
@@ -1109,6 +1111,20 @@ var ToolCapabilities = {
|
|
|
1109
1111
|
FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
|
|
1110
1112
|
/** Can perform outbound network requests. */
|
|
1111
1113
|
NET_OUTBOUND: "net.outbound",
|
|
1114
|
+
/** Can mutate in-memory session todos only. */
|
|
1115
|
+
SESSION_TODO: "session.todo",
|
|
1116
|
+
/** Can mutate in-memory session mode only. */
|
|
1117
|
+
SESSION_MODE: "session.mode",
|
|
1118
|
+
/** Can inspect registered tool metadata. */
|
|
1119
|
+
TOOL_META: "tool.meta",
|
|
1120
|
+
/** Can invoke arbitrary registered tools through a meta-tool. */
|
|
1121
|
+
TOOL_MUTATE_ANY: "tool.mutate.any",
|
|
1122
|
+
/** Can read persistent memory. */
|
|
1123
|
+
MEMORY_READ: "memory.read",
|
|
1124
|
+
/** Can write persistent memory. */
|
|
1125
|
+
MEMORY_WRITE: "memory.write",
|
|
1126
|
+
/** Can delete persistent memory. */
|
|
1127
|
+
MEMORY_DELETE: "memory.delete",
|
|
1112
1128
|
/** Proxies tools from external MCP servers (unknown capability). */
|
|
1113
1129
|
MCP_PROXY: "mcp.proxy",
|
|
1114
1130
|
/** Can spawn or manage subagents / multi-agent tasks. */
|
|
@@ -1127,8 +1143,12 @@ var ToolCapabilities = {
|
|
|
1127
1143
|
var DANGEROUS_FOR_SUBAGENTS = [
|
|
1128
1144
|
ToolCapabilities.SHELL_ARBITRARY,
|
|
1129
1145
|
ToolCapabilities.SHELL_RESTRICTED,
|
|
1146
|
+
ToolCapabilities.SHELL_EXEC,
|
|
1130
1147
|
ToolCapabilities.FS_WRITE,
|
|
1131
1148
|
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
1149
|
+
ToolCapabilities.TOOL_MUTATE_ANY,
|
|
1150
|
+
ToolCapabilities.MEMORY_WRITE,
|
|
1151
|
+
ToolCapabilities.MEMORY_DELETE,
|
|
1132
1152
|
ToolCapabilities.MCP_PROXY,
|
|
1133
1153
|
ToolCapabilities.SUBAGENT_SPAWN,
|
|
1134
1154
|
ToolCapabilities.CONFIG_MUTATE,
|
|
@@ -1138,8 +1158,12 @@ var DANGEROUS_FOR_SUBAGENTS = [
|
|
|
1138
1158
|
ToolCapabilities.FS_READ,
|
|
1139
1159
|
ToolCapabilities.FS_WRITE,
|
|
1140
1160
|
ToolCapabilities.NET_OUTBOUND,
|
|
1161
|
+
ToolCapabilities.SESSION_TODO,
|
|
1162
|
+
ToolCapabilities.TOOL_META,
|
|
1163
|
+
ToolCapabilities.MEMORY_READ,
|
|
1141
1164
|
ToolCapabilities.SHELL_ARBITRARY,
|
|
1142
1165
|
ToolCapabilities.SHELL_RESTRICTED,
|
|
1166
|
+
ToolCapabilities.SHELL_EXEC,
|
|
1143
1167
|
ToolCapabilities.PACKAGE_INSTALL
|
|
1144
1168
|
];
|
|
1145
1169
|
function hasDangerousCapabilityForSubagents(toolOrCaps) {
|
|
@@ -1198,19 +1222,51 @@ var LruCache = class {
|
|
|
1198
1222
|
return this.store.size;
|
|
1199
1223
|
}
|
|
1200
1224
|
};
|
|
1201
|
-
var
|
|
1202
|
-
/\
|
|
1203
|
-
|
|
1204
|
-
/\
|
|
1205
|
-
|
|
1206
|
-
/\
|
|
1207
|
-
|
|
1208
|
-
/\
|
|
1209
|
-
|
|
1210
|
-
|
|
1225
|
+
var CATASTROPHIC_PATTERNS = [
|
|
1226
|
+
/\b(?:mkfs(?:\.[a-z0-9]+)?|mke2fs|newfs)\b/i,
|
|
1227
|
+
// make a filesystem — wipes a partition
|
|
1228
|
+
/\bformat\s+[A-Za-z]:/i,
|
|
1229
|
+
// format C: — wipes a Windows volume
|
|
1230
|
+
/\bdiskpart\b/i,
|
|
1231
|
+
// Windows partition editor
|
|
1232
|
+
/\bdd\b[^|]*\bof=(?:\/dev\/|\\\\[.?]\\)/i,
|
|
1233
|
+
// dd writing straight to a raw device
|
|
1234
|
+
/>\s*\/dev\/(?:sd|hd|nvme|disk|mapper|vd)/i,
|
|
1235
|
+
// redirect into a raw block device
|
|
1236
|
+
/:\(\)\s*\{\s*:\|:&\s*\}\s*;/
|
|
1237
|
+
// classic fork bomb
|
|
1211
1238
|
];
|
|
1212
|
-
var
|
|
1213
|
-
|
|
1239
|
+
var CATASTROPHIC_POSIX_ROOTS = /* @__PURE__ */ new Set([
|
|
1240
|
+
"/etc",
|
|
1241
|
+
"/usr",
|
|
1242
|
+
"/bin",
|
|
1243
|
+
"/sbin",
|
|
1244
|
+
"/lib",
|
|
1245
|
+
"/lib64",
|
|
1246
|
+
"/var",
|
|
1247
|
+
"/boot",
|
|
1248
|
+
"/dev",
|
|
1249
|
+
"/sys",
|
|
1250
|
+
"/proc",
|
|
1251
|
+
"/opt",
|
|
1252
|
+
"/root",
|
|
1253
|
+
"/home",
|
|
1254
|
+
"/srv",
|
|
1255
|
+
"/run",
|
|
1256
|
+
"/system",
|
|
1257
|
+
"/library",
|
|
1258
|
+
"/applications",
|
|
1259
|
+
"/users"
|
|
1260
|
+
]);
|
|
1261
|
+
var CATASTROPHIC_WIN_SUBDIRS = /* @__PURE__ */ new Set([
|
|
1262
|
+
"windows",
|
|
1263
|
+
"system32",
|
|
1264
|
+
"winnt",
|
|
1265
|
+
"program files",
|
|
1266
|
+
"program files (x86)",
|
|
1267
|
+
"programdata",
|
|
1268
|
+
"users"
|
|
1269
|
+
]);
|
|
1214
1270
|
var SHELL_OPERATORS = /* @__PURE__ */ new Set(["&&", "||", "|", ";", ">", ">>", "<", "2>", "2>>"]);
|
|
1215
1271
|
function getInputString(input, key) {
|
|
1216
1272
|
if (!input || typeof input !== "object") return void 0;
|
|
@@ -1220,27 +1276,28 @@ function getInputString(input, key) {
|
|
|
1220
1276
|
function pathLooksInsideProject(rawPath, projectRoot) {
|
|
1221
1277
|
if (!projectRoot) return false;
|
|
1222
1278
|
if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
|
|
1223
|
-
const resolved =
|
|
1224
|
-
const relative2 =
|
|
1225
|
-
return !!relative2 && !relative2.startsWith("..") && !
|
|
1279
|
+
const resolved = path.resolve(projectRoot, rawPath);
|
|
1280
|
+
const relative2 = path.relative(projectRoot, resolved);
|
|
1281
|
+
return !!relative2 && !relative2.startsWith("..") && !path.isAbsolute(relative2);
|
|
1226
1282
|
}
|
|
1227
1283
|
function tokenizeShell(command) {
|
|
1228
1284
|
return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
|
|
1229
1285
|
}
|
|
1230
|
-
function
|
|
1231
|
-
|
|
1232
|
-
if (
|
|
1233
|
-
if (
|
|
1234
|
-
|
|
1235
|
-
if (
|
|
1286
|
+
function isCatastrophicDeleteTarget(rawTarget) {
|
|
1287
|
+
const t = rawTarget.replace(/^['"]|['"]$/g, "").trim();
|
|
1288
|
+
if (!t) return false;
|
|
1289
|
+
if (t === "*" || t === "." || t === "./" || t === ".\\" || t === "./*" || t === ".\\*") return true;
|
|
1290
|
+
const s = t.replace(/[\\/]\*+$/, "").replace(/[\\/]+$/, "");
|
|
1291
|
+
if (s === "") return true;
|
|
1292
|
+
if (s === "~" || /^\$HOME$/i.test(s) || /^%USERPROFILE%$/i.test(s)) return true;
|
|
1293
|
+
if (/^[A-Za-z]:$/.test(s)) return true;
|
|
1294
|
+
const norm = s.toLowerCase().replace(/\\/g, "/");
|
|
1295
|
+
if (CATASTROPHIC_POSIX_ROOTS.has(norm)) return true;
|
|
1296
|
+
const win = norm.match(/^[a-z]:\/([^/]+)$/);
|
|
1297
|
+
if (win?.[1] && CATASTROPHIC_WIN_SUBDIRS.has(win[1])) return true;
|
|
1236
1298
|
return false;
|
|
1237
1299
|
}
|
|
1238
|
-
function
|
|
1239
|
-
const targets = tokens.slice(start).filter((token) => !token.startsWith("-") && !SHELL_OPERATORS.has(token));
|
|
1240
|
-
if (targets.length === 0) return true;
|
|
1241
|
-
return targets.some((target) => pathTokenIsOutsideProject(target, projectRoot));
|
|
1242
|
-
}
|
|
1243
|
-
function hasDestructiveDelete(command, projectRoot) {
|
|
1300
|
+
function hasCatastrophicDelete(command) {
|
|
1244
1301
|
const tokens = tokenizeShell(command);
|
|
1245
1302
|
for (let i = 0; i < tokens.length; i++) {
|
|
1246
1303
|
const token = tokens[i]?.toLowerCase();
|
|
@@ -1248,35 +1305,43 @@ function hasDestructiveDelete(command, projectRoot) {
|
|
|
1248
1305
|
if (token === "rm") {
|
|
1249
1306
|
const args = tokens.slice(i + 1);
|
|
1250
1307
|
const recursiveOrForce = args.some(
|
|
1251
|
-
(arg) => /^-[^-]*[rf]/i.test(arg) || arg === "--recursive" || arg === "--force"
|
|
1308
|
+
(arg) => /^-[^-]*[rf]/i.test(arg) || arg === "--recursive" || arg === "--force" || arg === "--no-preserve-root"
|
|
1252
1309
|
);
|
|
1253
|
-
if (recursiveOrForce
|
|
1310
|
+
if (!recursiveOrForce) continue;
|
|
1311
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !SHELL_OPERATORS.has(arg));
|
|
1312
|
+
if (targets.length === 0) return true;
|
|
1313
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
1314
|
+
}
|
|
1315
|
+
if (token === "remove-item" || token === "ri") {
|
|
1316
|
+
const args = tokens.slice(i + 1);
|
|
1317
|
+
const recursive = args.some((arg) => {
|
|
1318
|
+
const a = arg.toLowerCase();
|
|
1319
|
+
return a === "-recurse" || a === "-force";
|
|
1320
|
+
});
|
|
1321
|
+
if (!recursive) continue;
|
|
1322
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !SHELL_OPERATORS.has(arg));
|
|
1323
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
1254
1324
|
}
|
|
1255
1325
|
if (token === "rmdir" || token === "rd") {
|
|
1256
1326
|
const args = tokens.slice(i + 1);
|
|
1257
1327
|
const recursive = args.some((arg) => arg.toLowerCase() === "/s");
|
|
1258
|
-
if (recursive
|
|
1328
|
+
if (!recursive) continue;
|
|
1329
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !arg.startsWith("/") && !SHELL_OPERATORS.has(arg));
|
|
1330
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
1259
1331
|
}
|
|
1260
1332
|
if (token === "del" || token === "erase") {
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
const args = tokens.slice(i + 1).map((arg) => arg.toLowerCase());
|
|
1265
|
-
const recursiveOrForce = args.includes("-recurse") || args.includes("-force");
|
|
1266
|
-
if (recursiveOrForce && hasDangerousDeleteTarget(tokens, i + 1, projectRoot)) return true;
|
|
1333
|
+
const args = tokens.slice(i + 1);
|
|
1334
|
+
const targets = args.filter((arg) => !arg.startsWith("-") && !arg.startsWith("/") && !SHELL_OPERATORS.has(arg));
|
|
1335
|
+
if (targets.some(isCatastrophicDeleteTarget)) return true;
|
|
1267
1336
|
}
|
|
1268
1337
|
}
|
|
1269
1338
|
return false;
|
|
1270
1339
|
}
|
|
1271
|
-
function isClearlyDestructiveBashCommand(command,
|
|
1340
|
+
function isClearlyDestructiveBashCommand(command, _projectRoot) {
|
|
1272
1341
|
const trimmed = command.trim();
|
|
1273
1342
|
if (!trimmed) return false;
|
|
1274
|
-
if (
|
|
1275
|
-
if (
|
|
1276
|
-
if (/\bcd\s+(?:\.\.|~|\/|[A-Za-z]:[\\/])/i.test(trimmed)) return true;
|
|
1277
|
-
if (PROJECT_ESCAPE_PATTERN.test(trimmed)) return true;
|
|
1278
|
-
const absolute = trimmed.match(ABSOLUTE_PATH_PATTERN)?.[0]?.trim().replace(/^['"]|['"]$/g, "");
|
|
1279
|
-
if (absolute && !pathLooksInsideProject(absolute, projectRoot)) return true;
|
|
1343
|
+
if (hasCatastrophicDelete(trimmed)) return true;
|
|
1344
|
+
if (CATASTROPHIC_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
|
|
1280
1345
|
return false;
|
|
1281
1346
|
}
|
|
1282
1347
|
|
|
@@ -1284,6 +1349,15 @@ function isClearlyDestructiveBashCommand(command, projectRoot) {
|
|
|
1284
1349
|
function matchesTrust(patterns, subject) {
|
|
1285
1350
|
return patterns.includes(subject) || matchAny(patterns, subject);
|
|
1286
1351
|
}
|
|
1352
|
+
function shellCommandLineFromInput(input) {
|
|
1353
|
+
const command = getInputString(input, "command") ?? getInputString(input, "cmd") ?? getInputString(input, "script");
|
|
1354
|
+
if (!command) return void 0;
|
|
1355
|
+
if (!input || typeof input !== "object") return command;
|
|
1356
|
+
const args = input["args"];
|
|
1357
|
+
if (!Array.isArray(args) || args.length === 0) return command;
|
|
1358
|
+
const renderedArgs = args.filter((arg) => typeof arg === "string").map((arg) => /\s/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg);
|
|
1359
|
+
return [command, ...renderedArgs].join(" ");
|
|
1360
|
+
}
|
|
1287
1361
|
var DefaultPermissionPolicy = class {
|
|
1288
1362
|
policy = {};
|
|
1289
1363
|
loaded = false;
|
|
@@ -1301,9 +1375,10 @@ var DefaultPermissionPolicy = class {
|
|
|
1301
1375
|
*/
|
|
1302
1376
|
sessionDenied = /* @__PURE__ */ new Map();
|
|
1303
1377
|
/**
|
|
1304
|
-
* Session-scoped "soft trust" map. When the user presses '
|
|
1305
|
-
*
|
|
1306
|
-
*
|
|
1378
|
+
* Session-scoped one-shot "soft trust" map. When the user presses 'y', the
|
|
1379
|
+
* tool+pattern is added here so the immediate confirm re-run can proceed.
|
|
1380
|
+
* The entry is consumed on first use; future calls must ask again unless the
|
|
1381
|
+
* user chose persistent trust.
|
|
1307
1382
|
*
|
|
1308
1383
|
* Cleared on reload().
|
|
1309
1384
|
*/
|
|
@@ -1342,7 +1417,7 @@ var DefaultPermissionPolicy = class {
|
|
|
1342
1417
|
this.trustFile = opts.trustFile;
|
|
1343
1418
|
this.yolo = opts.yolo ?? false;
|
|
1344
1419
|
this.yoloDestructive = opts.yoloDestructive ?? opts.forceAllYolo ?? false;
|
|
1345
|
-
this.confirmDestructive =
|
|
1420
|
+
this.confirmDestructive = true;
|
|
1346
1421
|
this.promptDelegate = opts.promptDelegate;
|
|
1347
1422
|
}
|
|
1348
1423
|
/**
|
|
@@ -1373,9 +1448,9 @@ var DefaultPermissionPolicy = class {
|
|
|
1373
1448
|
return this.yoloDestructive;
|
|
1374
1449
|
}
|
|
1375
1450
|
/** Toggle destructive confirmation gate (only meaningful when yolo is active). */
|
|
1376
|
-
setConfirmDestructive(
|
|
1377
|
-
if (this.confirmDestructive
|
|
1378
|
-
this.confirmDestructive =
|
|
1451
|
+
setConfirmDestructive(_enabled) {
|
|
1452
|
+
if (!this.confirmDestructive) this._evalCache.clear();
|
|
1453
|
+
this.confirmDestructive = true;
|
|
1379
1454
|
}
|
|
1380
1455
|
/** Check whether destructive confirmation gate is active. */
|
|
1381
1456
|
getConfirmDestructive() {
|
|
@@ -1414,12 +1489,12 @@ var DefaultPermissionPolicy = class {
|
|
|
1414
1489
|
return decision;
|
|
1415
1490
|
}
|
|
1416
1491
|
if (this.sessionAllowed.has(cacheKey)) {
|
|
1492
|
+
this.sessionAllowed.delete(cacheKey);
|
|
1417
1493
|
const decision = {
|
|
1418
1494
|
permission: "auto",
|
|
1419
1495
|
source: "trust",
|
|
1420
|
-
reason: "session
|
|
1496
|
+
reason: "session one-shot allow (user pressed yes)"
|
|
1421
1497
|
};
|
|
1422
|
-
this._evalCache.set(cacheKey, decision);
|
|
1423
1498
|
return decision;
|
|
1424
1499
|
}
|
|
1425
1500
|
if (entry?.deny && subject && matchesTrust(entry.deny, subject)) {
|
|
@@ -1432,6 +1507,29 @@ var DefaultPermissionPolicy = class {
|
|
|
1432
1507
|
this._evalCache.set(cacheKey, decision);
|
|
1433
1508
|
return decision;
|
|
1434
1509
|
}
|
|
1510
|
+
if (this.yolo) {
|
|
1511
|
+
const destructive = this.isDestructiveYoloCall(tool, input, ctx);
|
|
1512
|
+
if (destructive) {
|
|
1513
|
+
if (this.promptDelegate) {
|
|
1514
|
+
const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
1515
|
+
if (decision === "deny") {
|
|
1516
|
+
await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
|
|
1517
|
+
return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
|
|
1518
|
+
}
|
|
1519
|
+
return {
|
|
1520
|
+
permission: decision === "yes" || decision === "always" ? "auto" : "deny",
|
|
1521
|
+
source: "user",
|
|
1522
|
+
reason: "destructive yolo approved for this call"
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
return {
|
|
1526
|
+
permission: "confirm",
|
|
1527
|
+
source: "yolo_destructive",
|
|
1528
|
+
riskTier: "destructive",
|
|
1529
|
+
reason: "destructive tool needs explicit approval in YOLO mode"
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1435
1533
|
if (entry?.allow && subject && matchesTrust(entry.allow, subject)) {
|
|
1436
1534
|
const decision = { permission: "auto", source: "trust", reason: "matched allow pattern" };
|
|
1437
1535
|
this._evalCache.set(cacheKey, decision);
|
|
@@ -1443,29 +1541,6 @@ var DefaultPermissionPolicy = class {
|
|
|
1443
1541
|
return decision;
|
|
1444
1542
|
}
|
|
1445
1543
|
if (this.yolo) {
|
|
1446
|
-
if (this.confirmDestructive) {
|
|
1447
|
-
const destructive = this.isDestructiveYoloCall(tool, input, ctx);
|
|
1448
|
-
if (destructive) {
|
|
1449
|
-
if (this.promptDelegate) {
|
|
1450
|
-
const decision2 = await this.promptDelegate(tool, input, subject ?? tool.name);
|
|
1451
|
-
if (decision2 === "always") {
|
|
1452
|
-
await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
|
|
1453
|
-
return { permission: "auto", source: "user", reason: "destructive yolo always-allowed" };
|
|
1454
|
-
}
|
|
1455
|
-
if (decision2 === "deny") {
|
|
1456
|
-
await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
|
|
1457
|
-
return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
|
|
1458
|
-
}
|
|
1459
|
-
return { permission: decision2 === "yes" ? "auto" : "deny", source: "user" };
|
|
1460
|
-
}
|
|
1461
|
-
return {
|
|
1462
|
-
permission: "confirm",
|
|
1463
|
-
source: "yolo_destructive",
|
|
1464
|
-
riskTier: "destructive",
|
|
1465
|
-
reason: "destructive tool needs explicit approval (confirmDestructive is on)"
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
1544
|
const decision = { permission: "auto", source: "yolo" };
|
|
1470
1545
|
this._evalCache.set(cacheKey, decision);
|
|
1471
1546
|
return decision;
|
|
@@ -1482,7 +1557,8 @@ var DefaultPermissionPolicy = class {
|
|
|
1482
1557
|
const hasWriteCap = hasCapability(tool, ToolCapabilities.FS_WRITE);
|
|
1483
1558
|
const hasShellCap = hasCapability(tool, [
|
|
1484
1559
|
ToolCapabilities.SHELL_ARBITRARY,
|
|
1485
|
-
ToolCapabilities.SHELL_RESTRICTED
|
|
1560
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
1561
|
+
ToolCapabilities.SHELL_EXEC
|
|
1486
1562
|
]);
|
|
1487
1563
|
const hasInstallCap = hasCapability(tool, ToolCapabilities.PACKAGE_INSTALL);
|
|
1488
1564
|
const hasConfigCap = hasCapability(tool, ToolCapabilities.CONFIG_MUTATE);
|
|
@@ -1510,27 +1586,30 @@ var DefaultPermissionPolicy = class {
|
|
|
1510
1586
|
// Capability-based destructive check (preferred over name-based)
|
|
1511
1587
|
isDestructiveByCapability(tool) {
|
|
1512
1588
|
const caps = tool.capabilities ?? [];
|
|
1513
|
-
if (caps.includes(
|
|
1514
|
-
if (caps.includes(
|
|
1515
|
-
if (caps.includes(
|
|
1589
|
+
if (caps.includes(ToolCapabilities.SHELL_ARBITRARY)) return true;
|
|
1590
|
+
if (caps.includes(ToolCapabilities.SHELL_RESTRICTED)) return true;
|
|
1591
|
+
if (caps.includes(ToolCapabilities.SHELL_EXEC)) return true;
|
|
1592
|
+
if (caps.includes(ToolCapabilities.FS_WRITE)) return true;
|
|
1593
|
+
if (caps.includes(ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT)) return true;
|
|
1516
1594
|
return false;
|
|
1517
1595
|
}
|
|
1518
1596
|
isDestructiveYoloCall(tool, input, ctx) {
|
|
1519
1597
|
if (this.isDestructiveByCapability(tool)) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1598
|
+
const caps = tool.capabilities ?? [];
|
|
1599
|
+
if (caps.includes(ToolCapabilities.SHELL_ARBITRARY) || caps.includes(ToolCapabilities.SHELL_RESTRICTED) || caps.includes(ToolCapabilities.SHELL_EXEC)) {
|
|
1600
|
+
const command = shellCommandLineFromInput(input);
|
|
1601
|
+
return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : tool.riskTier === "destructive";
|
|
1523
1602
|
}
|
|
1524
|
-
if (
|
|
1603
|
+
if (caps.includes(ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT)) return true;
|
|
1604
|
+
if (caps.includes(ToolCapabilities.FS_WRITE)) {
|
|
1525
1605
|
const targetPath = getInputString(input, "path") ?? getInputString(input, "file");
|
|
1526
1606
|
if (!targetPath || !ctx.projectRoot) return false;
|
|
1527
1607
|
return !pathLooksInsideProject(targetPath, ctx.projectRoot);
|
|
1528
1608
|
}
|
|
1529
|
-
return true;
|
|
1530
1609
|
}
|
|
1531
|
-
if (tool.name === "bash") {
|
|
1532
|
-
const command =
|
|
1533
|
-
return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) :
|
|
1610
|
+
if (tool.name === "bash" || tool.name === "shell" || tool.name === "exec") {
|
|
1611
|
+
const command = shellCommandLineFromInput(input);
|
|
1612
|
+
return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : tool.riskTier === "destructive";
|
|
1534
1613
|
}
|
|
1535
1614
|
if (tool.name === "write" || tool.name === "edit" || tool.name === "replace" || tool.name === "patch") {
|
|
1536
1615
|
const targetPath = getInputString(input, "path") ?? getInputString(input, "file");
|
|
@@ -1579,7 +1658,7 @@ var DefaultPermissionPolicy = class {
|
|
|
1579
1658
|
this.sessionDenied.set(`${rule.tool}::${rule.pattern}`, true);
|
|
1580
1659
|
this._evalCache.clear();
|
|
1581
1660
|
}
|
|
1582
|
-
/** Auto-approve this tool+pattern
|
|
1661
|
+
/** Auto-approve this tool+pattern once (no trust file). */
|
|
1583
1662
|
allowOnce(rule) {
|
|
1584
1663
|
this.sessionAllowed.set(`${rule.tool}::${rule.pattern}`, true);
|
|
1585
1664
|
this._evalCache.clear();
|