wayang-ai 0.9.5 → 0.9.6
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/cli.js +450 -174
- package/dist/mcp-bridge-entry.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5516,22 +5516,22 @@ function fitWorkerRows(rows, budget) {
|
|
|
5516
5516
|
const fitted = [];
|
|
5517
5517
|
let used = 0;
|
|
5518
5518
|
for (const row of rows) {
|
|
5519
|
-
const
|
|
5519
|
+
const sep3 = fitted.length > 0 ? stringWidth(ROW_SEPARATOR) : 0;
|
|
5520
5520
|
const full = workerRowText(row, true);
|
|
5521
5521
|
const fullWidth = stringWidth(full);
|
|
5522
|
-
if (used +
|
|
5522
|
+
if (used + sep3 + fullWidth <= budget) {
|
|
5523
5523
|
fitted.push(full);
|
|
5524
|
-
used +=
|
|
5524
|
+
used += sep3 + fullWidth;
|
|
5525
5525
|
continue;
|
|
5526
5526
|
}
|
|
5527
5527
|
const compact = workerRowText(row, false);
|
|
5528
5528
|
const compactWidth = stringWidth(compact);
|
|
5529
|
-
if (used +
|
|
5529
|
+
if (used + sep3 + compactWidth <= budget) {
|
|
5530
5530
|
fitted.push(compact);
|
|
5531
|
-
used +=
|
|
5531
|
+
used += sep3 + compactWidth;
|
|
5532
5532
|
continue;
|
|
5533
5533
|
}
|
|
5534
|
-
const remaining = budget - used -
|
|
5534
|
+
const remaining = budget - used - sep3;
|
|
5535
5535
|
if (remaining > 0) {
|
|
5536
5536
|
fitted.push(fitWithSuffix(row.label, rowSuffix(row), remaining));
|
|
5537
5537
|
}
|
|
@@ -5938,7 +5938,7 @@ function AppContent() {
|
|
|
5938
5938
|
// package.json
|
|
5939
5939
|
var package_default = {
|
|
5940
5940
|
name: "wayang-ai",
|
|
5941
|
-
version: "0.9.
|
|
5941
|
+
version: "0.9.6",
|
|
5942
5942
|
description: "Multi-agent orchestration platform",
|
|
5943
5943
|
type: "module",
|
|
5944
5944
|
license: "MIT",
|
|
@@ -9356,6 +9356,9 @@ function classifyRedirections(segments) {
|
|
|
9356
9356
|
return result2;
|
|
9357
9357
|
}
|
|
9358
9358
|
|
|
9359
|
+
// src/services/tools/permission-rules/bash-capability.ts
|
|
9360
|
+
import { resolve as resolvePath, sep as sep2 } from "path";
|
|
9361
|
+
|
|
9359
9362
|
// src/services/tools/permission-rules/bash-head.ts
|
|
9360
9363
|
var DANGEROUS_HEAD_COMMANDS = /* @__PURE__ */ new Set([
|
|
9361
9364
|
"sudo",
|
|
@@ -9840,6 +9843,11 @@ function isWriteSegment(segment) {
|
|
|
9840
9843
|
}
|
|
9841
9844
|
return false;
|
|
9842
9845
|
}
|
|
9846
|
+
function isWriteCommandHead(command, words) {
|
|
9847
|
+
if (BASH_WRITE_VERBS.has(command)) return true;
|
|
9848
|
+
const flags2 = BASH_FLAGGED_WRITE_VERBS.get(command);
|
|
9849
|
+
return flags2 !== void 0 && flags2.some((flag) => words.some((other) => isFlagWord(other, flag)));
|
|
9850
|
+
}
|
|
9843
9851
|
var GIT_READ_ONLY_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
9844
9852
|
"status",
|
|
9845
9853
|
"log",
|
|
@@ -9879,6 +9887,10 @@ function isReadOnlyGitCommand(words) {
|
|
|
9879
9887
|
const flags2 = args.slice(i + 1).filter((word) => word.startsWith("-"));
|
|
9880
9888
|
return flags2.length > 0 && flags2.every((flag) => listFlags.includes(flag));
|
|
9881
9889
|
}
|
|
9890
|
+
function isDateSetFlag(word) {
|
|
9891
|
+
if (word === "-s" || word.startsWith("--set")) return true;
|
|
9892
|
+
return /^-[^-]*s/.test(word);
|
|
9893
|
+
}
|
|
9882
9894
|
function classifySegment(segment) {
|
|
9883
9895
|
if (isWriteSegment(segment)) return { mode: "write" };
|
|
9884
9896
|
const head = headCommand(segment);
|
|
@@ -9887,6 +9899,11 @@ function classifySegment(segment) {
|
|
|
9887
9899
|
if (head.command === "git") {
|
|
9888
9900
|
return { mode: isReadOnlyGitCommand(head.words) ? "read" : "unknown" };
|
|
9889
9901
|
}
|
|
9902
|
+
if (head.command === "date") {
|
|
9903
|
+
const sets = head.words.slice(1).some(isDateSetFlag);
|
|
9904
|
+
if (sets) return { mode: "unknown", unverified: "date" };
|
|
9905
|
+
return { mode: "read" };
|
|
9906
|
+
}
|
|
9890
9907
|
if (head.command === "sed") {
|
|
9891
9908
|
const scripts = sedScriptsOf(head.words);
|
|
9892
9909
|
if (scripts === void 0) return { mode: "unknown", unverified: "sed" };
|
|
@@ -9953,99 +9970,6 @@ function unanalyzedInnerCommand(segments) {
|
|
|
9953
9970
|
return void 0;
|
|
9954
9971
|
}
|
|
9955
9972
|
|
|
9956
|
-
// src/services/tools/permission-rules/bash-paths.ts
|
|
9957
|
-
function pathPieces(text, access) {
|
|
9958
|
-
const candidates = [];
|
|
9959
|
-
for (const piece of text.split("=")) {
|
|
9960
|
-
if (!piece.startsWith("/") && !piece.startsWith("~")) continue;
|
|
9961
|
-
if (isDiscardRedirectTarget(piece)) continue;
|
|
9962
|
-
candidates.push({ path: piece, access });
|
|
9963
|
-
}
|
|
9964
|
-
return candidates;
|
|
9965
|
-
}
|
|
9966
|
-
function extractPathCandidates(segments) {
|
|
9967
|
-
const candidates = [];
|
|
9968
|
-
for (const segment of segments) {
|
|
9969
|
-
const segmentLevel = segmentAccess(segment);
|
|
9970
|
-
for (const token of segment.tokens) {
|
|
9971
|
-
if (token.kind === "word") {
|
|
9972
|
-
candidates.push(...pathPieces(token.word.text, segmentLevel));
|
|
9973
|
-
continue;
|
|
9974
|
-
}
|
|
9975
|
-
const { redirect } = token;
|
|
9976
|
-
if (redirect.kind === "fd-dup" || redirect.kind === "here-doc" || redirect.kind === "here-string") continue;
|
|
9977
|
-
const target = redirect.target?.text;
|
|
9978
|
-
if (target === void 0 || isDiscardRedirectTarget(target)) continue;
|
|
9979
|
-
if (target.includes(CMDSUBST_PLACEHOLDER)) continue;
|
|
9980
|
-
const access = redirect.kind === "output" ? "write" : segmentLevel;
|
|
9981
|
-
for (const candidate of pathPieces(target, access)) {
|
|
9982
|
-
candidates.push(
|
|
9983
|
-
redirect.kind === "output" ? { ...candidate, reason: `output redirection to "${target}"` } : candidate
|
|
9984
|
-
);
|
|
9985
|
-
}
|
|
9986
|
-
}
|
|
9987
|
-
}
|
|
9988
|
-
return candidates;
|
|
9989
|
-
}
|
|
9990
|
-
function commandAccess(candidates) {
|
|
9991
|
-
let access = "unknown";
|
|
9992
|
-
for (const candidate of candidates) {
|
|
9993
|
-
if (candidate.access === "write") return "write";
|
|
9994
|
-
if (candidate.access === "read") access = "read";
|
|
9995
|
-
}
|
|
9996
|
-
return access;
|
|
9997
|
-
}
|
|
9998
|
-
|
|
9999
|
-
// src/services/tools/permission-rules/cd-chain.ts
|
|
10000
|
-
var NON_LITERAL_TARGET_CHARS = /[$`*?[\]{}]/;
|
|
10001
|
-
function literalCdTarget(segment) {
|
|
10002
|
-
if (segment.tokens.length !== 2) return void 0;
|
|
10003
|
-
const [command, argument] = segment.tokens;
|
|
10004
|
-
if (command.kind !== "word" || argument.kind !== "word") return void 0;
|
|
10005
|
-
if (command.word.text !== "cd") return void 0;
|
|
10006
|
-
if (command.word.substitutions.length > 0) return void 0;
|
|
10007
|
-
const target = argument.word.text;
|
|
10008
|
-
if (target === "" || target === "-") return void 0;
|
|
10009
|
-
if (argument.word.substitutions.length > 0) return void 0;
|
|
10010
|
-
if (target.includes(CMDSUBST_PLACEHOLDER)) return void 0;
|
|
10011
|
-
if (NON_LITERAL_TARGET_CHARS.test(target)) return void 0;
|
|
10012
|
-
return target;
|
|
10013
|
-
}
|
|
10014
|
-
function cdAskReason(scope, argument, abs, verdict) {
|
|
10015
|
-
if (verdict.sensitive === true && verdict.matchedEntry !== void 0) {
|
|
10016
|
-
return `cd target "${argument}" is the sensitive location ${verdict.matchedEntry} (${abs}) \u2014 controller approval is required`;
|
|
10017
|
-
}
|
|
10018
|
-
if (verdict.matchedRule === "read-within-home") {
|
|
10019
|
-
return `cd target "${argument}" is inside the home directory (${abs}) \u2014 controller approval is required`;
|
|
10020
|
-
}
|
|
10021
|
-
return `cd target "${argument}" resolves outside the allowed scope (${abs}) \u2014 controller approval is required`;
|
|
10022
|
-
}
|
|
10023
|
-
function applyCdChain(scope, segments) {
|
|
10024
|
-
let cwd = scope.workspace;
|
|
10025
|
-
let i = 0;
|
|
10026
|
-
while (i + 1 < segments.length && segments[i].operator === "&&") {
|
|
10027
|
-
const argument = literalCdTarget(segments[i]);
|
|
10028
|
-
if (argument === void 0) break;
|
|
10029
|
-
const expanded = expandHome(argument);
|
|
10030
|
-
if (expanded.unresolvable) break;
|
|
10031
|
-
const abs = normalizePath(expanded.path, { base: cwd });
|
|
10032
|
-
const verdict = decidePath(scope, abs, "read");
|
|
10033
|
-
if (verdict.decision === "deny") {
|
|
10034
|
-
return { segments: segments.slice(i), decision: toToolDecision(verdict, "read") };
|
|
10035
|
-
}
|
|
10036
|
-
const narrowed = narrowHomeRead(verdict, { path: abs, access: "read" });
|
|
10037
|
-
if (narrowed.decision === "ask") {
|
|
10038
|
-
return {
|
|
10039
|
-
segments: segments.slice(i),
|
|
10040
|
-
decision: { ...narrowed, reason: cdAskReason(scope, argument, abs, narrowed) }
|
|
10041
|
-
};
|
|
10042
|
-
}
|
|
10043
|
-
cwd = abs;
|
|
10044
|
-
i += 1;
|
|
10045
|
-
}
|
|
10046
|
-
return { segments: segments.slice(i) };
|
|
10047
|
-
}
|
|
10048
|
-
|
|
10049
9973
|
// src/services/tools/permission-rules/substitutions.ts
|
|
10050
9974
|
var MAX_CMDSUBST_DEPTH = 5;
|
|
10051
9975
|
function collectSubstitutions(segments) {
|
|
@@ -10223,6 +10147,355 @@ function unanalyzedScriptFile(segments) {
|
|
|
10223
10147
|
return void 0;
|
|
10224
10148
|
}
|
|
10225
10149
|
|
|
10150
|
+
// src/services/tools/permission-rules/bash-capability.ts
|
|
10151
|
+
var INTERPRETER_COMMANDS = /* @__PURE__ */ new Set([
|
|
10152
|
+
"bash",
|
|
10153
|
+
"sh",
|
|
10154
|
+
"dash",
|
|
10155
|
+
"zsh",
|
|
10156
|
+
"ksh",
|
|
10157
|
+
"busybox",
|
|
10158
|
+
"python",
|
|
10159
|
+
"python3",
|
|
10160
|
+
"node",
|
|
10161
|
+
"perl",
|
|
10162
|
+
"ruby",
|
|
10163
|
+
"awk",
|
|
10164
|
+
"php",
|
|
10165
|
+
"lua"
|
|
10166
|
+
]);
|
|
10167
|
+
var NETWORK_COMMANDS = /* @__PURE__ */ new Set(["curl", "wget", "nc", "scp"]);
|
|
10168
|
+
var WRITE_CAPABILITY_COMMANDS = /* @__PURE__ */ new Set(["rsync"]);
|
|
10169
|
+
var INERT_COMMANDS = /* @__PURE__ */ new Set([
|
|
10170
|
+
"sleep",
|
|
10171
|
+
"true",
|
|
10172
|
+
"false",
|
|
10173
|
+
":",
|
|
10174
|
+
"pwd",
|
|
10175
|
+
"whoami",
|
|
10176
|
+
"id",
|
|
10177
|
+
"date",
|
|
10178
|
+
"test",
|
|
10179
|
+
"[",
|
|
10180
|
+
"exit",
|
|
10181
|
+
"which",
|
|
10182
|
+
"command",
|
|
10183
|
+
"echo",
|
|
10184
|
+
"printf"
|
|
10185
|
+
]);
|
|
10186
|
+
var LAUNCHER_COMMANDS = /* @__PURE__ */ new Set([
|
|
10187
|
+
"script",
|
|
10188
|
+
"su",
|
|
10189
|
+
"setsid",
|
|
10190
|
+
"watch",
|
|
10191
|
+
"timeout",
|
|
10192
|
+
"ssh"
|
|
10193
|
+
]);
|
|
10194
|
+
var DYNAMIC_COMMANDS = /* @__PURE__ */ new Set(["source", "."]);
|
|
10195
|
+
var ASSIGNMENT_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
10196
|
+
var NUMERIC_PATTERN = /^\d+$/;
|
|
10197
|
+
var DYNAMIC_WORD_PATTERN2 = /[$`]/;
|
|
10198
|
+
function wordTexts(segment) {
|
|
10199
|
+
const words = [];
|
|
10200
|
+
for (const token of segment.tokens) {
|
|
10201
|
+
if (token.kind === "word") words.push(token.word.text);
|
|
10202
|
+
}
|
|
10203
|
+
return words;
|
|
10204
|
+
}
|
|
10205
|
+
function stripAssignment(word) {
|
|
10206
|
+
const match = word.match(/^[A-Za-z_][A-Za-z0-9_]*=(.*)$/);
|
|
10207
|
+
return match === null ? word : match[1];
|
|
10208
|
+
}
|
|
10209
|
+
function relativeOperands(words) {
|
|
10210
|
+
const operands = [];
|
|
10211
|
+
for (const word of words.slice(1)) {
|
|
10212
|
+
if (word.startsWith("-")) continue;
|
|
10213
|
+
if (word.includes(CMDSUBST_PLACEHOLDER)) continue;
|
|
10214
|
+
const value = stripAssignment(word);
|
|
10215
|
+
if (value === "" || value.startsWith("/") || value.startsWith("~")) continue;
|
|
10216
|
+
operands.push(value);
|
|
10217
|
+
}
|
|
10218
|
+
return operands;
|
|
10219
|
+
}
|
|
10220
|
+
function hasOutputRedirect(segment) {
|
|
10221
|
+
for (const token of segment.tokens) {
|
|
10222
|
+
if (token.kind !== "redirect") continue;
|
|
10223
|
+
const { redirect } = token;
|
|
10224
|
+
if (redirect.kind !== "output") continue;
|
|
10225
|
+
if (isDiscardRedirectTarget(redirect.target?.text)) continue;
|
|
10226
|
+
return true;
|
|
10227
|
+
}
|
|
10228
|
+
return false;
|
|
10229
|
+
}
|
|
10230
|
+
function relativeInputTargets(segment) {
|
|
10231
|
+
const targets = [];
|
|
10232
|
+
for (const token of segment.tokens) {
|
|
10233
|
+
if (token.kind !== "redirect") continue;
|
|
10234
|
+
const { redirect } = token;
|
|
10235
|
+
if (redirect.kind !== "input") continue;
|
|
10236
|
+
const target = redirect.target?.text;
|
|
10237
|
+
if (target === void 0 || isDiscardRedirectTarget(target)) continue;
|
|
10238
|
+
if (target.includes(CMDSUBST_PLACEHOLDER)) continue;
|
|
10239
|
+
if (target.startsWith("/") || target.startsWith("~")) continue;
|
|
10240
|
+
targets.push(target);
|
|
10241
|
+
}
|
|
10242
|
+
return targets;
|
|
10243
|
+
}
|
|
10244
|
+
function insideWorkspace(scope, abs) {
|
|
10245
|
+
const workspace = resolvePath(scope.workspace);
|
|
10246
|
+
return abs === workspace || abs.startsWith(`${workspace}${sep2}`);
|
|
10247
|
+
}
|
|
10248
|
+
function capabilityFromWords(words) {
|
|
10249
|
+
let start = 0;
|
|
10250
|
+
while (start < words.length && ASSIGNMENT_PATTERN2.test(words[start])) start += 1;
|
|
10251
|
+
const command = words[start];
|
|
10252
|
+
if (command === void 0) return "unknown";
|
|
10253
|
+
return capabilityOfHead(command, words.slice(start));
|
|
10254
|
+
}
|
|
10255
|
+
function launcherCapability(command, words) {
|
|
10256
|
+
const args = words.slice(1);
|
|
10257
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
10258
|
+
const word = args[i];
|
|
10259
|
+
if ((command === "script" || command === "su") && word === "-c") {
|
|
10260
|
+
const script = args[i + 1];
|
|
10261
|
+
return script === void 0 ? "exec" : capabilityFromWords(script.split(/\s+/).filter(Boolean));
|
|
10262
|
+
}
|
|
10263
|
+
if (word.startsWith("-")) continue;
|
|
10264
|
+
if (NUMERIC_PATTERN.test(word)) continue;
|
|
10265
|
+
if (command === "ssh") {
|
|
10266
|
+
const remote = args.slice(i + 1).filter((w) => !w.startsWith("-"));
|
|
10267
|
+
return remote.length === 0 ? "network" : capabilityFromWords(remote);
|
|
10268
|
+
}
|
|
10269
|
+
return capabilityFromWords(args.slice(i));
|
|
10270
|
+
}
|
|
10271
|
+
return "exec";
|
|
10272
|
+
}
|
|
10273
|
+
function capabilityOfHead(command, words) {
|
|
10274
|
+
if (DYNAMIC_WORD_PATTERN2.test(command) || command.includes(CMDSUBST_PLACEHOLDER)) return "dynamic";
|
|
10275
|
+
if (DYNAMIC_COMMANDS.has(command)) return "dynamic";
|
|
10276
|
+
if (command === "git" && words.slice(1).includes("-c")) return "unknown";
|
|
10277
|
+
if (command === "env" && words.includes("-S")) return "exec";
|
|
10278
|
+
if (LAUNCHER_COMMANDS.has(command)) return launcherCapability(command, words);
|
|
10279
|
+
if (INTERPRETER_COMMANDS.has(command)) return "exec";
|
|
10280
|
+
if (NETWORK_COMMANDS.has(command)) return "network";
|
|
10281
|
+
if (WRITE_CAPABILITY_COMMANDS.has(command)) return "write";
|
|
10282
|
+
return "unknown";
|
|
10283
|
+
}
|
|
10284
|
+
function isJudgedShellScript(head) {
|
|
10285
|
+
if (!SHELL_COMMANDS.has(head.command)) return false;
|
|
10286
|
+
return head.words.slice(1).some(
|
|
10287
|
+
(word) => word === "-c" || word.startsWith("--command") || word.startsWith("-") && !word.startsWith("--") && word.includes("c")
|
|
10288
|
+
);
|
|
10289
|
+
}
|
|
10290
|
+
function isInertSegment(segment, head) {
|
|
10291
|
+
if (!INERT_COMMANDS.has(head.command)) return false;
|
|
10292
|
+
const args = head.words.slice(1);
|
|
10293
|
+
if (head.command === "date" && args.some(isDateSetFlag)) return false;
|
|
10294
|
+
if (head.command === "command" && args[0] !== "-v" && args[0] !== "-V") return false;
|
|
10295
|
+
for (const token of segment.tokens) {
|
|
10296
|
+
if (token.kind === "redirect") return false;
|
|
10297
|
+
}
|
|
10298
|
+
for (const word of head.words) {
|
|
10299
|
+
if (word.includes(CMDSUBST_PLACEHOLDER) || /[$`]/.test(word)) return false;
|
|
10300
|
+
const value = stripAssignment(word);
|
|
10301
|
+
if (value.startsWith("/") || value.startsWith("~")) return false;
|
|
10302
|
+
}
|
|
10303
|
+
return true;
|
|
10304
|
+
}
|
|
10305
|
+
function segmentCapability(segment) {
|
|
10306
|
+
if (hasOutputRedirect(segment)) return "write";
|
|
10307
|
+
const head = headCommand(segment);
|
|
10308
|
+
if (head !== void 0 && isJudgedShellScript(head)) return "read";
|
|
10309
|
+
if (head !== void 0 && isWriteCommandHead(head.command, head.words)) return "write";
|
|
10310
|
+
if (head !== void 0 && isInertSegment(segment, head)) return "inert";
|
|
10311
|
+
if (isReadOnlySegment(segment)) return "read";
|
|
10312
|
+
if (head === void 0) {
|
|
10313
|
+
const words = wordTexts(segment);
|
|
10314
|
+
let i = 0;
|
|
10315
|
+
while (i < words.length && ASSIGNMENT_PATTERN2.test(words[i])) i += 1;
|
|
10316
|
+
const first = words[i];
|
|
10317
|
+
if (first === void 0) return "unknown";
|
|
10318
|
+
if (first === "env" && words.includes("-S")) return "exec";
|
|
10319
|
+
if (DYNAMIC_WORD_PATTERN2.test(first) || first.includes(CMDSUBST_PLACEHOLDER)) return "dynamic";
|
|
10320
|
+
return "unknown";
|
|
10321
|
+
}
|
|
10322
|
+
return capabilityOfHead(head.command, head.words);
|
|
10323
|
+
}
|
|
10324
|
+
function headWord(segment) {
|
|
10325
|
+
const head = headCommand(segment);
|
|
10326
|
+
if (head !== void 0) return head.command;
|
|
10327
|
+
const words = wordTexts(segment);
|
|
10328
|
+
return words.find((word) => !ASSIGNMENT_PATTERN2.test(word)) ?? words[0] ?? "\u2026";
|
|
10329
|
+
}
|
|
10330
|
+
function ask(matchedRule, reason) {
|
|
10331
|
+
return { decision: "ask", matchedRule, reason };
|
|
10332
|
+
}
|
|
10333
|
+
function evaluateCapabilityGate(scope, segments, cursor) {
|
|
10334
|
+
let execAsk;
|
|
10335
|
+
let networkAsk;
|
|
10336
|
+
let unknownAsk;
|
|
10337
|
+
for (const segment of segments) {
|
|
10338
|
+
const capability = segmentCapability(segment);
|
|
10339
|
+
if (capability === "inert") continue;
|
|
10340
|
+
if (capability === "dynamic") {
|
|
10341
|
+
const word = headWord(segment);
|
|
10342
|
+
const reason = DYNAMIC_COMMANDS.has(word) ? `\`${word}\` runs a script inside the current shell \u2014 controller approval is required` : `the command word "${word}" is dynamically constructed \u2014 controller approval is required`;
|
|
10343
|
+
return ask("dynamic-shell", reason);
|
|
10344
|
+
}
|
|
10345
|
+
if (capability === "write") {
|
|
10346
|
+
const head = headCommand(segment);
|
|
10347
|
+
const operands = relativeOperands(head !== void 0 ? head.words : wordTexts(segment));
|
|
10348
|
+
for (const operand of operands) {
|
|
10349
|
+
const abs = resolvePath(cursor, operand);
|
|
10350
|
+
if (!insideWorkspace(scope, abs)) {
|
|
10351
|
+
return ask(
|
|
10352
|
+
"write-scope",
|
|
10353
|
+
`write target "${operand}" resolves outside the workspace (${abs}) \u2014 controller approval is required`
|
|
10354
|
+
);
|
|
10355
|
+
}
|
|
10356
|
+
}
|
|
10357
|
+
continue;
|
|
10358
|
+
}
|
|
10359
|
+
if (capability === "read") {
|
|
10360
|
+
const head = headCommand(segment);
|
|
10361
|
+
const operands = head !== void 0 ? relativeOperands(head.words) : [];
|
|
10362
|
+
for (const operand of [...operands, ...relativeInputTargets(segment)]) {
|
|
10363
|
+
const abs = resolvePath(cursor, operand);
|
|
10364
|
+
const verdict = decidePath(scope, abs, "read");
|
|
10365
|
+
if (verdict.decision === "deny") return toToolDecision(verdict, "read");
|
|
10366
|
+
const narrowed = narrowHomeRead(verdict, { path: abs, access: "read" });
|
|
10367
|
+
if (narrowed.decision === "ask") {
|
|
10368
|
+
return { ...narrowed, askPaths: [narrowed.resolvedPath ?? abs] };
|
|
10369
|
+
}
|
|
10370
|
+
}
|
|
10371
|
+
continue;
|
|
10372
|
+
}
|
|
10373
|
+
if (capability === "exec") {
|
|
10374
|
+
execAsk ??= ask(
|
|
10375
|
+
"exec-capability",
|
|
10376
|
+
`\`${headWord(segment)}\` runs a shell or interpreter \u2014 controller approval is required (exec capability)`
|
|
10377
|
+
);
|
|
10378
|
+
} else if (capability === "network") {
|
|
10379
|
+
networkAsk ??= ask(
|
|
10380
|
+
"network-capability",
|
|
10381
|
+
`\`${headWord(segment)}\` reaches the network \u2014 controller approval is required (network capability)`
|
|
10382
|
+
);
|
|
10383
|
+
} else if (capability === "unknown") {
|
|
10384
|
+
unknownAsk ??= ask(
|
|
10385
|
+
"unknown-capability",
|
|
10386
|
+
`\`${headWord(segment)}\` is not on the bash capability tables \u2014 controller approval is required (unknown capability)`
|
|
10387
|
+
);
|
|
10388
|
+
}
|
|
10389
|
+
}
|
|
10390
|
+
return execAsk ?? networkAsk ?? unknownAsk;
|
|
10391
|
+
}
|
|
10392
|
+
function redirectWriteScopeAsk(scope, targets, cursor) {
|
|
10393
|
+
for (const target of targets) {
|
|
10394
|
+
if (target.startsWith("/") || target.startsWith("~")) continue;
|
|
10395
|
+
const abs = resolvePath(cursor, target);
|
|
10396
|
+
if (!insideWorkspace(scope, abs)) {
|
|
10397
|
+
return ask(
|
|
10398
|
+
"write-scope",
|
|
10399
|
+
`output redirection target "${target}" resolves outside the workspace (${abs}) \u2014 controller approval is required`
|
|
10400
|
+
);
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
return void 0;
|
|
10404
|
+
}
|
|
10405
|
+
|
|
10406
|
+
// src/services/tools/permission-rules/bash-paths.ts
|
|
10407
|
+
function pathPieces(text, access) {
|
|
10408
|
+
const candidates = [];
|
|
10409
|
+
for (const piece of text.split("=")) {
|
|
10410
|
+
if (!piece.startsWith("/") && !piece.startsWith("~")) continue;
|
|
10411
|
+
if (isDiscardRedirectTarget(piece)) continue;
|
|
10412
|
+
candidates.push({ path: piece, access });
|
|
10413
|
+
}
|
|
10414
|
+
return candidates;
|
|
10415
|
+
}
|
|
10416
|
+
function extractPathCandidates(segments) {
|
|
10417
|
+
const candidates = [];
|
|
10418
|
+
for (const segment of segments) {
|
|
10419
|
+
const segmentLevel = segmentAccess(segment);
|
|
10420
|
+
for (const token of segment.tokens) {
|
|
10421
|
+
if (token.kind === "word") {
|
|
10422
|
+
candidates.push(...pathPieces(token.word.text, segmentLevel));
|
|
10423
|
+
continue;
|
|
10424
|
+
}
|
|
10425
|
+
const { redirect } = token;
|
|
10426
|
+
if (redirect.kind === "fd-dup" || redirect.kind === "here-doc" || redirect.kind === "here-string") continue;
|
|
10427
|
+
const target = redirect.target?.text;
|
|
10428
|
+
if (target === void 0 || isDiscardRedirectTarget(target)) continue;
|
|
10429
|
+
if (target.includes(CMDSUBST_PLACEHOLDER)) continue;
|
|
10430
|
+
const access = redirect.kind === "output" ? "write" : segmentLevel;
|
|
10431
|
+
for (const candidate of pathPieces(target, access)) {
|
|
10432
|
+
candidates.push(
|
|
10433
|
+
redirect.kind === "output" ? { ...candidate, reason: `output redirection to "${target}"` } : candidate
|
|
10434
|
+
);
|
|
10435
|
+
}
|
|
10436
|
+
}
|
|
10437
|
+
}
|
|
10438
|
+
return candidates;
|
|
10439
|
+
}
|
|
10440
|
+
function commandAccess(candidates) {
|
|
10441
|
+
let access = "unknown";
|
|
10442
|
+
for (const candidate of candidates) {
|
|
10443
|
+
if (candidate.access === "write") return "write";
|
|
10444
|
+
if (candidate.access === "read") access = "read";
|
|
10445
|
+
}
|
|
10446
|
+
return access;
|
|
10447
|
+
}
|
|
10448
|
+
|
|
10449
|
+
// src/services/tools/permission-rules/cd-chain.ts
|
|
10450
|
+
var NON_LITERAL_TARGET_CHARS = /[$`*?[\]{}]/;
|
|
10451
|
+
function literalCdTarget(segment) {
|
|
10452
|
+
if (segment.tokens.length !== 2) return void 0;
|
|
10453
|
+
const [command, argument] = segment.tokens;
|
|
10454
|
+
if (command.kind !== "word" || argument.kind !== "word") return void 0;
|
|
10455
|
+
if (command.word.text !== "cd") return void 0;
|
|
10456
|
+
if (command.word.substitutions.length > 0) return void 0;
|
|
10457
|
+
const target = argument.word.text;
|
|
10458
|
+
if (target === "" || target === "-") return void 0;
|
|
10459
|
+
if (argument.word.substitutions.length > 0) return void 0;
|
|
10460
|
+
if (target.includes(CMDSUBST_PLACEHOLDER)) return void 0;
|
|
10461
|
+
if (NON_LITERAL_TARGET_CHARS.test(target)) return void 0;
|
|
10462
|
+
return target;
|
|
10463
|
+
}
|
|
10464
|
+
function cdAskReason(scope, argument, abs, verdict) {
|
|
10465
|
+
if (verdict.sensitive === true && verdict.matchedEntry !== void 0) {
|
|
10466
|
+
return `cd target "${argument}" is the sensitive location ${verdict.matchedEntry} (${abs}) \u2014 controller approval is required`;
|
|
10467
|
+
}
|
|
10468
|
+
if (verdict.matchedRule === "read-within-home") {
|
|
10469
|
+
return `cd target "${argument}" is inside the home directory (${abs}) \u2014 controller approval is required`;
|
|
10470
|
+
}
|
|
10471
|
+
return `cd target "${argument}" resolves outside the allowed scope (${abs}) \u2014 controller approval is required`;
|
|
10472
|
+
}
|
|
10473
|
+
function applyCdChain(scope, segments) {
|
|
10474
|
+
let cwd = scope.workspace;
|
|
10475
|
+
let i = 0;
|
|
10476
|
+
while (i + 1 < segments.length && segments[i].operator === "&&") {
|
|
10477
|
+
const argument = literalCdTarget(segments[i]);
|
|
10478
|
+
if (argument === void 0) break;
|
|
10479
|
+
const expanded = expandHome(argument);
|
|
10480
|
+
if (expanded.unresolvable) break;
|
|
10481
|
+
const abs = normalizePath(expanded.path, { base: cwd });
|
|
10482
|
+
const verdict = decidePath(scope, abs, "read");
|
|
10483
|
+
if (verdict.decision === "deny") {
|
|
10484
|
+
return { segments: segments.slice(i), decision: toToolDecision(verdict, "read") };
|
|
10485
|
+
}
|
|
10486
|
+
const narrowed = narrowHomeRead(verdict, { path: abs, access: "read" });
|
|
10487
|
+
if (narrowed.decision === "ask") {
|
|
10488
|
+
return {
|
|
10489
|
+
segments: segments.slice(i),
|
|
10490
|
+
decision: { ...narrowed, reason: cdAskReason(scope, argument, abs, narrowed) }
|
|
10491
|
+
};
|
|
10492
|
+
}
|
|
10493
|
+
cwd = abs;
|
|
10494
|
+
i += 1;
|
|
10495
|
+
}
|
|
10496
|
+
return { segments: segments.slice(i), cwd };
|
|
10497
|
+
}
|
|
10498
|
+
|
|
10226
10499
|
// src/services/tools/permission-rules/bash-access.ts
|
|
10227
10500
|
function fallbackSensitiveHit(scope, expandedCommand) {
|
|
10228
10501
|
for (const raw of expandedCommand.split(/[\s=]+/)) {
|
|
@@ -10274,14 +10547,14 @@ function evaluateBashCommand(scope, command, depth) {
|
|
|
10274
10547
|
if (verdict.decision === "deny") return toToolDecision(verdict, candidate.access);
|
|
10275
10548
|
const narrowed = narrowHomeRead(verdict, candidate);
|
|
10276
10549
|
if (narrowed.decision === "ask") {
|
|
10277
|
-
const
|
|
10550
|
+
const ask2 = {
|
|
10278
10551
|
...narrowed,
|
|
10279
10552
|
...candidate.reason !== void 0 && { reason: candidate.reason }
|
|
10280
10553
|
};
|
|
10281
10554
|
const askingPath = narrowed.resolvedPath ?? candidate.path;
|
|
10282
10555
|
if (!askPaths.includes(askingPath)) askPaths.push(askingPath);
|
|
10283
|
-
if (
|
|
10284
|
-
else firstAsk ??=
|
|
10556
|
+
if (ask2.sensitive === true) sensitiveAsk ??= ask2;
|
|
10557
|
+
else firstAsk ??= ask2;
|
|
10285
10558
|
} else firstAllow ??= narrowed;
|
|
10286
10559
|
}
|
|
10287
10560
|
const lineAccess = candidates.length > 0 ? commandAccess(candidates) : segments.length > 0 && segments.every((segment) => segmentAccess(segment) === "read") ? "read" : "unknown";
|
|
@@ -10316,11 +10589,12 @@ function evaluateBashCommand(scope, command, depth) {
|
|
|
10316
10589
|
};
|
|
10317
10590
|
}
|
|
10318
10591
|
if (redirections.writeTargets.length > 0 && !firstAsk && !sensitiveAsk) {
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10592
|
+
const scopeAsk = redirectWriteScopeAsk(
|
|
10593
|
+
scope,
|
|
10594
|
+
redirections.writeTargets,
|
|
10595
|
+
cdChain.cwd ?? scope.workspace
|
|
10596
|
+
);
|
|
10597
|
+
if (scopeAsk) return { ...scopeAsk, access: lineAccess };
|
|
10324
10598
|
}
|
|
10325
10599
|
if (hasUnresolvableHome) {
|
|
10326
10600
|
return {
|
|
@@ -10352,6 +10626,8 @@ function evaluateBashCommand(scope, command, depth) {
|
|
|
10352
10626
|
};
|
|
10353
10627
|
}
|
|
10354
10628
|
if (firstAsk) return firstAsk;
|
|
10629
|
+
const capabilityAsk = evaluateCapabilityGate(scope, segments, cdChain.cwd ?? scope.workspace);
|
|
10630
|
+
if (capabilityAsk) return { ...capabilityAsk, access: lineAccess };
|
|
10355
10631
|
if (firstAllow) return firstAllow;
|
|
10356
10632
|
return { decision: "allow", matchedRule: "no-path", access: lineAccess };
|
|
10357
10633
|
}
|
|
@@ -10916,8 +11192,8 @@ var ClaudeWorkerBase = class {
|
|
|
10916
11192
|
* Provide the engine-backed ask_controller implementation. Once set, the
|
|
10917
11193
|
* worker exposes it to Claude Code as an in-process SDK MCP tool.
|
|
10918
11194
|
*/
|
|
10919
|
-
setInquirySupport(
|
|
10920
|
-
this._askController =
|
|
11195
|
+
setInquirySupport(ask2) {
|
|
11196
|
+
this._askController = ask2;
|
|
10921
11197
|
}
|
|
10922
11198
|
flushMessageQueue() {
|
|
10923
11199
|
this._messageQueue.length = 0;
|
|
@@ -11126,11 +11402,11 @@ var ClaudeWorkerPermissionGate = class _ClaudeWorkerPermissionGate extends Claud
|
|
|
11126
11402
|
* times out → deny). Shared by every `ask` verdict so the request flow stays
|
|
11127
11403
|
* identical to the puppet worker's.
|
|
11128
11404
|
*/
|
|
11129
|
-
async requestPermission(toolName, input2,
|
|
11405
|
+
async requestPermission(toolName, input2, ask2, grantKeys = []) {
|
|
11130
11406
|
if (!this._signalQueue || !this._runContext) return { behavior: "allow", updatedInput: input2 };
|
|
11131
11407
|
const requestId = generateId("perm");
|
|
11132
|
-
const description = this.describePermissionRequest(toolName, input2,
|
|
11133
|
-
const operation =
|
|
11408
|
+
const description = this.describePermissionRequest(toolName, input2, ask2);
|
|
11409
|
+
const operation = ask2.operation;
|
|
11134
11410
|
this._signalQueue.enqueue({
|
|
11135
11411
|
source: "worker",
|
|
11136
11412
|
type: "permission_request",
|
|
@@ -11144,12 +11420,12 @@ var ClaudeWorkerPermissionGate = class _ClaudeWorkerPermissionGate extends Claud
|
|
|
11144
11420
|
toolName,
|
|
11145
11421
|
toolArgs: input2,
|
|
11146
11422
|
description,
|
|
11147
|
-
...
|
|
11148
|
-
...
|
|
11149
|
-
...
|
|
11150
|
-
...
|
|
11151
|
-
...
|
|
11152
|
-
...
|
|
11423
|
+
...ask2.matchedRule !== void 0 && { matchedRule: ask2.matchedRule },
|
|
11424
|
+
...ask2.matchedEntry !== void 0 && { matchedEntry: ask2.matchedEntry },
|
|
11425
|
+
...ask2.matchedSource !== void 0 && { matchedSource: ask2.matchedSource },
|
|
11426
|
+
...ask2.resolvedPath !== void 0 && { resolvedPath: ask2.resolvedPath },
|
|
11427
|
+
...ask2.sensitive !== void 0 && { sensitive: ask2.sensitive },
|
|
11428
|
+
...ask2.access !== void 0 && { access: ask2.access },
|
|
11153
11429
|
operation
|
|
11154
11430
|
},
|
|
11155
11431
|
...this._turnLineage
|
|
@@ -11164,12 +11440,12 @@ var ClaudeWorkerPermissionGate = class _ClaudeWorkerPermissionGate extends Claud
|
|
|
11164
11440
|
recordPermissionAudit(this.logger, {
|
|
11165
11441
|
decision: denied ? "denied" : "approved",
|
|
11166
11442
|
operation,
|
|
11167
|
-
access: auditAccessFor(
|
|
11168
|
-
resolvedPath:
|
|
11443
|
+
access: auditAccessFor(ask2.access),
|
|
11444
|
+
resolvedPath: ask2.resolvedPath,
|
|
11169
11445
|
requestedPath: this.requestedPathOf(input2),
|
|
11170
|
-
matchedEntry:
|
|
11171
|
-
matchedSource:
|
|
11172
|
-
matchedRule:
|
|
11446
|
+
matchedEntry: ask2.matchedEntry,
|
|
11447
|
+
matchedSource: ask2.matchedSource,
|
|
11448
|
+
matchedRule: ask2.matchedRule,
|
|
11173
11449
|
taskId: this._runContext.taskId,
|
|
11174
11450
|
workerId: this._runContext.workerId,
|
|
11175
11451
|
toolName,
|
|
@@ -11185,7 +11461,7 @@ var ClaudeWorkerPermissionGate = class _ClaudeWorkerPermissionGate extends Claud
|
|
|
11185
11461
|
return result2;
|
|
11186
11462
|
}
|
|
11187
11463
|
/** Human-readable summary of a gated call for the approval prompt. */
|
|
11188
|
-
describePermissionRequest(toolName, input2,
|
|
11464
|
+
describePermissionRequest(toolName, input2, ask2) {
|
|
11189
11465
|
let detail;
|
|
11190
11466
|
if (toolName === "Bash") {
|
|
11191
11467
|
detail = `Execute shell command: ${String(input2.command ?? "")}`;
|
|
@@ -11198,15 +11474,15 @@ var ClaudeWorkerPermissionGate = class _ClaudeWorkerPermissionGate extends Claud
|
|
|
11198
11474
|
} else {
|
|
11199
11475
|
detail = `${toolName}: ${JSON.stringify(input2)}`;
|
|
11200
11476
|
}
|
|
11201
|
-
const hit =
|
|
11202
|
-
matchedRule:
|
|
11203
|
-
matchedEntry:
|
|
11204
|
-
matchedSource:
|
|
11205
|
-
resolvedPath:
|
|
11206
|
-
operation:
|
|
11477
|
+
const hit = ask2.sensitive ? verdictDetailSuffix({
|
|
11478
|
+
matchedRule: ask2.matchedRule,
|
|
11479
|
+
matchedEntry: ask2.matchedEntry,
|
|
11480
|
+
matchedSource: ask2.matchedSource,
|
|
11481
|
+
resolvedPath: ask2.resolvedPath,
|
|
11482
|
+
operation: ask2.operation
|
|
11207
11483
|
}) : "";
|
|
11208
11484
|
const withHit = `${detail}${hit}`;
|
|
11209
|
-
return
|
|
11485
|
+
return ask2.reason ? `${withHit} (${ask2.reason})` : withHit;
|
|
11210
11486
|
}
|
|
11211
11487
|
};
|
|
11212
11488
|
|
|
@@ -13120,9 +13396,9 @@ function mcpToolIdentity(name) {
|
|
|
13120
13396
|
const lower = name.trim().toLowerCase();
|
|
13121
13397
|
if (lower.startsWith("mcp__")) {
|
|
13122
13398
|
const rest = lower.slice("mcp__".length);
|
|
13123
|
-
const
|
|
13124
|
-
if (
|
|
13125
|
-
return { server: rest.slice(0,
|
|
13399
|
+
const sep3 = rest.lastIndexOf("__");
|
|
13400
|
+
if (sep3 <= 0 || sep3 + 2 >= rest.length) return void 0;
|
|
13401
|
+
return { server: rest.slice(0, sep3), tool: rest.slice(sep3 + 2) };
|
|
13126
13402
|
}
|
|
13127
13403
|
const qualified = /^([a-z0-9_]+)[.-]([a-z0-9_]+)$/.exec(lower);
|
|
13128
13404
|
return qualified ? { server: qualified[1], tool: qualified[2] } : void 0;
|
|
@@ -13225,7 +13501,7 @@ function bridgeToolArgs(snapshot) {
|
|
|
13225
13501
|
}
|
|
13226
13502
|
return { locations: snapshot.locations ?? [] };
|
|
13227
13503
|
}
|
|
13228
|
-
function describeRequest(snapshot, classification,
|
|
13504
|
+
function describeRequest(snapshot, classification, ask2) {
|
|
13229
13505
|
let detail;
|
|
13230
13506
|
if (classification.type === "policy" && classification.command !== void 0) {
|
|
13231
13507
|
detail = `Execute shell command: ${classification.command}`;
|
|
@@ -13234,15 +13510,15 @@ function describeRequest(snapshot, classification, ask) {
|
|
|
13234
13510
|
} else {
|
|
13235
13511
|
detail = `${bridgeToolName(snapshot)}: ${JSON.stringify(bridgeToolArgs(snapshot))}`;
|
|
13236
13512
|
}
|
|
13237
|
-
const hit =
|
|
13238
|
-
matchedRule:
|
|
13239
|
-
matchedEntry:
|
|
13240
|
-
matchedSource:
|
|
13241
|
-
resolvedPath:
|
|
13242
|
-
operation:
|
|
13513
|
+
const hit = ask2?.sensitive ? verdictDetailSuffix({
|
|
13514
|
+
matchedRule: ask2.matchedRule,
|
|
13515
|
+
matchedEntry: ask2.matchedEntry,
|
|
13516
|
+
matchedSource: ask2.matchedSource,
|
|
13517
|
+
resolvedPath: ask2.resolvedPath,
|
|
13518
|
+
operation: ask2.operation
|
|
13243
13519
|
}) : "";
|
|
13244
13520
|
const withHit = `${detail}${hit}`;
|
|
13245
|
-
return
|
|
13521
|
+
return ask2?.reason ? `${withHit} (${ask2.reason})` : withHit;
|
|
13246
13522
|
}
|
|
13247
13523
|
function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
13248
13524
|
const permissionMs = deps.permissionMs ?? ACP_PERMISSION_TIMEOUT_MS;
|
|
@@ -13295,7 +13571,7 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13295
13571
|
if (classification.type === "direct-allow") {
|
|
13296
13572
|
return outcomeFor("allow");
|
|
13297
13573
|
}
|
|
13298
|
-
const
|
|
13574
|
+
const ask2 = classification.type === "direct-ask" ? { reason: classification.reason, operation: classification.operation } : { operation: operationForTool(classification.toolName) };
|
|
13299
13575
|
if (classification.type === "policy") {
|
|
13300
13576
|
const verdict = decideToolAccess(deps.scope, classification.toolName, classification.args);
|
|
13301
13577
|
if (verdict.decision === "allow") {
|
|
@@ -13310,14 +13586,14 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13310
13586
|
});
|
|
13311
13587
|
return outcomeFor("allow");
|
|
13312
13588
|
}
|
|
13313
|
-
|
|
13314
|
-
|
|
13315
|
-
|
|
13316
|
-
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13589
|
+
ask2.reason = verdict.reason;
|
|
13590
|
+
ask2.matchedRule = verdict.matchedRule;
|
|
13591
|
+
ask2.matchedEntry = verdict.matchedEntry;
|
|
13592
|
+
ask2.matchedSource = verdict.matchedSource;
|
|
13593
|
+
ask2.resolvedPath = verdict.resolvedPath;
|
|
13594
|
+
ask2.sensitive = verdict.sensitive;
|
|
13595
|
+
ask2.access = verdict.access;
|
|
13596
|
+
ask2.askPaths = verdict.askPaths;
|
|
13321
13597
|
if (verdict.decision === "deny") {
|
|
13322
13598
|
audit({
|
|
13323
13599
|
...base,
|
|
@@ -13358,14 +13634,14 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13358
13634
|
emoji: deps.runContext.emoji,
|
|
13359
13635
|
toolName,
|
|
13360
13636
|
toolArgs: bridgeToolArgs(snapshot),
|
|
13361
|
-
description: describeRequest(snapshot, classification,
|
|
13362
|
-
...
|
|
13363
|
-
...
|
|
13364
|
-
...
|
|
13365
|
-
...
|
|
13366
|
-
...
|
|
13367
|
-
...
|
|
13368
|
-
operation:
|
|
13637
|
+
description: describeRequest(snapshot, classification, ask2),
|
|
13638
|
+
...ask2.matchedRule !== void 0 && { matchedRule: ask2.matchedRule },
|
|
13639
|
+
...ask2.matchedEntry !== void 0 && { matchedEntry: ask2.matchedEntry },
|
|
13640
|
+
...ask2.matchedSource !== void 0 && { matchedSource: ask2.matchedSource },
|
|
13641
|
+
...ask2.resolvedPath !== void 0 && { resolvedPath: ask2.resolvedPath },
|
|
13642
|
+
...ask2.sensitive !== void 0 && { sensitive: ask2.sensitive },
|
|
13643
|
+
...ask2.access !== void 0 && { access: ask2.access },
|
|
13644
|
+
operation: ask2.operation
|
|
13369
13645
|
},
|
|
13370
13646
|
...deps.turnLineage ?? {}
|
|
13371
13647
|
});
|
|
@@ -13376,11 +13652,11 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13376
13652
|
audit({
|
|
13377
13653
|
...base,
|
|
13378
13654
|
decision: result3.approved ? "approved" : "denied",
|
|
13379
|
-
access:
|
|
13380
|
-
resolvedPath:
|
|
13381
|
-
matchedRule:
|
|
13382
|
-
matchedEntry:
|
|
13383
|
-
matchedSource:
|
|
13655
|
+
access: ask2.access !== void 0 ? auditAccessFor(ask2.access) : "unknown",
|
|
13656
|
+
resolvedPath: ask2.resolvedPath,
|
|
13657
|
+
matchedRule: ask2.matchedRule,
|
|
13658
|
+
matchedEntry: ask2.matchedEntry,
|
|
13659
|
+
matchedSource: ask2.matchedSource,
|
|
13384
13660
|
...!result3.approved && { source: denialSource(result3.reason) }
|
|
13385
13661
|
});
|
|
13386
13662
|
if (result3.approved) {
|
|
@@ -13389,7 +13665,7 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13389
13665
|
}
|
|
13390
13666
|
return outcomeFor("allow");
|
|
13391
13667
|
}
|
|
13392
|
-
emitWaitDenial(result3.reason, toolName, denialTarget,
|
|
13668
|
+
emitWaitDenial(result3.reason, toolName, denialTarget, ask2.matchedRule);
|
|
13393
13669
|
return outcomeFor("reject");
|
|
13394
13670
|
}
|
|
13395
13671
|
const requestId = generateId("perm");
|
|
@@ -13405,8 +13681,8 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13405
13681
|
emoji: deps.runContext.emoji,
|
|
13406
13682
|
toolName,
|
|
13407
13683
|
toolArgs: bridgeToolArgs(snapshot),
|
|
13408
|
-
description: describeRequest(snapshot, classification,
|
|
13409
|
-
operation:
|
|
13684
|
+
description: describeRequest(snapshot, classification, ask2),
|
|
13685
|
+
operation: ask2.operation
|
|
13410
13686
|
},
|
|
13411
13687
|
...deps.turnLineage ?? {}
|
|
13412
13688
|
});
|
|
@@ -13420,7 +13696,7 @@ function createAcpPermissionBridge(deps, pendingPermissions) {
|
|
|
13420
13696
|
access: "unknown",
|
|
13421
13697
|
...!result2.approved && { source: denialSource(result2.reason) }
|
|
13422
13698
|
});
|
|
13423
|
-
if (!result2.approved) emitWaitDenial(result2.reason, toolName, denialTarget,
|
|
13699
|
+
if (!result2.approved) emitWaitDenial(result2.reason, toolName, denialTarget, ask2.matchedRule);
|
|
13424
13700
|
return result2.approved ? outcomeFor("allow") : outcomeFor("reject");
|
|
13425
13701
|
};
|
|
13426
13702
|
}
|
|
@@ -14001,8 +14277,8 @@ var BaseAcpWorker = class {
|
|
|
14001
14277
|
* MCP bridge serves it over a Unix socket (member presence is what the
|
|
14002
14278
|
* engine's `wireWorker` probe detects, as with ClaudeCodeWorker).
|
|
14003
14279
|
*/
|
|
14004
|
-
setInquirySupport(
|
|
14005
|
-
this._askController =
|
|
14280
|
+
setInquirySupport(ask2) {
|
|
14281
|
+
this._askController = ask2;
|
|
14006
14282
|
}
|
|
14007
14283
|
getState() {
|
|
14008
14284
|
return this.state;
|
|
@@ -15064,15 +15340,15 @@ ${joinControllerMessages(initialQueued)}`;
|
|
|
15064
15340
|
}
|
|
15065
15341
|
/** ask_controller: same blocking engine channel as ClaudeCodeWorker, with an audit entry. */
|
|
15066
15342
|
async mcpAsk(question, context) {
|
|
15067
|
-
const
|
|
15068
|
-
if (!
|
|
15343
|
+
const ask2 = this._askController;
|
|
15344
|
+
if (!ask2 || !this._mcpHost?.mcpAvailable) {
|
|
15069
15345
|
return "[UNAVAILABLE] The controller inquiry channel is not available for this worker.";
|
|
15070
15346
|
}
|
|
15071
15347
|
const q = capText(question, MAX_QUESTION_CHARS);
|
|
15072
15348
|
const c = context === void 0 ? void 0 : capText(context, MAX_CONTEXT_CHARS);
|
|
15073
15349
|
let answer;
|
|
15074
15350
|
try {
|
|
15075
|
-
answer = await
|
|
15351
|
+
answer = await ask2(q, c);
|
|
15076
15352
|
} catch (err) {
|
|
15077
15353
|
answer = `[UNAVAILABLE] ${err instanceof Error ? err.message : String(err)}`;
|
|
15078
15354
|
}
|
package/dist/mcp-bridge-entry.js
CHANGED