@atlasharness/atlascode 0.1.80 → 0.1.82
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 +1494 -333
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19862,7 +19862,7 @@ var init_openai = __esm(() => {
|
|
|
19862
19862
|
});
|
|
19863
19863
|
|
|
19864
19864
|
// src/modelprovider/constants.ts
|
|
19865
|
-
var API_IMAGE_MAX_BASE64_SIZE, IMAGE_TARGET_RAW_SIZE, PDF_TARGET_RAW_SIZE, API_PDF_MAX_PAGES = 100, LLM_TIMEOUT_DEFAULT_MS = 900000, LLM_TIMEOUT_CAP_MS = 1800000, LLM_TIMEOUT_UNIT_WARN_THRESHOLD_MS = 60000, STREAM_IDLE_TIMEOUT_MS = 300000, SUMMARIZE_CONNECTOR_TEXT_BETA_HEADER, AFK_MODE_BETA_HEADER;
|
|
19865
|
+
var API_IMAGE_MAX_BASE64_SIZE, IMAGE_TARGET_RAW_SIZE, PDF_TARGET_RAW_SIZE, API_PDF_MAX_PAGES = 100, LLM_TIMEOUT_DEFAULT_MS = 900000, LLM_TIMEOUT_CAP_MS = 1800000, LLM_TIMEOUT_UNIT_WARN_THRESHOLD_MS = 60000, STREAM_IDLE_TIMEOUT_MS = 300000, STREAM_IDLE_RETURN_GRACE_MS = 1000, SUMMARIZE_CONNECTOR_TEXT_BETA_HEADER, AFK_MODE_BETA_HEADER;
|
|
19866
19866
|
var init_constants2 = __esm(() => {
|
|
19867
19867
|
init_shared();
|
|
19868
19868
|
API_IMAGE_MAX_BASE64_SIZE = 5 * 1024 * 1024;
|
|
@@ -19901,7 +19901,7 @@ var init_clients = __esm(() => {
|
|
|
19901
19901
|
});
|
|
19902
19902
|
|
|
19903
19903
|
// src/modelprovider/streamIdleWatchdog.ts
|
|
19904
|
-
async function* withStreamIdleWatchdog(stream, timeoutMs = STREAM_IDLE_TIMEOUT_MS) {
|
|
19904
|
+
async function* withStreamIdleWatchdog(stream, timeoutMs = STREAM_IDLE_TIMEOUT_MS, returnGraceMs = STREAM_IDLE_RETURN_GRACE_MS) {
|
|
19905
19905
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
19906
19906
|
yield* stream;
|
|
19907
19907
|
return;
|
|
@@ -19933,7 +19933,19 @@ async function* withStreamIdleWatchdog(stream, timeoutMs = STREAM_IDLE_TIMEOUT_M
|
|
|
19933
19933
|
}
|
|
19934
19934
|
} finally {
|
|
19935
19935
|
try {
|
|
19936
|
-
|
|
19936
|
+
const ret = iterator.return?.();
|
|
19937
|
+
if (ret) {
|
|
19938
|
+
let graceTimer;
|
|
19939
|
+
const grace = new Promise((resolve) => {
|
|
19940
|
+
graceTimer = setTimeout(resolve, returnGraceMs);
|
|
19941
|
+
if (typeof graceTimer === "object" && graceTimer !== null && "unref" in graceTimer) {
|
|
19942
|
+
graceTimer.unref();
|
|
19943
|
+
}
|
|
19944
|
+
});
|
|
19945
|
+
await Promise.race([ret.then(() => {
|
|
19946
|
+
return;
|
|
19947
|
+
}, () => {}), grace]);
|
|
19948
|
+
}
|
|
19937
19949
|
} catch {}
|
|
19938
19950
|
}
|
|
19939
19951
|
}
|
|
@@ -27201,6 +27213,12 @@ function isQuotaExhaustedMessage(message) {
|
|
|
27201
27213
|
}
|
|
27202
27214
|
return false;
|
|
27203
27215
|
}
|
|
27216
|
+
function parseToolSchemaRejectionError(err) {
|
|
27217
|
+
if (!(err instanceof APIError2) || err.status !== 400)
|
|
27218
|
+
return null;
|
|
27219
|
+
const m = /invalid schema for (?:tool|function) [`'"]?([A-Za-z0-9_.\-]+)[`'"]?/i.exec(err.message);
|
|
27220
|
+
return m ? m[1] : null;
|
|
27221
|
+
}
|
|
27204
27222
|
function classifyAPIError(error) {
|
|
27205
27223
|
if (error instanceof Error && error.message === "Request was aborted.") {
|
|
27206
27224
|
return "aborted";
|
|
@@ -27341,6 +27359,8 @@ function shouldRetryModelError(err, signal) {
|
|
|
27341
27359
|
return false;
|
|
27342
27360
|
if (isClientRequestTimeout(err))
|
|
27343
27361
|
return false;
|
|
27362
|
+
if (parseToolSchemaRejectionError(err))
|
|
27363
|
+
return false;
|
|
27344
27364
|
return isRetryableError(err);
|
|
27345
27365
|
}
|
|
27346
27366
|
function llmTimeoutUnitHint(ms) {
|
|
@@ -29134,6 +29154,11 @@ var DEFAULT_HOOK_SHELL = "bash";
|
|
|
29134
29154
|
var init_getMatchingHooks = () => {};
|
|
29135
29155
|
|
|
29136
29156
|
// src/hooks/runHooks.ts
|
|
29157
|
+
function sessionEndHookTimeoutMs() {
|
|
29158
|
+
const raw = process.env.ATLAS_SESSIONEND_HOOKS_TIMEOUT_MS;
|
|
29159
|
+
const parsed = raw ? Number(raw) : NaN;
|
|
29160
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : SESSION_END_HOOK_TIMEOUT_MS_DEFAULT;
|
|
29161
|
+
}
|
|
29137
29162
|
function isSimpleModeHooksSkipped() {
|
|
29138
29163
|
const raw = process.env.ATLAS_SIMPLE;
|
|
29139
29164
|
if (raw === undefined)
|
|
@@ -29253,6 +29278,7 @@ ${r.additionalContext}` : r.additionalContext;
|
|
|
29253
29278
|
if (!acc.permissionBehavior || PERMISSION_RANK[r.permissionBehavior] > PERMISSION_RANK[acc.permissionBehavior]) {
|
|
29254
29279
|
acc.permissionBehavior = r.permissionBehavior;
|
|
29255
29280
|
acc.permissionDecisionReason = r.permissionDecisionReason;
|
|
29281
|
+
acc.permissionDecisionSource = r.source;
|
|
29256
29282
|
}
|
|
29257
29283
|
}
|
|
29258
29284
|
}
|
|
@@ -29270,12 +29296,14 @@ function runOneHook(m, port, env, signal, options) {
|
|
|
29270
29296
|
if (result.blockingError && cmd.continueOnBlock === true) {
|
|
29271
29297
|
result.continueOnBlock = true;
|
|
29272
29298
|
}
|
|
29299
|
+
result.source = m.source;
|
|
29273
29300
|
return result;
|
|
29274
29301
|
}, (e) => ({
|
|
29275
29302
|
command: cmd.command,
|
|
29276
29303
|
status: -1,
|
|
29277
29304
|
succeeded: false,
|
|
29278
|
-
stderr: e instanceof Error ? e.message : String(e)
|
|
29305
|
+
stderr: e instanceof Error ? e.message : String(e),
|
|
29306
|
+
source: m.source
|
|
29279
29307
|
}));
|
|
29280
29308
|
}
|
|
29281
29309
|
async function runHooks(hookEvent, hookInput, options = {}) {
|
|
@@ -29330,7 +29358,15 @@ async function runStopHooks(options = {}) {
|
|
|
29330
29358
|
const hookInput = { ...base, hook_event_name: "Stop" };
|
|
29331
29359
|
return runHooks("Stop", hookInput, options);
|
|
29332
29360
|
}
|
|
29333
|
-
|
|
29361
|
+
async function runSessionEndHooks(reason, options = {}) {
|
|
29362
|
+
const base = createBaseHookInput(options.permissionMode, options.sessionId, options.agentInfo);
|
|
29363
|
+
const hookInput = { ...base, hook_event_name: "SessionEnd", reason };
|
|
29364
|
+
return runHooks("SessionEnd", hookInput, {
|
|
29365
|
+
...options,
|
|
29366
|
+
timeoutMs: options.timeoutMs ?? sessionEndHookTimeoutMs()
|
|
29367
|
+
});
|
|
29368
|
+
}
|
|
29369
|
+
var MAX_HOOK_CONCURRENCY = 12, HOOK_EXECUTION_TIMEOUT_MS, SESSION_END_HOOK_TIMEOUT_MS_DEFAULT = 1500, PERMISSION_RANK;
|
|
29334
29370
|
var init_runHooks = __esm(() => {
|
|
29335
29371
|
init_createBaseHookInput();
|
|
29336
29372
|
init_getMatchingHooks();
|
|
@@ -29366,9 +29402,20 @@ async function* runHooksStream(hookEvent, hookInput, options = {}) {
|
|
|
29366
29402
|
toolUseID: options.toolUseID
|
|
29367
29403
|
};
|
|
29368
29404
|
}
|
|
29369
|
-
const
|
|
29370
|
-
|
|
29371
|
-
|
|
29405
|
+
const slots = [];
|
|
29406
|
+
let nextStart = 0;
|
|
29407
|
+
const startNext = () => {
|
|
29408
|
+
if (nextStart < matched.length) {
|
|
29409
|
+
slots[nextStart] = runOneHook(matched[nextStart], port, env, signal, options);
|
|
29410
|
+
nextStart++;
|
|
29411
|
+
}
|
|
29412
|
+
};
|
|
29413
|
+
for (let i = 0;i < Math.min(MAX_HOOK_CONCURRENCY, matched.length); i++) {
|
|
29414
|
+
startNext();
|
|
29415
|
+
}
|
|
29416
|
+
for (let i = 0;i < matched.length; i++) {
|
|
29417
|
+
const result = await slots[i];
|
|
29418
|
+
startNext();
|
|
29372
29419
|
results.push(result);
|
|
29373
29420
|
yield { kind: "hook_result", result };
|
|
29374
29421
|
mergeAggregated(aggregated, result);
|
|
@@ -29509,8 +29556,8 @@ var SettingsSchema = () => object({
|
|
|
29509
29556
|
mcpServers: record(string2(), any()).optional(),
|
|
29510
29557
|
allowedMcpServers: any().optional(),
|
|
29511
29558
|
deniedMcpServers: any().optional(),
|
|
29512
|
-
enabledMcpjsonServers: array(string2()).optional(),
|
|
29513
|
-
disabledMcpjsonServers: array(string2()).optional(),
|
|
29559
|
+
enabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
|
|
29560
|
+
disabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
|
|
29514
29561
|
sandbox: any().optional(),
|
|
29515
29562
|
permissions: any().optional(),
|
|
29516
29563
|
hooks: lazy(() => HooksSchema).optional(),
|
|
@@ -30144,7 +30191,7 @@ var init_filesystem = __esm(() => {
|
|
|
30144
30191
|
|
|
30145
30192
|
// src/permissions/permissionRuleParser.ts
|
|
30146
30193
|
function setLegacyToolNameAliases(map) {
|
|
30147
|
-
legacyToolNameAliases = map;
|
|
30194
|
+
legacyToolNameAliases = Object.assign(Object.create(null), map);
|
|
30148
30195
|
}
|
|
30149
30196
|
function normalizeLegacyToolName(name) {
|
|
30150
30197
|
return legacyToolNameAliases[name] ?? name;
|
|
@@ -30219,7 +30266,7 @@ function findLastUnescapedChar(str, char) {
|
|
|
30219
30266
|
}
|
|
30220
30267
|
var legacyToolNameAliases;
|
|
30221
30268
|
var init_permissionRuleParser = __esm(() => {
|
|
30222
|
-
legacyToolNameAliases =
|
|
30269
|
+
legacyToolNameAliases = Object.create(null);
|
|
30223
30270
|
});
|
|
30224
30271
|
|
|
30225
30272
|
// src/permissions/mcpRuleNames.ts
|
|
@@ -30228,6 +30275,9 @@ function normalizeNameForMCP(name) {
|
|
|
30228
30275
|
const normalized = folded.replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
30229
30276
|
return normalized === "" ? folded : normalized;
|
|
30230
30277
|
}
|
|
30278
|
+
function foldMcpNameForCompare(name) {
|
|
30279
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
|
|
30280
|
+
}
|
|
30231
30281
|
function mcpInfoFromString(toolString) {
|
|
30232
30282
|
const parts = toolString.split("__");
|
|
30233
30283
|
const [mcpPart, serverName, ...toolNameParts] = parts;
|
|
@@ -30297,9 +30347,17 @@ function toolMatchesRule(tool, rule) {
|
|
|
30297
30347
|
if (rule.ruleValue.toolName === nameForRuleMatch) {
|
|
30298
30348
|
return true;
|
|
30299
30349
|
}
|
|
30300
|
-
const
|
|
30301
|
-
const
|
|
30302
|
-
|
|
30350
|
+
const toolInfo = tool.mcpInfo ?? mcpInfoFromString(nameForRuleMatch);
|
|
30351
|
+
const ruleName = rule.ruleValue.toolName;
|
|
30352
|
+
if (!toolInfo || !ruleName.startsWith("mcp__")) {
|
|
30353
|
+
return false;
|
|
30354
|
+
}
|
|
30355
|
+
if (foldMcpNameForCompare(ruleName) === foldMcpNameForCompare(nameForRuleMatch)) {
|
|
30356
|
+
return true;
|
|
30357
|
+
}
|
|
30358
|
+
const rest = ruleName.slice("mcp__".length);
|
|
30359
|
+
const ruleServerRaw = rest.endsWith("__*") ? rest.slice(0, -3) : rest;
|
|
30360
|
+
return foldMcpNameForCompare(ruleServerRaw) === foldMcpNameForCompare(toolInfo.serverName);
|
|
30303
30361
|
}
|
|
30304
30362
|
function toolAlwaysAllowedRule(context, tool) {
|
|
30305
30363
|
return getAllowRules(context).find((rule) => toolMatchesRule(tool, rule)) || null;
|
|
@@ -30512,12 +30570,22 @@ function applyDontAskMode(decision, permissionContext, toolName) {
|
|
|
30512
30570
|
}
|
|
30513
30571
|
return decision;
|
|
30514
30572
|
}
|
|
30515
|
-
var BASH_TOOL_NAME = "Bash", hasPermissionsToUseTool = async (tool, input, context, _assistantMessage, _toolUseID, forceDecision) => {
|
|
30573
|
+
var BASH_TOOL_NAME = "Bash", PLAN_MODE_WRITE_EXEMPT_TOOL_NAMES, PLAN_MODE_WRITE_DENIED_MESSAGE = (toolName) => `Plan mode is active — ${toolName} is not allowed to make changes. ` + `Present your plan and use ExitPlanMode, or ask the user to change the permission mode.`, hasPermissionsToUseTool = async (tool, input, context, _assistantMessage, _toolUseID, forceDecision) => {
|
|
30516
30574
|
const permissionContext = context.getToolPermissionContext?.();
|
|
30517
30575
|
if (forceDecision !== undefined) {
|
|
30518
30576
|
return applyDontAskMode(forceDecision, permissionContext, tool.name);
|
|
30519
30577
|
}
|
|
30520
30578
|
const sandbox = getSandboxAccess();
|
|
30579
|
+
if (permissionContext?.mode === "plan" && !permissionContext.isBypassPermissionsModeAvailable && !PLAN_MODE_WRITE_EXEMPT_TOOL_NAMES.has(tool.name) && !(tool.isReadOnly?.(input) ?? false)) {
|
|
30580
|
+
return {
|
|
30581
|
+
behavior: "deny",
|
|
30582
|
+
message: PLAN_MODE_WRITE_DENIED_MESSAGE(tool.name),
|
|
30583
|
+
decisionReason: {
|
|
30584
|
+
type: "mode",
|
|
30585
|
+
mode: "plan"
|
|
30586
|
+
}
|
|
30587
|
+
};
|
|
30588
|
+
}
|
|
30521
30589
|
const denyRule = permissionContext ? getDenyRuleForTool(permissionContext, tool) : null;
|
|
30522
30590
|
if (denyRule) {
|
|
30523
30591
|
return {
|
|
@@ -30636,6 +30704,10 @@ var init_permissions2 = __esm(() => {
|
|
|
30636
30704
|
init_ruleMatching();
|
|
30637
30705
|
init_sandboxAccess();
|
|
30638
30706
|
init_denialMessages();
|
|
30707
|
+
PLAN_MODE_WRITE_EXEMPT_TOOL_NAMES = new Set([
|
|
30708
|
+
"EnterPlanMode",
|
|
30709
|
+
"ExitPlanMode"
|
|
30710
|
+
]);
|
|
30639
30711
|
});
|
|
30640
30712
|
|
|
30641
30713
|
// src/permissions/permissionMode.ts
|
|
@@ -36851,6 +36923,15 @@ function filterToolsForAgent({
|
|
|
36851
36923
|
function computeChildSpawnDepth(parentOptions) {
|
|
36852
36924
|
return (parentOptions?.spawnDepth ?? 0) + 1;
|
|
36853
36925
|
}
|
|
36926
|
+
function resolveMcpServerDomainTools(toolName, available) {
|
|
36927
|
+
if (typeof toolName !== "string")
|
|
36928
|
+
return null;
|
|
36929
|
+
const info = mcpInfoFromString(toolName);
|
|
36930
|
+
if (!info || info.toolName !== undefined)
|
|
36931
|
+
return null;
|
|
36932
|
+
const prefix = getMcpPrefix(info.serverName);
|
|
36933
|
+
return available.filter((t) => t.name.startsWith(prefix));
|
|
36934
|
+
}
|
|
36854
36935
|
function resolveAgentTools(agentDefinition, availableTools, allowFanOut = false) {
|
|
36855
36936
|
const { tools: agentTools, disallowedTools, source } = agentDefinition;
|
|
36856
36937
|
const filtered = filterToolsForAgent({
|
|
@@ -36881,7 +36962,18 @@ function resolveAgentTools(agentDefinition, availableTools, allowFanOut = false)
|
|
|
36881
36962
|
seen.add(tool);
|
|
36882
36963
|
}
|
|
36883
36964
|
} else {
|
|
36884
|
-
|
|
36965
|
+
const domain = resolveMcpServerDomainTools(toolName, allowed);
|
|
36966
|
+
if (domain && domain.length > 0) {
|
|
36967
|
+
validTools.push(spec);
|
|
36968
|
+
for (const t of domain) {
|
|
36969
|
+
if (!seen.has(t)) {
|
|
36970
|
+
resolved.push(t);
|
|
36971
|
+
seen.add(t);
|
|
36972
|
+
}
|
|
36973
|
+
}
|
|
36974
|
+
} else {
|
|
36975
|
+
invalidTools.push(spec);
|
|
36976
|
+
}
|
|
36885
36977
|
}
|
|
36886
36978
|
}
|
|
36887
36979
|
return { hasWildcard: false, validTools, invalidTools, resolvedTools: resolved };
|
|
@@ -46561,9 +46653,67 @@ var init_bashCommandHelpers = __esm(() => {
|
|
|
46561
46653
|
});
|
|
46562
46654
|
|
|
46563
46655
|
// src/engine/tools/bash/bashReadOnly.ts
|
|
46656
|
+
function globPatternCouldMatchWriteAction(pattern) {
|
|
46657
|
+
let regex = "^";
|
|
46658
|
+
let i = 0;
|
|
46659
|
+
while (i < pattern.length) {
|
|
46660
|
+
const c = pattern[i];
|
|
46661
|
+
if (c === "*") {
|
|
46662
|
+
regex += ".*";
|
|
46663
|
+
i++;
|
|
46664
|
+
continue;
|
|
46665
|
+
}
|
|
46666
|
+
if (c === "?") {
|
|
46667
|
+
regex += ".";
|
|
46668
|
+
i++;
|
|
46669
|
+
continue;
|
|
46670
|
+
}
|
|
46671
|
+
if (c === "[") {
|
|
46672
|
+
const close = pattern.indexOf("]", i + 1);
|
|
46673
|
+
if (close > i) {
|
|
46674
|
+
let cls = pattern.slice(i + 1, close);
|
|
46675
|
+
if (cls.startsWith("!"))
|
|
46676
|
+
cls = `^${cls.slice(1)}`;
|
|
46677
|
+
regex += `[${cls}]`;
|
|
46678
|
+
i = close + 1;
|
|
46679
|
+
continue;
|
|
46680
|
+
}
|
|
46681
|
+
}
|
|
46682
|
+
regex += c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46683
|
+
i++;
|
|
46684
|
+
}
|
|
46685
|
+
regex += "$";
|
|
46686
|
+
let re;
|
|
46687
|
+
try {
|
|
46688
|
+
re = new RegExp(regex);
|
|
46689
|
+
} catch {
|
|
46690
|
+
return true;
|
|
46691
|
+
}
|
|
46692
|
+
for (const action of FIND_WRITE_ACTION_TOKENS) {
|
|
46693
|
+
if (re.test(action))
|
|
46694
|
+
return true;
|
|
46695
|
+
}
|
|
46696
|
+
return false;
|
|
46697
|
+
}
|
|
46698
|
+
function isAllowedNonStringToken(token) {
|
|
46699
|
+
if (typeof token !== "object" || token === null)
|
|
46700
|
+
return false;
|
|
46701
|
+
const t = token;
|
|
46702
|
+
if (t.comment !== undefined)
|
|
46703
|
+
return true;
|
|
46704
|
+
if (t.op === "<")
|
|
46705
|
+
return true;
|
|
46706
|
+
if (t.op === "glob") {
|
|
46707
|
+
if (typeof t.pattern !== "string")
|
|
46708
|
+
return false;
|
|
46709
|
+
return !globPatternCouldMatchWriteAction(t.pattern);
|
|
46710
|
+
}
|
|
46711
|
+
return false;
|
|
46712
|
+
}
|
|
46564
46713
|
function matchReadOnlyPrefix(command) {
|
|
46565
46714
|
const trimmed = command.trim();
|
|
46566
|
-
if (trimmed.includes("&&") || trimmed.includes("||") || trimmed.includes(";") || trimmed.includes("|") || trimmed.includes("`") || trimmed.includes("$(") || trimmed.includes(">")
|
|
46715
|
+
if (trimmed.includes("&&") || trimmed.includes("||") || trimmed.includes(";") || trimmed.includes("|") || trimmed.includes("`") || trimmed.includes("$(") || trimmed.includes(">") || trimmed.includes(`
|
|
46716
|
+
`) || trimmed.includes("\r") || trimmed.includes("-delete") || trimmed.includes("-exec") || trimmed.includes("-ok") || trimmed.includes("-fprint") || trimmed.includes("-fls")) {
|
|
46567
46717
|
return null;
|
|
46568
46718
|
}
|
|
46569
46719
|
let best = null;
|
|
@@ -46573,13 +46723,31 @@ function matchReadOnlyPrefix(command) {
|
|
|
46573
46723
|
best = p;
|
|
46574
46724
|
}
|
|
46575
46725
|
}
|
|
46726
|
+
if (best === null)
|
|
46727
|
+
return null;
|
|
46728
|
+
const parsed = tryParseShellCommand(trimmed, (key) => `$${key}`);
|
|
46729
|
+
if (!parsed.success) {
|
|
46730
|
+
return null;
|
|
46731
|
+
}
|
|
46732
|
+
for (const token of parsed.tokens) {
|
|
46733
|
+
if (typeof token === "string") {
|
|
46734
|
+
if (FIND_WRITE_ACTION_TOKENS.has(token))
|
|
46735
|
+
return null;
|
|
46736
|
+
if (token.includes("[") && globPatternCouldMatchWriteAction(token)) {
|
|
46737
|
+
return null;
|
|
46738
|
+
}
|
|
46739
|
+
} else if (!isAllowedNonStringToken(token)) {
|
|
46740
|
+
return null;
|
|
46741
|
+
}
|
|
46742
|
+
}
|
|
46576
46743
|
return best;
|
|
46577
46744
|
}
|
|
46578
46745
|
function isReadOnlyCommand(command) {
|
|
46579
46746
|
return matchReadOnlyPrefix(command) !== null;
|
|
46580
46747
|
}
|
|
46581
|
-
var READ_ONLY_PREFIXES;
|
|
46748
|
+
var READ_ONLY_PREFIXES, FIND_WRITE_ACTION_TOKENS;
|
|
46582
46749
|
var init_bashReadOnly = __esm(() => {
|
|
46750
|
+
init_shellQuote();
|
|
46583
46751
|
READ_ONLY_PREFIXES = [
|
|
46584
46752
|
"ls",
|
|
46585
46753
|
"cat",
|
|
@@ -46606,6 +46774,17 @@ var init_bashReadOnly = __esm(() => {
|
|
|
46606
46774
|
"printenv",
|
|
46607
46775
|
"type"
|
|
46608
46776
|
];
|
|
46777
|
+
FIND_WRITE_ACTION_TOKENS = new Set([
|
|
46778
|
+
"-delete",
|
|
46779
|
+
"-exec",
|
|
46780
|
+
"-execdir",
|
|
46781
|
+
"-ok",
|
|
46782
|
+
"-okdir",
|
|
46783
|
+
"-fprint",
|
|
46784
|
+
"-fprint0",
|
|
46785
|
+
"-fprintf",
|
|
46786
|
+
"-fls"
|
|
46787
|
+
]);
|
|
46609
46788
|
});
|
|
46610
46789
|
|
|
46611
46790
|
// src/engine/tools/bash/abortError.ts
|
|
@@ -75284,6 +75463,27 @@ var init_tasks3 = __esm(() => {
|
|
|
75284
75463
|
init_taskOutputTool();
|
|
75285
75464
|
});
|
|
75286
75465
|
|
|
75466
|
+
// src/engine/skill/commandNameSafety.ts
|
|
75467
|
+
function isUnsafeCommandName(name) {
|
|
75468
|
+
if (name === "")
|
|
75469
|
+
return true;
|
|
75470
|
+
if (name.includes("<") || name.includes(">"))
|
|
75471
|
+
return true;
|
|
75472
|
+
return UNSAFE_NAME_CHARS_RE.test(name);
|
|
75473
|
+
}
|
|
75474
|
+
function sanitizeDisplayName(displayName, canonicalName) {
|
|
75475
|
+
if (displayName === undefined || !isUnsafeCommandName(displayName)) {
|
|
75476
|
+
return displayName;
|
|
75477
|
+
}
|
|
75478
|
+
logForDebugging(`Discarding unsafe frontmatter display name ${JSON.stringify(displayName)} for ${canonicalName}; falling back to canonical name`);
|
|
75479
|
+
return;
|
|
75480
|
+
}
|
|
75481
|
+
var UNSAFE_NAME_CHARS_RE;
|
|
75482
|
+
var init_commandNameSafety = __esm(() => {
|
|
75483
|
+
init_shared();
|
|
75484
|
+
UNSAFE_NAME_CHARS_RE = /[\u0000-\u001F\u007F-\u00A0\u00AD\u034F\u061C\u070F\u1680\u180E\u2000-\u200F\u2028\u2029\u202A-\u202E\u202F\u205F\u2060-\u2069\u3000\uFE00-\uFE0F\uFEFF\u{E0100}-\u{E01EF}]/u;
|
|
75485
|
+
});
|
|
75486
|
+
|
|
75287
75487
|
// src/engine/skill/types.ts
|
|
75288
75488
|
function getCommandName(cmd) {
|
|
75289
75489
|
return cmd.userFacingName?.() ?? cmd.name;
|
|
@@ -75406,44 +75606,129 @@ function parseArgumentNames(argumentNames) {
|
|
|
75406
75606
|
return [];
|
|
75407
75607
|
}
|
|
75408
75608
|
const isValidName = (name) => typeof name === "string" && name.trim() !== "" && !/^\d+$/.test(name);
|
|
75609
|
+
const cap = (names) => names.map((name) => name.slice(0, MAX_ARGUMENT_NAME_CHARS)).slice(0, MAX_ARGUMENT_NAMES);
|
|
75409
75610
|
if (Array.isArray(argumentNames)) {
|
|
75410
|
-
return argumentNames.filter(isValidName);
|
|
75611
|
+
return cap(argumentNames.filter(isValidName));
|
|
75411
75612
|
}
|
|
75412
75613
|
if (typeof argumentNames === "string") {
|
|
75413
|
-
return argumentNames.split(/\s+/).filter(isValidName);
|
|
75614
|
+
return cap(argumentNames.split(/\s+/).filter(isValidName));
|
|
75414
75615
|
}
|
|
75415
75616
|
return [];
|
|
75416
75617
|
}
|
|
75417
|
-
function
|
|
75618
|
+
function isWordChar2(c) {
|
|
75619
|
+
return c !== undefined && /[A-Za-z0-9_]/.test(c);
|
|
75620
|
+
}
|
|
75621
|
+
function matchPlaceholderAt(content, i, argumentNames) {
|
|
75622
|
+
if (content[i] !== "$")
|
|
75623
|
+
return null;
|
|
75624
|
+
for (let n = 0;n < argumentNames.length; n++) {
|
|
75625
|
+
const name = argumentNames[n];
|
|
75626
|
+
if (!name)
|
|
75627
|
+
continue;
|
|
75628
|
+
if (content.startsWith(`$${name}`, i)) {
|
|
75629
|
+
const after = content[i + 1 + name.length];
|
|
75630
|
+
if (after !== "[" && !isWordChar2(after)) {
|
|
75631
|
+
return { length: 1 + name.length, kind: "named", nameIndex: n };
|
|
75632
|
+
}
|
|
75633
|
+
}
|
|
75634
|
+
}
|
|
75635
|
+
INDEXED_PLACEHOLDER_RE.lastIndex = i;
|
|
75636
|
+
const indexed = INDEXED_PLACEHOLDER_RE.exec(content);
|
|
75637
|
+
if (indexed) {
|
|
75638
|
+
return {
|
|
75639
|
+
length: indexed[0].length,
|
|
75640
|
+
kind: "indexed",
|
|
75641
|
+
argIndex: parseInt(indexed[1], 10)
|
|
75642
|
+
};
|
|
75643
|
+
}
|
|
75644
|
+
if (content.startsWith("$ARGUMENTS", i)) {
|
|
75645
|
+
return { length: "$ARGUMENTS".length, kind: "bare" };
|
|
75646
|
+
}
|
|
75647
|
+
SHORTHAND_PLACEHOLDER_RE.lastIndex = i;
|
|
75648
|
+
const shorthand = SHORTHAND_PLACEHOLDER_RE.exec(content);
|
|
75649
|
+
if (shorthand && !isWordChar2(content[i + shorthand[0].length])) {
|
|
75650
|
+
return {
|
|
75651
|
+
length: shorthand[0].length,
|
|
75652
|
+
kind: "shorthand",
|
|
75653
|
+
argIndex: parseInt(shorthand[1], 10)
|
|
75654
|
+
};
|
|
75655
|
+
}
|
|
75656
|
+
return null;
|
|
75657
|
+
}
|
|
75658
|
+
function countPlaceholderOccurrences(content, argumentNames) {
|
|
75659
|
+
let count = 0;
|
|
75660
|
+
let i = 0;
|
|
75661
|
+
while (i < content.length) {
|
|
75662
|
+
const m = matchPlaceholderAt(content, i, argumentNames);
|
|
75663
|
+
if (m) {
|
|
75664
|
+
count++;
|
|
75665
|
+
i += m.length;
|
|
75666
|
+
} else {
|
|
75667
|
+
i++;
|
|
75668
|
+
}
|
|
75669
|
+
}
|
|
75670
|
+
return count;
|
|
75671
|
+
}
|
|
75672
|
+
function substituteArgumentsImpl(content, args, appendIfNoPlaceholder, argumentNames, strict) {
|
|
75418
75673
|
if (args === undefined || args === null) {
|
|
75419
75674
|
return content;
|
|
75420
75675
|
}
|
|
75421
|
-
const
|
|
75422
|
-
|
|
75423
|
-
|
|
75424
|
-
|
|
75425
|
-
|
|
75426
|
-
|
|
75427
|
-
|
|
75676
|
+
const placeholderCount = countPlaceholderOccurrences(content, argumentNames);
|
|
75677
|
+
let effectiveArgs = args;
|
|
75678
|
+
if (placeholderCount > 0 && content.length + placeholderCount * args.length > MAX_SUBSTITUTION_OUTPUT_CHARS) {
|
|
75679
|
+
if (strict)
|
|
75680
|
+
return null;
|
|
75681
|
+
const budgetPerReplacement = Math.max(0, Math.floor((MAX_SUBSTITUTION_OUTPUT_CHARS - content.length) / placeholderCount));
|
|
75682
|
+
effectiveArgs = args.slice(0, budgetPerReplacement);
|
|
75683
|
+
} else if (strict) {
|
|
75684
|
+
const appendLen = appendIfNoPlaceholder && args ? `
|
|
75685
|
+
|
|
75686
|
+
ARGUMENTS: `.length + args.length : 0;
|
|
75687
|
+
if (content.length + appendLen > MAX_SUBSTITUTION_OUTPUT_CHARS) {
|
|
75688
|
+
return null;
|
|
75689
|
+
}
|
|
75428
75690
|
}
|
|
75429
|
-
|
|
75430
|
-
|
|
75431
|
-
|
|
75432
|
-
|
|
75433
|
-
|
|
75434
|
-
|
|
75435
|
-
|
|
75436
|
-
}
|
|
75437
|
-
|
|
75438
|
-
|
|
75439
|
-
|
|
75691
|
+
const parsedArgs = parseArguments(effectiveArgs);
|
|
75692
|
+
let out = "";
|
|
75693
|
+
const appendValue = (value) => {
|
|
75694
|
+
if (out.length + value.length > MAX_SUBSTITUTION_OUTPUT_CHARS) {
|
|
75695
|
+
value = value.slice(0, Math.max(0, MAX_SUBSTITUTION_OUTPUT_CHARS - out.length));
|
|
75696
|
+
}
|
|
75697
|
+
out += value;
|
|
75698
|
+
};
|
|
75699
|
+
let matched = false;
|
|
75700
|
+
let i = 0;
|
|
75701
|
+
while (i < content.length) {
|
|
75702
|
+
const m = matchPlaceholderAt(content, i, argumentNames);
|
|
75703
|
+
if (m) {
|
|
75704
|
+
matched = true;
|
|
75705
|
+
if (m.kind === "bare")
|
|
75706
|
+
appendValue(effectiveArgs);
|
|
75707
|
+
else if (m.kind === "named")
|
|
75708
|
+
appendValue(parsedArgs[m.nameIndex] ?? "");
|
|
75709
|
+
else
|
|
75710
|
+
appendValue(parsedArgs[m.argIndex] ?? "");
|
|
75711
|
+
i += m.length;
|
|
75712
|
+
} else {
|
|
75713
|
+
out += content[i];
|
|
75714
|
+
i++;
|
|
75715
|
+
}
|
|
75716
|
+
}
|
|
75717
|
+
if (!matched && appendIfNoPlaceholder && args) {
|
|
75718
|
+
out = out + `
|
|
75440
75719
|
|
|
75441
75720
|
ARGUMENTS: ${args}`;
|
|
75442
75721
|
}
|
|
75443
|
-
return
|
|
75722
|
+
return out;
|
|
75723
|
+
}
|
|
75724
|
+
function substituteArguments(content, args, appendIfNoPlaceholder = true, argumentNames = []) {
|
|
75725
|
+
return substituteArgumentsImpl(content, args, appendIfNoPlaceholder, argumentNames, false) ?? content;
|
|
75444
75726
|
}
|
|
75727
|
+
var MAX_ARGUMENT_NAMES = 64, MAX_ARGUMENT_NAME_CHARS = 64, MAX_SUBSTITUTION_OUTPUT_CHARS = 1e6, INDEXED_PLACEHOLDER_RE, SHORTHAND_PLACEHOLDER_RE;
|
|
75445
75728
|
var init_argumentSubstitution = __esm(() => {
|
|
75446
75729
|
init_bash();
|
|
75730
|
+
INDEXED_PLACEHOLDER_RE = /\$ARGUMENTS\[(\d+)\]/y;
|
|
75731
|
+
SHORTHAND_PLACEHOLDER_RE = /\$(\d+)/y;
|
|
75447
75732
|
});
|
|
75448
75733
|
|
|
75449
75734
|
// src/engine/skill/skillModel.ts
|
|
@@ -76880,7 +77165,7 @@ function parseSkillFrontmatterFields(frontmatter, markdownContent, resolvedName,
|
|
|
76880
77165
|
logForDebugging(`Skill ${resolvedName} has invalid effort '${effortRaw}'. Valid options: ${EFFORT_LEVELS.join(", ")} or an integer`);
|
|
76881
77166
|
}
|
|
76882
77167
|
return {
|
|
76883
|
-
displayName: frontmatter.name != null ? String(frontmatter.name) : undefined,
|
|
77168
|
+
displayName: sanitizeDisplayName(frontmatter.name != null ? String(frontmatter.name) : undefined, resolvedName),
|
|
76884
77169
|
description,
|
|
76885
77170
|
hasUserSpecifiedDescription: validatedDescription !== null,
|
|
76886
77171
|
allowedTools: parseSlashCommandToolsFromFrontmatter(frontmatter["allowed-tools"]),
|
|
@@ -76991,6 +77276,7 @@ var init_skillCommand = __esm(() => {
|
|
|
76991
77276
|
init_frontmatterFields();
|
|
76992
77277
|
init_markdownLoader();
|
|
76993
77278
|
init_skillModel();
|
|
77279
|
+
init_commandNameSafety();
|
|
76994
77280
|
init_promptShellExecution();
|
|
76995
77281
|
});
|
|
76996
77282
|
|
|
@@ -77030,6 +77316,10 @@ async function loadSkillsFromSkillsDir(basePath, source) {
|
|
|
77030
77316
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
77031
77317
|
return null;
|
|
77032
77318
|
}
|
|
77319
|
+
if (isUnsafeCommandName(entry.name)) {
|
|
77320
|
+
logForDebugging(`[skills] skipping unsafe skill directory name: ${JSON.stringify(entry.name)}`);
|
|
77321
|
+
return null;
|
|
77322
|
+
}
|
|
77033
77323
|
const skillDirPath = join32(basePath, entry.name);
|
|
77034
77324
|
const skillFilePath = join32(skillDirPath, "SKILL.md");
|
|
77035
77325
|
let content;
|
|
@@ -77138,6 +77428,10 @@ async function loadSkillsFromCommandsDir(cwd) {
|
|
|
77138
77428
|
content,
|
|
77139
77429
|
source
|
|
77140
77430
|
});
|
|
77431
|
+
if (isUnsafeCommandName(cmdName)) {
|
|
77432
|
+
logForDebugging(`[skills] skipping unsafe command name: ${JSON.stringify(cmdName)}`);
|
|
77433
|
+
continue;
|
|
77434
|
+
}
|
|
77141
77435
|
const parsed = parseSkillFrontmatterFields(frontmatter, content, cmdName, "Custom command");
|
|
77142
77436
|
skills.push({
|
|
77143
77437
|
skill: createSkillCommand({
|
|
@@ -77177,6 +77471,7 @@ var init_loadSkillsDir = __esm(() => {
|
|
|
77177
77471
|
init_memory();
|
|
77178
77472
|
init_config();
|
|
77179
77473
|
init_messaging();
|
|
77474
|
+
init_commandNameSafety();
|
|
77180
77475
|
init_gitignore();
|
|
77181
77476
|
init_markdownLoader();
|
|
77182
77477
|
init_skillCommand();
|
|
@@ -77592,6 +77887,7 @@ var init_pluginIdentifier = __esm(() => {
|
|
|
77592
77887
|
|
|
77593
77888
|
// src/engine/skill/index.ts
|
|
77594
77889
|
var init_skill = __esm(() => {
|
|
77890
|
+
init_commandNameSafety();
|
|
77595
77891
|
init_frontmatterFields();
|
|
77596
77892
|
init_argumentSubstitution();
|
|
77597
77893
|
init_skillModel();
|
|
@@ -77645,6 +77941,14 @@ import { randomUUID as randomUUID14 } from "crypto";
|
|
|
77645
77941
|
async function getAllCommands() {
|
|
77646
77942
|
return getCommands(process.cwd());
|
|
77647
77943
|
}
|
|
77944
|
+
function buildUnknownSkillMessage(skillName, commands) {
|
|
77945
|
+
const available = commands.filter((c) => !c.isHidden && !c.disableModelInvocation).map((c) => c.userFacingName?.() ?? c.name);
|
|
77946
|
+
if (available.length === 0)
|
|
77947
|
+
return `Unknown skill: ${skillName}`;
|
|
77948
|
+
const listed = available.slice(0, MAX_UNKNOWN_SKILL_SUGGESTIONS);
|
|
77949
|
+
const more = available.length - listed.length;
|
|
77950
|
+
return `Unknown skill: ${skillName}. Available skills: ${listed.join(", ")}${more > 0 ? ` (and ${more} more)` : ""}`;
|
|
77951
|
+
}
|
|
77648
77952
|
function getToolUseIDFromParentMessage(parentMessage, toolName) {
|
|
77649
77953
|
if (!parentMessage)
|
|
77650
77954
|
return;
|
|
@@ -77712,7 +78016,7 @@ function skillHasOnlySafeProperties(command) {
|
|
|
77712
78016
|
}
|
|
77713
78017
|
return true;
|
|
77714
78018
|
}
|
|
77715
|
-
var SKILL_TOOL_INPUT_SCHEMA, SAFE_SKILL_PROPERTIES, SkillTool;
|
|
78019
|
+
var SKILL_TOOL_INPUT_SCHEMA, MAX_UNKNOWN_SKILL_SUGGESTIONS = 10, SAFE_SKILL_PROPERTIES, SkillTool;
|
|
77716
78020
|
var init_skillTool = __esm(() => {
|
|
77717
78021
|
init_shared();
|
|
77718
78022
|
init_permissions3();
|
|
@@ -77884,7 +78188,7 @@ var init_skillTool = __esm(() => {
|
|
|
77884
78188
|
if (!foundCommand) {
|
|
77885
78189
|
return {
|
|
77886
78190
|
result: false,
|
|
77887
|
-
message:
|
|
78191
|
+
message: buildUnknownSkillMessage(normalizedCommandName, commands),
|
|
77888
78192
|
errorCode: 2
|
|
77889
78193
|
};
|
|
77890
78194
|
}
|
|
@@ -78400,7 +78704,7 @@ function getMcpDiscoveryInput() {
|
|
|
78400
78704
|
return discoveryInput;
|
|
78401
78705
|
}
|
|
78402
78706
|
async function buildMcpServerConfigs(input) {
|
|
78403
|
-
const servers =
|
|
78707
|
+
const servers = Object.create(null);
|
|
78404
78708
|
for (const [name, raw] of Object.entries(input.settingsServers ?? {})) {
|
|
78405
78709
|
const parsed = parseMcpServerConfig(raw);
|
|
78406
78710
|
if (parsed) {
|
|
@@ -78631,6 +78935,7 @@ async function terminateChild(name, child) {
|
|
|
78631
78935
|
function createMcpConnectionManager() {
|
|
78632
78936
|
const connections = new Map;
|
|
78633
78937
|
const pending = new Set;
|
|
78938
|
+
const pendingConnects = new Map;
|
|
78634
78939
|
const connectFailures = new Map;
|
|
78635
78940
|
function fail(name, config, error, authFailure = false) {
|
|
78636
78941
|
const conn = {
|
|
@@ -78643,6 +78948,17 @@ function createMcpConnectionManager() {
|
|
|
78643
78948
|
connections.set(name, conn);
|
|
78644
78949
|
return conn;
|
|
78645
78950
|
}
|
|
78951
|
+
function markBlocked(name, config, reason) {
|
|
78952
|
+
const conn = {
|
|
78953
|
+
name,
|
|
78954
|
+
type: "failed",
|
|
78955
|
+
config,
|
|
78956
|
+
error: `MCP server "${name}" blocked by enterprise policy: ${reason}`,
|
|
78957
|
+
blockedByPolicy: true
|
|
78958
|
+
};
|
|
78959
|
+
connections.set(name, conn);
|
|
78960
|
+
return conn;
|
|
78961
|
+
}
|
|
78646
78962
|
async function connectStdio(name, config, opts) {
|
|
78647
78963
|
const finalCommand = process.env.ATLAS_SHELL_PREFIX || config.command;
|
|
78648
78964
|
const finalArgs = process.env.ATLAS_SHELL_PREFIX ? [[config.command, ...config.args ?? []].join(" ")] : config.args ?? [];
|
|
@@ -78700,7 +79016,7 @@ function createMcpConnectionManager() {
|
|
|
78700
79016
|
};
|
|
78701
79017
|
client.onClose(closeOnDrop);
|
|
78702
79018
|
const initPromise = client.request("initialize", {
|
|
78703
|
-
protocolVersion: "
|
|
79019
|
+
protocolVersion: "2026-07-28",
|
|
78704
79020
|
capabilities: {
|
|
78705
79021
|
elicitation: {},
|
|
78706
79022
|
roots: {}
|
|
@@ -78727,6 +79043,7 @@ function createMcpConnectionManager() {
|
|
|
78727
79043
|
version: init.serverInfo.version
|
|
78728
79044
|
}
|
|
78729
79045
|
} : {},
|
|
79046
|
+
...init.protocolVersion ? { negotiatedProtocolVersion: init.protocolVersion } : {},
|
|
78730
79047
|
...init.instructions ? { instructions: init.instructions } : {},
|
|
78731
79048
|
config,
|
|
78732
79049
|
cleanup: async () => {
|
|
@@ -78753,6 +79070,21 @@ function createMcpConnectionManager() {
|
|
|
78753
79070
|
}
|
|
78754
79071
|
}
|
|
78755
79072
|
async function connect(name, config, opts) {
|
|
79073
|
+
const existing = connections.get(name);
|
|
79074
|
+
if (existing?.type === "connected")
|
|
79075
|
+
return existing;
|
|
79076
|
+
const inflight = pendingConnects.get(name);
|
|
79077
|
+
if (inflight)
|
|
79078
|
+
return inflight;
|
|
79079
|
+
const attempt = connectOnce(name, config, opts);
|
|
79080
|
+
pendingConnects.set(name, attempt);
|
|
79081
|
+
try {
|
|
79082
|
+
return await attempt;
|
|
79083
|
+
} finally {
|
|
79084
|
+
pendingConnects.delete(name);
|
|
79085
|
+
}
|
|
79086
|
+
}
|
|
79087
|
+
async function connectOnce(name, config, opts) {
|
|
78756
79088
|
const existing = connections.get(name);
|
|
78757
79089
|
if (existing?.type === "connected")
|
|
78758
79090
|
return existing;
|
|
@@ -78794,7 +79126,8 @@ function createMcpConnectionManager() {
|
|
|
78794
79126
|
await manager.close(name);
|
|
78795
79127
|
}
|
|
78796
79128
|
},
|
|
78797
|
-
getPendingServerNames: () => [...pending]
|
|
79129
|
+
getPendingServerNames: () => [...pending],
|
|
79130
|
+
markBlocked
|
|
78798
79131
|
};
|
|
78799
79132
|
return manager;
|
|
78800
79133
|
}
|
|
@@ -78809,6 +79142,143 @@ var init_mcpConnectionManager = __esm(() => {
|
|
|
78809
79142
|
init_mcpFetch();
|
|
78810
79143
|
});
|
|
78811
79144
|
|
|
79145
|
+
// src/mcp/mcpPolicy.ts
|
|
79146
|
+
function getMcpPolicyFromSettings(settings) {
|
|
79147
|
+
if (typeof settings !== "object" || settings === null)
|
|
79148
|
+
return {};
|
|
79149
|
+
const rec = settings;
|
|
79150
|
+
return {
|
|
79151
|
+
...rec.allowedMcpServers !== undefined ? { allowedMcpServers: rec.allowedMcpServers } : {},
|
|
79152
|
+
...rec.deniedMcpServers !== undefined ? { deniedMcpServers: rec.deniedMcpServers } : {}
|
|
79153
|
+
};
|
|
79154
|
+
}
|
|
79155
|
+
function getMcpPolicyInput() {
|
|
79156
|
+
return policyInput;
|
|
79157
|
+
}
|
|
79158
|
+
function normalizePolicyEntries(raw) {
|
|
79159
|
+
if (!Array.isArray(raw))
|
|
79160
|
+
return [];
|
|
79161
|
+
const out = [];
|
|
79162
|
+
for (const entry of raw) {
|
|
79163
|
+
if (typeof entry === "string") {
|
|
79164
|
+
out.push({ kind: "name", value: entry });
|
|
79165
|
+
continue;
|
|
79166
|
+
}
|
|
79167
|
+
if (typeof entry !== "object" || entry === null)
|
|
79168
|
+
continue;
|
|
79169
|
+
const rec = entry;
|
|
79170
|
+
if (typeof rec.serverName === "string") {
|
|
79171
|
+
out.push({ kind: "name", value: rec.serverName });
|
|
79172
|
+
} else if (Array.isArray(rec.serverCommand) && rec.serverCommand.every((c) => typeof c === "string")) {
|
|
79173
|
+
out.push({ kind: "command", value: rec.serverCommand });
|
|
79174
|
+
} else if (typeof rec.serverUrl === "string") {
|
|
79175
|
+
out.push({ kind: "url", value: rec.serverUrl });
|
|
79176
|
+
}
|
|
79177
|
+
}
|
|
79178
|
+
return out;
|
|
79179
|
+
}
|
|
79180
|
+
function getServerCommandArray(config) {
|
|
79181
|
+
const t = config.type;
|
|
79182
|
+
if (t !== undefined && t !== "stdio") {
|
|
79183
|
+
return null;
|
|
79184
|
+
}
|
|
79185
|
+
const stdioConfig = config;
|
|
79186
|
+
return [stdioConfig.command, ...stdioConfig.args ?? []];
|
|
79187
|
+
}
|
|
79188
|
+
function commandArraysMatch(a, b) {
|
|
79189
|
+
if (a.length !== b.length) {
|
|
79190
|
+
return false;
|
|
79191
|
+
}
|
|
79192
|
+
return a.every((val, idx) => val === b[idx]);
|
|
79193
|
+
}
|
|
79194
|
+
function getServerUrl(config) {
|
|
79195
|
+
return "url" in config ? config.url : null;
|
|
79196
|
+
}
|
|
79197
|
+
function urlPatternToRegex(pattern) {
|
|
79198
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
79199
|
+
const regexStr = escaped.replace(/\*/g, ".*");
|
|
79200
|
+
return new RegExp(`^${regexStr}$`);
|
|
79201
|
+
}
|
|
79202
|
+
function urlMatchesPattern(url, pattern) {
|
|
79203
|
+
return urlPatternToRegex(pattern).test(url);
|
|
79204
|
+
}
|
|
79205
|
+
function isDeniedByEntries(serverName, config, entries) {
|
|
79206
|
+
for (const entry of entries) {
|
|
79207
|
+
if (entry.kind === "name" && entry.value === serverName)
|
|
79208
|
+
return true;
|
|
79209
|
+
}
|
|
79210
|
+
const serverCommand = getServerCommandArray(config);
|
|
79211
|
+
if (serverCommand) {
|
|
79212
|
+
for (const entry of entries) {
|
|
79213
|
+
if (entry.kind === "command" && commandArraysMatch(entry.value, serverCommand)) {
|
|
79214
|
+
return true;
|
|
79215
|
+
}
|
|
79216
|
+
}
|
|
79217
|
+
}
|
|
79218
|
+
const serverUrl = getServerUrl(config);
|
|
79219
|
+
if (serverUrl) {
|
|
79220
|
+
for (const entry of entries) {
|
|
79221
|
+
if (entry.kind === "url" && urlMatchesPattern(serverUrl, entry.value)) {
|
|
79222
|
+
return true;
|
|
79223
|
+
}
|
|
79224
|
+
}
|
|
79225
|
+
}
|
|
79226
|
+
return false;
|
|
79227
|
+
}
|
|
79228
|
+
function isAllowedByEntries(serverName, config, entries) {
|
|
79229
|
+
const hasCommandEntries = entries.some((e) => e.kind === "command");
|
|
79230
|
+
const hasUrlEntries = entries.some((e) => e.kind === "url");
|
|
79231
|
+
const serverCommand = getServerCommandArray(config);
|
|
79232
|
+
const serverUrl = getServerUrl(config);
|
|
79233
|
+
if (serverCommand) {
|
|
79234
|
+
if (hasCommandEntries) {
|
|
79235
|
+
return entries.some((e) => e.kind === "command" && commandArraysMatch(e.value, serverCommand));
|
|
79236
|
+
}
|
|
79237
|
+
return entries.some((e) => e.kind === "name" && e.value === serverName);
|
|
79238
|
+
}
|
|
79239
|
+
if (serverUrl) {
|
|
79240
|
+
if (hasUrlEntries) {
|
|
79241
|
+
return entries.some((e) => e.kind === "url" && urlMatchesPattern(serverUrl, e.value));
|
|
79242
|
+
}
|
|
79243
|
+
return entries.some((e) => e.kind === "name" && e.value === serverName);
|
|
79244
|
+
}
|
|
79245
|
+
return entries.some((e) => e.kind === "name" && e.value === serverName);
|
|
79246
|
+
}
|
|
79247
|
+
function checkMcpServerPolicy(serverName, config, policy) {
|
|
79248
|
+
const p = policy ?? getMcpPolicyInput() ?? {};
|
|
79249
|
+
const denied = normalizePolicyEntries(p.deniedMcpServers);
|
|
79250
|
+
if (isDeniedByEntries(serverName, config, denied)) {
|
|
79251
|
+
return { blocked: true, reason: "denied by policy (deniedMcpServers)" };
|
|
79252
|
+
}
|
|
79253
|
+
if (!Array.isArray(p.allowedMcpServers))
|
|
79254
|
+
return { blocked: false };
|
|
79255
|
+
if (p.allowedMcpServers.length === 0) {
|
|
79256
|
+
return {
|
|
79257
|
+
blocked: true,
|
|
79258
|
+
reason: "not in allowedMcpServers allowlist (empty allowlist blocks all servers)"
|
|
79259
|
+
};
|
|
79260
|
+
}
|
|
79261
|
+
const allowed = normalizePolicyEntries(p.allowedMcpServers);
|
|
79262
|
+
if (isAllowedByEntries(serverName, config, allowed)) {
|
|
79263
|
+
return { blocked: false };
|
|
79264
|
+
}
|
|
79265
|
+
return { blocked: true, reason: "not in allowedMcpServers allowlist" };
|
|
79266
|
+
}
|
|
79267
|
+
function filterMcpServersByPolicy(configs, policy) {
|
|
79268
|
+
const allowed = {};
|
|
79269
|
+
const blocked = [];
|
|
79270
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
79271
|
+
const verdict = checkMcpServerPolicy(name, config, policy);
|
|
79272
|
+
if (verdict.blocked) {
|
|
79273
|
+
blocked.push({ name, reason: verdict.reason ?? "blocked by policy" });
|
|
79274
|
+
} else {
|
|
79275
|
+
allowed[name] = config;
|
|
79276
|
+
}
|
|
79277
|
+
}
|
|
79278
|
+
return { allowed, blocked };
|
|
79279
|
+
}
|
|
79280
|
+
var policyInput = null;
|
|
79281
|
+
|
|
78812
79282
|
// src/mcp/index.ts
|
|
78813
79283
|
var init_mcp3 = __esm(() => {
|
|
78814
79284
|
init_types6();
|
|
@@ -78833,6 +79303,24 @@ function classifyToolError(error) {
|
|
|
78833
79303
|
return "UnknownError";
|
|
78834
79304
|
}
|
|
78835
79305
|
|
|
79306
|
+
// src/engine/pipeline/sanitizeUpdatedInput.ts
|
|
79307
|
+
function isPlainObject3(value) {
|
|
79308
|
+
if (typeof value !== "object" || value === null)
|
|
79309
|
+
return false;
|
|
79310
|
+
const proto = Object.getPrototypeOf(value);
|
|
79311
|
+
return proto === Object.prototype || proto === null;
|
|
79312
|
+
}
|
|
79313
|
+
function sanitizeUpdatedInput(originalInput, updatedInput) {
|
|
79314
|
+
if (!isPlainObject3(originalInput) || !isPlainObject3(updatedInput)) {
|
|
79315
|
+
return originalInput;
|
|
79316
|
+
}
|
|
79317
|
+
const result = {};
|
|
79318
|
+
for (const key of Object.keys(originalInput)) {
|
|
79319
|
+
result[key] = key in updatedInput ? updatedInput[key] : originalInput[key];
|
|
79320
|
+
}
|
|
79321
|
+
return result;
|
|
79322
|
+
}
|
|
79323
|
+
|
|
78836
79324
|
// src/engine/pipeline/schemaValidation.ts
|
|
78837
79325
|
function typeName(v) {
|
|
78838
79326
|
if (v === null)
|
|
@@ -78993,7 +79481,7 @@ async function runWithToolHeartbeat(opts, fn) {
|
|
|
78993
79481
|
var DEFAULT_TOOL_HEARTBEAT_INTERVAL_MS = 30000, MIN_TOOL_HEARTBEAT_INTERVAL_MS = 100;
|
|
78994
79482
|
|
|
78995
79483
|
// src/engine/pipeline/toolExecution.ts
|
|
78996
|
-
function mergeHookPermission(hookBehavior, gate, hookReason) {
|
|
79484
|
+
function mergeHookPermission(hookBehavior, gate, hookReason, hookSource) {
|
|
78997
79485
|
if (hookBehavior === "defer") {
|
|
78998
79486
|
return {
|
|
78999
79487
|
allowed: false,
|
|
@@ -79007,7 +79495,8 @@ function mergeHookPermission(hookBehavior, gate, hookReason) {
|
|
|
79007
79495
|
if (!gate.allowed)
|
|
79008
79496
|
return gate;
|
|
79009
79497
|
if (hookBehavior === "ask") {
|
|
79010
|
-
|
|
79498
|
+
const reason = hookSource && hookSource !== "settings" ? `hook requested confirmation (from ${hookSource})` : "hook requested confirmation";
|
|
79499
|
+
return { allowed: false, ask: true, reason };
|
|
79011
79500
|
}
|
|
79012
79501
|
return gate;
|
|
79013
79502
|
}
|
|
@@ -79078,12 +79567,12 @@ async function executeToolUse(tu, assistantMsg, deps) {
|
|
|
79078
79567
|
isError: true
|
|
79079
79568
|
};
|
|
79080
79569
|
}
|
|
79081
|
-
const effectiveInput = preOutcome?.updatedInput
|
|
79570
|
+
const effectiveInput = preOutcome?.updatedInput !== undefined ? sanitizeUpdatedInput(tu.input, preOutcome.updatedInput) : tu.input;
|
|
79082
79571
|
const gateVerdict = deps.checkPermission ? await deps.checkPermission(tool, effectiveInput, {
|
|
79083
79572
|
toolUseId: tu.id,
|
|
79084
79573
|
assistantMessage: assistantMsg
|
|
79085
79574
|
}) : { allowed: true };
|
|
79086
|
-
const verdict = mergeHookPermission(preOutcome?.hookBehavior, gateVerdict, preOutcome?.hookPermissionDecisionReason);
|
|
79575
|
+
const verdict = mergeHookPermission(preOutcome?.hookBehavior, gateVerdict, preOutcome?.hookPermissionDecisionReason, preOutcome?.hookSource);
|
|
79087
79576
|
if (verdict.deferred) {
|
|
79088
79577
|
return { deferred: true, deferReason: verdict.reason, isError: false };
|
|
79089
79578
|
}
|
|
@@ -79909,12 +80398,12 @@ var LEGACY_TOOL_NAME_ALIASES;
|
|
|
79909
80398
|
var init_legacyToolNameAliases = __esm(() => {
|
|
79910
80399
|
init_permissions3();
|
|
79911
80400
|
init_toolNames();
|
|
79912
|
-
LEGACY_TOOL_NAME_ALIASES = {
|
|
80401
|
+
LEGACY_TOOL_NAME_ALIASES = Object.assign(Object.create(null), {
|
|
79913
80402
|
Task: AGENT_TOOL_NAME,
|
|
79914
80403
|
KillShell: TASK_STOP_TOOL_NAME,
|
|
79915
80404
|
AgentOutputTool: TASK_OUTPUT_TOOL_NAME,
|
|
79916
80405
|
BashOutputTool: TASK_OUTPUT_TOOL_NAME
|
|
79917
|
-
};
|
|
80406
|
+
});
|
|
79918
80407
|
setLegacyToolNameAliases(LEGACY_TOOL_NAME_ALIASES);
|
|
79919
80408
|
});
|
|
79920
80409
|
|
|
@@ -82800,6 +83289,8 @@ async function queryAgentLoop(deps, args) {
|
|
|
82800
83289
|
let turns = 0;
|
|
82801
83290
|
let terminated = false;
|
|
82802
83291
|
let lastRound;
|
|
83292
|
+
let activeTools = tools;
|
|
83293
|
+
const degradedTools = new Set;
|
|
82803
83294
|
const maxGoalTurns = args.context?.maxGoalTurns ?? DEFAULT_MAX_GOAL_TURNS;
|
|
82804
83295
|
let goalTurns = 0;
|
|
82805
83296
|
let deferredToolUse;
|
|
@@ -82842,6 +83333,24 @@ async function queryAgentLoop(deps, args) {
|
|
|
82842
83333
|
deps.emit?.({ type: "compacted", turn: turns, messages });
|
|
82843
83334
|
}
|
|
82844
83335
|
}
|
|
83336
|
+
if (deps.refreshRoundContext) {
|
|
83337
|
+
try {
|
|
83338
|
+
const refresh = deps.refreshRoundContext(messages);
|
|
83339
|
+
if (refresh) {
|
|
83340
|
+
if (refresh.tools) {
|
|
83341
|
+
activeTools = degradedTools.size > 0 ? refresh.tools.filter((t) => !degradedTools.has(t.name)) : refresh.tools;
|
|
83342
|
+
}
|
|
83343
|
+
if (refresh.appendedMessages && refresh.appendedMessages.length > 0) {
|
|
83344
|
+
messages = [...messages, ...refresh.appendedMessages];
|
|
83345
|
+
if (deps.transcript) {
|
|
83346
|
+
await deps.transcript.record(messages);
|
|
83347
|
+
}
|
|
83348
|
+
}
|
|
83349
|
+
}
|
|
83350
|
+
} catch (refreshErr) {
|
|
83351
|
+
logForDebugging(`[round-context-refresh] failed: ${errorMessage(refreshErr)}`);
|
|
83352
|
+
}
|
|
83353
|
+
}
|
|
82845
83354
|
deps.emit?.({ type: "round_start", turn: turns });
|
|
82846
83355
|
let reactiveRetried = false;
|
|
82847
83356
|
let effectiveDeps = deps;
|
|
@@ -82849,7 +83358,7 @@ async function queryAgentLoop(deps, args) {
|
|
|
82849
83358
|
for (;; ) {
|
|
82850
83359
|
const roundInputLen = messages.length;
|
|
82851
83360
|
try {
|
|
82852
|
-
lastRound = await queryOneRound(effectiveDeps,
|
|
83361
|
+
lastRound = await queryOneRound(effectiveDeps, activeTools, messages);
|
|
82853
83362
|
} catch (err) {
|
|
82854
83363
|
if (!reactiveRetried && deps.reactiveCompact !== undefined && isReactiveCompactEnabled() && isReactiveCompactRecoverableError(err)) {
|
|
82855
83364
|
reactiveRetried = true;
|
|
@@ -82872,6 +83381,13 @@ async function queryAgentLoop(deps, args) {
|
|
|
82872
83381
|
continue;
|
|
82873
83382
|
}
|
|
82874
83383
|
}
|
|
83384
|
+
const rejectedTool = parseToolSchemaRejectionError(err);
|
|
83385
|
+
if (rejectedTool && !degradedTools.has(rejectedTool) && activeTools.some((t) => t.name === rejectedTool)) {
|
|
83386
|
+
degradedTools.add(rejectedTool);
|
|
83387
|
+
activeTools = activeTools.filter((t) => t.name !== rejectedTool);
|
|
83388
|
+
logForDebugging(`[tool-schema-strip] 网关拒绝工具 '${rejectedTool}' 的 input schema,本会话剥除其参数后续跑(tools ${activeTools.length} 个)`);
|
|
83389
|
+
continue;
|
|
83390
|
+
}
|
|
82875
83391
|
const nextRef = nextFallbackRef(deps.fallbackModels, triedFallbackRefs);
|
|
82876
83392
|
if (nextRef && shouldRetryModelError(err, deps.signal)) {
|
|
82877
83393
|
triedFallbackRefs.add(nextRef);
|
|
@@ -83058,6 +83574,7 @@ function createToolHooks(opts = {}) {
|
|
|
83058
83574
|
updatedInput: r.updatedInput,
|
|
83059
83575
|
hookBehavior: r.permissionBehavior,
|
|
83060
83576
|
hookPermissionDecisionReason: r.permissionDecisionReason,
|
|
83577
|
+
hookSource: r.permissionDecisionSource,
|
|
83061
83578
|
preventContinuation: r.preventContinuation,
|
|
83062
83579
|
stopReason: r.stopReason
|
|
83063
83580
|
};
|
|
@@ -83346,6 +83863,7 @@ var init_state2 = () => {};
|
|
|
83346
83863
|
var init_hooks2 = __esm(() => {
|
|
83347
83864
|
init_toolHooks();
|
|
83348
83865
|
init_hooks();
|
|
83866
|
+
init_hooks();
|
|
83349
83867
|
});
|
|
83350
83868
|
|
|
83351
83869
|
// src/engine/goal/goalCheckIn.ts
|
|
@@ -83434,6 +83952,7 @@ var init_engine = __esm(() => {
|
|
|
83434
83952
|
init_permissions4();
|
|
83435
83953
|
init_hooks2();
|
|
83436
83954
|
init_hooks2();
|
|
83955
|
+
init_hooks2();
|
|
83437
83956
|
init_config();
|
|
83438
83957
|
init_messaging();
|
|
83439
83958
|
init_messaging();
|
|
@@ -86861,6 +87380,19 @@ var init_hooksWiring = __esm(() => {
|
|
|
86861
87380
|
init_hooks();
|
|
86862
87381
|
});
|
|
86863
87382
|
|
|
87383
|
+
// src/cli/headlessSessionEnd.ts
|
|
87384
|
+
async function runHeadlessSessionEndHooks(reason) {
|
|
87385
|
+
try {
|
|
87386
|
+
await runSessionEndHooks(reason);
|
|
87387
|
+
} catch (error) {
|
|
87388
|
+
logForDebugging2(`SessionEnd hooks (headless exit, reason=${reason}) failed: ${String(error)}`, { level: "error" });
|
|
87389
|
+
}
|
|
87390
|
+
}
|
|
87391
|
+
var init_headlessSessionEnd = __esm(() => {
|
|
87392
|
+
init_hooks();
|
|
87393
|
+
init_debugSink();
|
|
87394
|
+
});
|
|
87395
|
+
|
|
86864
87396
|
// src/cli/print.ts
|
|
86865
87397
|
import { randomUUID as randomUUID21 } from "crypto";
|
|
86866
87398
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
@@ -86960,7 +87492,12 @@ async function connectMcpStartup(args) {
|
|
|
86960
87492
|
}) : {};
|
|
86961
87493
|
const dynamicNames = new Set(Object.keys(dynamicConfigs));
|
|
86962
87494
|
const allConfigs = strictMcpConfig ? dynamicConfigs : { ...baseConfigs, ...dynamicConfigs };
|
|
86963
|
-
const
|
|
87495
|
+
const policy = getMcpPolicyInput() ?? getMcpPolicyFromSettings(getSettingsWithErrors().settings);
|
|
87496
|
+
const { allowed, blocked } = filterMcpServersByPolicy(allConfigs, policy);
|
|
87497
|
+
for (const b of blocked) {
|
|
87498
|
+
manager.markBlocked(b.name, allConfigs[b.name], b.reason);
|
|
87499
|
+
}
|
|
87500
|
+
const connectAll = () => Promise.allSettled(Object.entries(allowed).map(([name, config]) => manager.connect(name, config, dynamicNames.has(name) ? { connectionTimeoutMs: 5000 } : undefined)));
|
|
86964
87501
|
const nonBlocking = isMcpConnectionNonBlocking();
|
|
86965
87502
|
if (nonBlocking) {
|
|
86966
87503
|
connectAll();
|
|
@@ -87615,6 +88152,7 @@ async function runHeadless(inputPrompt, options) {
|
|
|
87615
88152
|
cronScheduler.stop();
|
|
87616
88153
|
await drainToOutput();
|
|
87617
88154
|
await drainPendingBackgroundAgents(abortController.signal);
|
|
88155
|
+
await runHeadlessSessionEndHooks("prompt_input_exit");
|
|
87618
88156
|
} catch (error) {
|
|
87619
88157
|
cronScheduler.stop();
|
|
87620
88158
|
logForDebugging2(headlessErrorLine(error), { level: "error" });
|
|
@@ -87633,6 +88171,7 @@ async function runHeadless(inputPrompt, options) {
|
|
|
87633
88171
|
});
|
|
87634
88172
|
} catch {}
|
|
87635
88173
|
abortController.abort();
|
|
88174
|
+
await runHeadlessSessionEndHooks("other");
|
|
87636
88175
|
process.exitCode = 1;
|
|
87637
88176
|
}
|
|
87638
88177
|
await flushDebugSink();
|
|
@@ -87683,6 +88222,7 @@ var init_print = __esm(() => {
|
|
|
87683
88222
|
init_headlessPrompt();
|
|
87684
88223
|
init_debugSink();
|
|
87685
88224
|
init_hooksWiring();
|
|
88225
|
+
init_headlessSessionEnd();
|
|
87686
88226
|
});
|
|
87687
88227
|
|
|
87688
88228
|
// src/cli/handlers/update.ts
|
|
@@ -88219,6 +88759,10 @@ async function mapConcurrent(items, fn, concurrency) {
|
|
|
88219
88759
|
return results;
|
|
88220
88760
|
}
|
|
88221
88761
|
async function checkMcpServerHealth(manager, name, server) {
|
|
88762
|
+
const policy = getMcpPolicyInput() ?? getMcpPolicyFromSettings(getSettingsWithErrors().settings);
|
|
88763
|
+
if (checkMcpServerPolicy(name, server, policy).blocked) {
|
|
88764
|
+
return "✗ Blocked by enterprise policy";
|
|
88765
|
+
}
|
|
88222
88766
|
try {
|
|
88223
88767
|
const result = await manager.connect(name, server);
|
|
88224
88768
|
if (result.type === "connected") {
|
|
@@ -88514,6 +89058,7 @@ Warning: The command "${actualCommand}" looks like a URL, but is being interpret
|
|
|
88514
89058
|
}
|
|
88515
89059
|
var init_mcp4 = __esm(() => {
|
|
88516
89060
|
init_mcp3();
|
|
89061
|
+
init_engine();
|
|
88517
89062
|
init_entryInit();
|
|
88518
89063
|
init_mcpConfigWrite();
|
|
88519
89064
|
init_setup();
|
|
@@ -89066,7 +89611,7 @@ var init_cli = __esm(() => {
|
|
|
89066
89611
|
});
|
|
89067
89612
|
|
|
89068
89613
|
// node_modules/is-plain-obj/index.js
|
|
89069
|
-
function
|
|
89614
|
+
function isPlainObject4(value) {
|
|
89070
89615
|
if (typeof value !== "object" || value === null) {
|
|
89071
89616
|
return false;
|
|
89072
89617
|
}
|
|
@@ -89088,7 +89633,7 @@ var init_file_url = () => {};
|
|
|
89088
89633
|
// node_modules/execa/lib/methods/parameters.js
|
|
89089
89634
|
var normalizeParameters = (rawFile, rawArguments = [], rawOptions = {}) => {
|
|
89090
89635
|
const filePath = safeNormalizeFileUrl(rawFile, "First argument");
|
|
89091
|
-
const [commandArguments, options] =
|
|
89636
|
+
const [commandArguments, options] = isPlainObject4(rawArguments) ? [[], rawArguments] : [rawArguments, rawOptions];
|
|
89092
89637
|
if (!Array.isArray(commandArguments)) {
|
|
89093
89638
|
throw new TypeError(`Second argument must be either an array of arguments or an options object: ${commandArguments}`);
|
|
89094
89639
|
}
|
|
@@ -89100,7 +89645,7 @@ var normalizeParameters = (rawFile, rawArguments = [], rawOptions = {}) => {
|
|
|
89100
89645
|
if (nullByteArgument !== undefined) {
|
|
89101
89646
|
throw new TypeError(`Arguments cannot contain null bytes ("\\0"): ${nullByteArgument}`);
|
|
89102
89647
|
}
|
|
89103
|
-
if (!
|
|
89648
|
+
if (!isPlainObject4(options)) {
|
|
89104
89649
|
throw new TypeError(`Last argument must be an options object: ${options}`);
|
|
89105
89650
|
}
|
|
89106
89651
|
return [filePath, normalizedArguments, { __proto__: null, ...options }];
|
|
@@ -89222,7 +89767,7 @@ var isTemplateString = (templates) => Array.isArray(templates) && Array.isArray(
|
|
|
89222
89767
|
if (typeOfExpression === "number") {
|
|
89223
89768
|
return String(expression);
|
|
89224
89769
|
}
|
|
89225
|
-
if (
|
|
89770
|
+
if (isPlainObject4(expression) && (("stdout" in expression) || ("isMaxBuffer" in expression))) {
|
|
89226
89771
|
return getSubprocessResult(expression);
|
|
89227
89772
|
}
|
|
89228
89773
|
if (expression instanceof ChildProcess || Object.prototype.toString.call(expression) === "[object Promise]") {
|
|
@@ -89269,7 +89814,7 @@ var normalizeFdSpecificOptions = (options) => {
|
|
|
89269
89814
|
const optionBaseArray = Array.from({ length: stdioLength + 1 });
|
|
89270
89815
|
const optionArray = normalizeFdSpecificValue(options[optionName], optionBaseArray, optionName, stdioLength);
|
|
89271
89816
|
return addDefaultValue(optionArray, optionName);
|
|
89272
|
-
}, getStdioLength = ({ stdio }) => Array.isArray(stdio) ? Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length) : STANDARD_STREAMS_ALIASES.length, normalizeFdSpecificValue = (optionValue, optionArray, optionName, stdioLength) =>
|
|
89817
|
+
}, getStdioLength = ({ stdio }) => Array.isArray(stdio) ? Math.max(stdio.length, STANDARD_STREAMS_ALIASES.length) : STANDARD_STREAMS_ALIASES.length, normalizeFdSpecificValue = (optionValue, optionArray, optionName, stdioLength) => isPlainObject4(optionValue) ? normalizeOptionObject(optionValue, optionArray, optionName, stdioLength) : optionArray.fill(optionValue), normalizeOptionObject = (optionValue, optionArray, optionName, stdioLength) => {
|
|
89273
89818
|
for (const fdName of Object.keys(optionValue).sort(compareFdName)) {
|
|
89274
89819
|
for (const fdNumber of parseFdName(fdName, optionName, stdioLength)) {
|
|
89275
89820
|
optionArray[fdNumber] = optionValue[fdName];
|
|
@@ -92516,7 +93061,7 @@ var getStdioItemType = (value, optionName) => {
|
|
|
92516
93061
|
if (value !== undefined && typeof value !== "boolean") {
|
|
92517
93062
|
throw new TypeError(`The \`${optionName}\` option must use a boolean.`);
|
|
92518
93063
|
}
|
|
92519
|
-
}, isGenerator = (value) => isAsyncGenerator(value) || isSyncGenerator(value), isAsyncGenerator = (value) => Object.prototype.toString.call(value) === "[object AsyncGeneratorFunction]", isSyncGenerator = (value) => Object.prototype.toString.call(value) === "[object GeneratorFunction]", isTransformOptions = (value) =>
|
|
93064
|
+
}, isGenerator = (value) => isAsyncGenerator(value) || isSyncGenerator(value), isAsyncGenerator = (value) => Object.prototype.toString.call(value) === "[object AsyncGeneratorFunction]", isSyncGenerator = (value) => Object.prototype.toString.call(value) === "[object GeneratorFunction]", isTransformOptions = (value) => isPlainObject4(value) && (value.transform !== undefined || value.final !== undefined), isUrl = (value) => Object.prototype.toString.call(value) === "[object URL]", isRegularUrl = (value) => isUrl(value) && value.protocol !== "file:", isFilePathObject = (value) => isPlainObject4(value) && Object.keys(value).length > 0 && Object.keys(value).every((key) => FILE_PATH_KEYS.has(key)) && isFilePathString(value.file), FILE_PATH_KEYS, isFilePathString = (file) => typeof file === "string", isStdioValueObject = (value) => isPlainObject4(value) && Object.keys(value).length > 0 && Object.keys(value).every((key) => STDIO_VALUE_KEYS.has(key)) && ("value" in value), STDIO_VALUE_KEYS, isUnknownStdioString = (type, value) => type === "native" && typeof value === "string" && !KNOWN_STDIO_STRINGS.has(value), KNOWN_STDIO_STRINGS, isReadableStream3 = (value) => Object.prototype.toString.call(value) === "[object ReadableStream]", isWritableStream2 = (value) => Object.prototype.toString.call(value) === "[object WritableStream]", isWebStream = (value) => isReadableStream3(value) || isWritableStream2(value), isTransformStream = (value) => isReadableStream3(value?.readable) && isWritableStream2(value?.writable), isAsyncIterableObject = (value) => isObject2(value) && typeof value[Symbol.asyncIterator] === "function", isIterableObject = (value) => isObject2(value) && typeof value[Symbol.iterator] === "function", isObject2 = (value) => typeof value === "object" && value !== null, TRANSFORM_TYPES, FILE_TYPES, SPECIAL_DUPLICATE_TYPES_SYNC, SPECIAL_DUPLICATE_TYPES, FORBID_DUPLICATE_TYPES, TYPE_TO_MESSAGE;
|
|
92520
93065
|
var init_type = __esm(() => {
|
|
92521
93066
|
init_uint_array();
|
|
92522
93067
|
FILE_PATH_KEYS = new Set(["file", "append"]);
|
|
@@ -92618,7 +93163,7 @@ var normalizeTransforms = (stdioItems, optionName, direction, options) => [
|
|
|
92618
93163
|
value: { transform, writableObjectMode, readableObjectMode }
|
|
92619
93164
|
};
|
|
92620
93165
|
}, normalizeTransformStream = ({ stdioItem, stdioItem: { value }, index, newTransforms, direction }) => {
|
|
92621
|
-
const { transform, objectMode } =
|
|
93166
|
+
const { transform, objectMode } = isPlainObject4(value) ? value : { transform: value };
|
|
92622
93167
|
const { writableObjectMode, readableObjectMode } = getTransformObjectModes(objectMode, index, newTransforms, direction);
|
|
92623
93168
|
return {
|
|
92624
93169
|
...stdioItem,
|
|
@@ -92631,7 +93176,7 @@ var normalizeTransforms = (stdioItems, optionName, direction, options) => [
|
|
|
92631
93176
|
binary: binaryOption = false,
|
|
92632
93177
|
preserveNewlines = false,
|
|
92633
93178
|
objectMode
|
|
92634
|
-
} =
|
|
93179
|
+
} = isPlainObject4(value) ? value : { transform: value };
|
|
92635
93180
|
const binary = binaryOption || BINARY_ENCODINGS.has(encoding);
|
|
92636
93181
|
const { writableObjectMode, readableObjectMode } = getTransformObjectModes(objectMode, index, newTransforms, direction);
|
|
92637
93182
|
return {
|
|
@@ -95326,7 +95871,7 @@ var init_abort2 = __esm(() => {
|
|
|
95326
95871
|
|
|
95327
95872
|
// node_modules/execa/lib/pipe/setup.js
|
|
95328
95873
|
var pipeToSubprocess = (sourceInfo, ...pipeArguments) => {
|
|
95329
|
-
if (
|
|
95874
|
+
if (isPlainObject4(pipeArguments[0])) {
|
|
95330
95875
|
return pipeToSubprocess.bind(undefined, {
|
|
95331
95876
|
...sourceInfo,
|
|
95332
95877
|
boundOptions: { ...sourceInfo.boundOptions, ...pipeArguments[0] }
|
|
@@ -96235,7 +96780,7 @@ var mergeOptions = (boundOptions, options) => {
|
|
|
96235
96780
|
]));
|
|
96236
96781
|
return { ...safeBoundOptions, ...mergedOptions };
|
|
96237
96782
|
}, mergeOption = (optionName, boundOptionValue, optionValue) => {
|
|
96238
|
-
if (DEEP_OPTIONS.has(optionName) &&
|
|
96783
|
+
if (DEEP_OPTIONS.has(optionName) && isPlainObject4(boundOptionValue) && isPlainObject4(optionValue)) {
|
|
96239
96784
|
return { ...boundOptionValue, ...optionValue };
|
|
96240
96785
|
}
|
|
96241
96786
|
return optionValue;
|
|
@@ -96260,7 +96805,7 @@ var createExeca = (mapArguments, boundOptions, deepOptions, setBoundExeca) => {
|
|
|
96260
96805
|
}
|
|
96261
96806
|
return boundExeca;
|
|
96262
96807
|
}, callBoundExeca = ({ mapArguments, deepOptions = {}, boundOptions = {}, setBoundExeca, createNested }, firstArgument, ...nextArguments) => {
|
|
96263
|
-
if (
|
|
96808
|
+
if (isPlainObject4(firstArgument)) {
|
|
96264
96809
|
return createNested(mapArguments, mergeOptions(boundOptions, firstArgument), setBoundExeca);
|
|
96265
96810
|
}
|
|
96266
96811
|
const { file, commandArguments, options, isSync } = parseArguments2({
|
|
@@ -98324,7 +98869,7 @@ var init_isArrayLikeObject = __esm(() => {
|
|
|
98324
98869
|
});
|
|
98325
98870
|
|
|
98326
98871
|
// node_modules/lodash-es/isPlainObject.js
|
|
98327
|
-
function
|
|
98872
|
+
function isPlainObject5(value) {
|
|
98328
98873
|
if (!isObjectLike_default(value) || _baseGetTag_default(value) != objectTag4) {
|
|
98329
98874
|
return false;
|
|
98330
98875
|
}
|
|
@@ -98345,7 +98890,7 @@ var init_isPlainObject = __esm(() => {
|
|
|
98345
98890
|
funcToString3 = funcProto3.toString;
|
|
98346
98891
|
hasOwnProperty11 = objectProto14.hasOwnProperty;
|
|
98347
98892
|
objectCtorString = funcToString3.call(Object);
|
|
98348
|
-
isPlainObject_default =
|
|
98893
|
+
isPlainObject_default = isPlainObject5;
|
|
98349
98894
|
});
|
|
98350
98895
|
|
|
98351
98896
|
// node_modules/lodash-es/_safeGet.js
|
|
@@ -102476,9 +103021,9 @@ function merge2(...objs) {
|
|
|
102476
103021
|
}
|
|
102477
103022
|
const targetKey = caseless && typeof key === "string" && findKey(result, key) || key;
|
|
102478
103023
|
const existing = hasOwnProperty14(result, targetKey) ? result[targetKey] : undefined;
|
|
102479
|
-
if (
|
|
103024
|
+
if (isPlainObject6(existing) && isPlainObject6(val)) {
|
|
102480
103025
|
result[targetKey] = merge2(existing, val);
|
|
102481
|
-
} else if (
|
|
103026
|
+
} else if (isPlainObject6(val)) {
|
|
102482
103027
|
result[targetKey] = merge2({}, val);
|
|
102483
103028
|
} else if (isArray3(val)) {
|
|
102484
103029
|
result[targetKey] = val.slice();
|
|
@@ -102581,7 +103126,7 @@ var toString3, getPrototypeOf, iterator, toStringTag, hasOwnProperty14, isUnsafe
|
|
|
102581
103126
|
}, kindOf, kindOfTest = (type) => {
|
|
102582
103127
|
type = type.toLowerCase();
|
|
102583
103128
|
return (thing) => kindOf(thing) === type;
|
|
102584
|
-
}, typeOfTest = (type) => (thing) => typeof thing === type, isArray3, isUndefined, isArrayBuffer3, isString, isFunction2, isNumber, isObject4 = (thing) => thing !== null && typeof thing === "object", isBoolean = (thing) => thing === true || thing === false,
|
|
103129
|
+
}, typeOfTest = (type) => (thing) => typeof thing === type, isArray3, isUndefined, isArrayBuffer3, isString, isFunction2, isNumber, isObject4 = (thing) => thing !== null && typeof thing === "object", isBoolean = (thing) => thing === true || thing === false, isPlainObject6 = (val) => {
|
|
102585
103130
|
if (!isObject4(val)) {
|
|
102586
103131
|
return false;
|
|
102587
103132
|
}
|
|
@@ -102857,7 +103402,7 @@ var init_utils3 = __esm(() => {
|
|
|
102857
103402
|
isNumber,
|
|
102858
103403
|
isBoolean,
|
|
102859
103404
|
isObject: isObject4,
|
|
102860
|
-
isPlainObject:
|
|
103405
|
+
isPlainObject: isPlainObject6,
|
|
102861
103406
|
isEmptyObject,
|
|
102862
103407
|
isReadableStream: isReadableStream4,
|
|
102863
103408
|
isRequest,
|
|
@@ -124455,8 +125000,8 @@ var isMcpServerCommandEntry = () => ({}), isMcpServerNameEntry = () => ({}), isM
|
|
|
124455
125000
|
mcpServers: record(string2(), any()).optional(),
|
|
124456
125001
|
allowedMcpServers: any().optional(),
|
|
124457
125002
|
deniedMcpServers: any().optional(),
|
|
124458
|
-
enabledMcpjsonServers: array(string2()).optional(),
|
|
124459
|
-
disabledMcpjsonServers: array(string2()).optional(),
|
|
125003
|
+
enabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
|
|
125004
|
+
disabledMcpjsonServers: union([string2(), array(string2())]).transform((v) => Array.isArray(v) ? v : [v]).optional(),
|
|
124460
125005
|
enabledPlugins: record(string2(), any()).optional(),
|
|
124461
125006
|
pluginConfigs: any().optional(),
|
|
124462
125007
|
extraKnownMarketplaces: any().optional(),
|
|
@@ -124797,12 +125342,12 @@ function findLastUnescapedChar2(str, char) {
|
|
|
124797
125342
|
var LEGACY_TOOL_NAME_ALIASES2;
|
|
124798
125343
|
var init_permissionRuleParser2 = __esm(() => {
|
|
124799
125344
|
init_constants6();
|
|
124800
|
-
LEGACY_TOOL_NAME_ALIASES2 = {
|
|
125345
|
+
LEGACY_TOOL_NAME_ALIASES2 = Object.assign(Object.create(null), {
|
|
124801
125346
|
Task: AGENT_TOOL_NAME2,
|
|
124802
125347
|
KillShell: TASK_STOP_TOOL_NAME2,
|
|
124803
125348
|
AgentOutputTool: TASK_OUTPUT_TOOL_NAME2,
|
|
124804
125349
|
BashOutputTool: TASK_OUTPUT_TOOL_NAME2
|
|
124805
|
-
};
|
|
125350
|
+
});
|
|
124806
125351
|
});
|
|
124807
125352
|
|
|
124808
125353
|
// src/tui/utils/settings/toolValidationConfig.ts
|
|
@@ -180895,6 +181440,7 @@ __export(exports_engineCompat, {
|
|
|
180895
181440
|
MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES: () => MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES,
|
|
180896
181441
|
MAX_GOAL_CHECKINS: () => MAX_GOAL_CHECKINS,
|
|
180897
181442
|
MAX_GOAL_CONDITION_CHARS: () => MAX_GOAL_CONDITION_CHARS,
|
|
181443
|
+
MAX_HOOK_CONCURRENCY: () => MAX_HOOK_CONCURRENCY,
|
|
180898
181444
|
MAX_PTL_RETRIES: () => MAX_PTL_RETRIES,
|
|
180899
181445
|
MAX_TRANSCRIPT_READ_BYTES: () => MAX_TRANSCRIPT_READ_BYTES,
|
|
180900
181446
|
MAX_WORKER_SPAWN_DEPTH: () => MAX_WORKER_SPAWN_DEPTH,
|
|
@@ -182181,11 +182727,12 @@ function parseArgumentNames2(argumentNames) {
|
|
|
182181
182727
|
return [];
|
|
182182
182728
|
}
|
|
182183
182729
|
const isValidName = (name) => typeof name === "string" && name.trim() !== "" && !/^\d+$/.test(name);
|
|
182730
|
+
const cap = (names) => names.map((name) => name.slice(0, MAX_ARGUMENT_NAME_CHARS2)).slice(0, MAX_ARGUMENT_NAMES2);
|
|
182184
182731
|
if (Array.isArray(argumentNames)) {
|
|
182185
|
-
return argumentNames.filter(isValidName);
|
|
182732
|
+
return cap(argumentNames.filter(isValidName));
|
|
182186
182733
|
}
|
|
182187
182734
|
if (typeof argumentNames === "string") {
|
|
182188
|
-
return argumentNames.split(/\s+/).filter(isValidName);
|
|
182735
|
+
return cap(argumentNames.split(/\s+/).filter(isValidName));
|
|
182189
182736
|
}
|
|
182190
182737
|
return [];
|
|
182191
182738
|
}
|
|
@@ -182195,36 +182742,144 @@ function generateProgressiveArgumentHint2(argNames, typedArgs) {
|
|
|
182195
182742
|
return;
|
|
182196
182743
|
return remaining.map((name) => `[${name}]`).join(" ");
|
|
182197
182744
|
}
|
|
182198
|
-
function
|
|
182745
|
+
function isWordChar3(c) {
|
|
182746
|
+
return c !== undefined && /[A-Za-z0-9_]/.test(c);
|
|
182747
|
+
}
|
|
182748
|
+
function matchPlaceholderAt2(content, i, argumentNames) {
|
|
182749
|
+
if (content[i] !== "$")
|
|
182750
|
+
return null;
|
|
182751
|
+
for (let n = 0;n < argumentNames.length; n++) {
|
|
182752
|
+
const name = argumentNames[n];
|
|
182753
|
+
if (!name)
|
|
182754
|
+
continue;
|
|
182755
|
+
if (content.startsWith(`$${name}`, i)) {
|
|
182756
|
+
const after = content[i + 1 + name.length];
|
|
182757
|
+
if (after !== "[" && !isWordChar3(after)) {
|
|
182758
|
+
return { length: 1 + name.length, kind: "named", nameIndex: n };
|
|
182759
|
+
}
|
|
182760
|
+
}
|
|
182761
|
+
}
|
|
182762
|
+
INDEXED_PLACEHOLDER_RE2.lastIndex = i;
|
|
182763
|
+
const indexed = INDEXED_PLACEHOLDER_RE2.exec(content);
|
|
182764
|
+
if (indexed) {
|
|
182765
|
+
return {
|
|
182766
|
+
length: indexed[0].length,
|
|
182767
|
+
kind: "indexed",
|
|
182768
|
+
argIndex: parseInt(indexed[1], 10)
|
|
182769
|
+
};
|
|
182770
|
+
}
|
|
182771
|
+
if (content.startsWith("$ARGUMENTS", i)) {
|
|
182772
|
+
return { length: "$ARGUMENTS".length, kind: "bare" };
|
|
182773
|
+
}
|
|
182774
|
+
SHORTHAND_PLACEHOLDER_RE2.lastIndex = i;
|
|
182775
|
+
const shorthand = SHORTHAND_PLACEHOLDER_RE2.exec(content);
|
|
182776
|
+
if (shorthand && !isWordChar3(content[i + shorthand[0].length])) {
|
|
182777
|
+
return {
|
|
182778
|
+
length: shorthand[0].length,
|
|
182779
|
+
kind: "shorthand",
|
|
182780
|
+
argIndex: parseInt(shorthand[1], 10)
|
|
182781
|
+
};
|
|
182782
|
+
}
|
|
182783
|
+
return null;
|
|
182784
|
+
}
|
|
182785
|
+
function countPlaceholderOccurrences2(content, argumentNames) {
|
|
182786
|
+
let count = 0;
|
|
182787
|
+
let i = 0;
|
|
182788
|
+
while (i < content.length) {
|
|
182789
|
+
const m = matchPlaceholderAt2(content, i, argumentNames);
|
|
182790
|
+
if (m) {
|
|
182791
|
+
count++;
|
|
182792
|
+
i += m.length;
|
|
182793
|
+
} else {
|
|
182794
|
+
i++;
|
|
182795
|
+
}
|
|
182796
|
+
}
|
|
182797
|
+
return count;
|
|
182798
|
+
}
|
|
182799
|
+
function substituteArgumentsImpl2(content, args, appendIfNoPlaceholder, argumentNames, strict) {
|
|
182199
182800
|
if (args === undefined || args === null) {
|
|
182200
182801
|
return content;
|
|
182201
182802
|
}
|
|
182202
|
-
const
|
|
182203
|
-
|
|
182204
|
-
|
|
182205
|
-
|
|
182206
|
-
|
|
182207
|
-
|
|
182208
|
-
|
|
182803
|
+
const placeholderCount = countPlaceholderOccurrences2(content, argumentNames);
|
|
182804
|
+
let effectiveArgs = args;
|
|
182805
|
+
if (placeholderCount > 0 && content.length + placeholderCount * args.length > MAX_SUBSTITUTION_OUTPUT_CHARS2) {
|
|
182806
|
+
if (strict)
|
|
182807
|
+
return null;
|
|
182808
|
+
const budgetPerReplacement = Math.max(0, Math.floor((MAX_SUBSTITUTION_OUTPUT_CHARS2 - content.length) / placeholderCount));
|
|
182809
|
+
effectiveArgs = args.slice(0, budgetPerReplacement);
|
|
182810
|
+
} else if (strict) {
|
|
182811
|
+
const appendLen = appendIfNoPlaceholder && args ? `
|
|
182812
|
+
|
|
182813
|
+
ARGUMENTS: `.length + args.length : 0;
|
|
182814
|
+
if (content.length + appendLen > MAX_SUBSTITUTION_OUTPUT_CHARS2) {
|
|
182815
|
+
return null;
|
|
182816
|
+
}
|
|
182209
182817
|
}
|
|
182210
|
-
|
|
182211
|
-
|
|
182212
|
-
|
|
182213
|
-
|
|
182214
|
-
|
|
182215
|
-
|
|
182216
|
-
|
|
182217
|
-
}
|
|
182218
|
-
|
|
182219
|
-
|
|
182220
|
-
|
|
182818
|
+
const parsedArgs = parseArguments3(effectiveArgs);
|
|
182819
|
+
let out = "";
|
|
182820
|
+
const appendValue = (value) => {
|
|
182821
|
+
if (out.length + value.length > MAX_SUBSTITUTION_OUTPUT_CHARS2) {
|
|
182822
|
+
value = value.slice(0, Math.max(0, MAX_SUBSTITUTION_OUTPUT_CHARS2 - out.length));
|
|
182823
|
+
}
|
|
182824
|
+
out += value;
|
|
182825
|
+
};
|
|
182826
|
+
let matched = false;
|
|
182827
|
+
let i = 0;
|
|
182828
|
+
while (i < content.length) {
|
|
182829
|
+
const m = matchPlaceholderAt2(content, i, argumentNames);
|
|
182830
|
+
if (m) {
|
|
182831
|
+
matched = true;
|
|
182832
|
+
if (m.kind === "bare")
|
|
182833
|
+
appendValue(effectiveArgs);
|
|
182834
|
+
else if (m.kind === "named")
|
|
182835
|
+
appendValue(parsedArgs[m.nameIndex] ?? "");
|
|
182836
|
+
else
|
|
182837
|
+
appendValue(parsedArgs[m.argIndex] ?? "");
|
|
182838
|
+
i += m.length;
|
|
182839
|
+
} else {
|
|
182840
|
+
out += content[i];
|
|
182841
|
+
i++;
|
|
182842
|
+
}
|
|
182843
|
+
}
|
|
182844
|
+
if (!matched && appendIfNoPlaceholder && args) {
|
|
182845
|
+
out = out + `
|
|
182221
182846
|
|
|
182222
182847
|
ARGUMENTS: ${args}`;
|
|
182223
182848
|
}
|
|
182224
|
-
return
|
|
182849
|
+
return out;
|
|
182225
182850
|
}
|
|
182851
|
+
function substituteArguments2(content, args, appendIfNoPlaceholder = true, argumentNames = []) {
|
|
182852
|
+
return substituteArgumentsImpl2(content, args, appendIfNoPlaceholder, argumentNames, false) ?? content;
|
|
182853
|
+
}
|
|
182854
|
+
function substituteArgumentsStrict2(content, args, appendIfNoPlaceholder = true, argumentNames = []) {
|
|
182855
|
+
return substituteArgumentsImpl2(content, args, appendIfNoPlaceholder, argumentNames, true);
|
|
182856
|
+
}
|
|
182857
|
+
var MAX_ARGUMENT_NAMES2 = 64, MAX_ARGUMENT_NAME_CHARS2 = 64, MAX_SUBSTITUTION_OUTPUT_CHARS2 = 1e6, INDEXED_PLACEHOLDER_RE2, SHORTHAND_PLACEHOLDER_RE2;
|
|
182226
182858
|
var init_argumentSubstitution2 = __esm(() => {
|
|
182227
182859
|
init_shellQuote2();
|
|
182860
|
+
INDEXED_PLACEHOLDER_RE2 = /\$ARGUMENTS\[(\d+)\]/y;
|
|
182861
|
+
SHORTHAND_PLACEHOLDER_RE2 = /\$(\d+)/y;
|
|
182862
|
+
});
|
|
182863
|
+
|
|
182864
|
+
// src/tui/utils/commandNameSafety.ts
|
|
182865
|
+
function isUnsafeCommandName2(name) {
|
|
182866
|
+
if (name === "")
|
|
182867
|
+
return true;
|
|
182868
|
+
if (name.includes("<") || name.includes(">"))
|
|
182869
|
+
return true;
|
|
182870
|
+
return UNSAFE_NAME_CHARS_RE2.test(name);
|
|
182871
|
+
}
|
|
182872
|
+
function sanitizeDisplayName2(displayName, canonicalName) {
|
|
182873
|
+
if (displayName === undefined || !isUnsafeCommandName2(displayName)) {
|
|
182874
|
+
return displayName;
|
|
182875
|
+
}
|
|
182876
|
+
logForDebugging3(`Discarding unsafe frontmatter display name ${JSON.stringify(displayName)} for ${canonicalName}; falling back to canonical name`);
|
|
182877
|
+
return;
|
|
182878
|
+
}
|
|
182879
|
+
var UNSAFE_NAME_CHARS_RE2;
|
|
182880
|
+
var init_commandNameSafety2 = __esm(() => {
|
|
182881
|
+
init_debug();
|
|
182882
|
+
UNSAFE_NAME_CHARS_RE2 = /[\u0000-\u001F\u007F-\u00A0\u00AD\u034F\u061C\u070F\u1680\u180E\u2000-\u200F\u2028\u2029\u202A-\u202E\u202F\u205F\u2060-\u2069\u3000\uFE00-\uFE0F\uFEFF\u{E0100}-\u{E01EF}]/u;
|
|
182228
182883
|
});
|
|
182229
182884
|
|
|
182230
182885
|
// src/tui/utils/generatedFiles.ts
|
|
@@ -184657,7 +185312,7 @@ class Protocol {
|
|
|
184657
185312
|
};
|
|
184658
185313
|
}
|
|
184659
185314
|
}
|
|
184660
|
-
function
|
|
185315
|
+
function isPlainObject7(value) {
|
|
184661
185316
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
184662
185317
|
}
|
|
184663
185318
|
function mergeCapabilities(base, additional) {
|
|
@@ -184668,7 +185323,7 @@ function mergeCapabilities(base, additional) {
|
|
|
184668
185323
|
if (addValue === undefined)
|
|
184669
185324
|
continue;
|
|
184670
185325
|
const baseValue = result[k];
|
|
184671
|
-
if (
|
|
185326
|
+
if (isPlainObject7(baseValue) && isPlainObject7(addValue)) {
|
|
184672
185327
|
result[k] = { ...baseValue, ...addValue };
|
|
184673
185328
|
} else {
|
|
184674
185329
|
result[k] = addValue;
|
|
@@ -188182,6 +188837,97 @@ var init_streamableHttp = __esm(() => {
|
|
|
188182
188837
|
};
|
|
188183
188838
|
});
|
|
188184
188839
|
|
|
188840
|
+
// src/tui/services/mcp/fetchTimeout.ts
|
|
188841
|
+
function getMcpConnectionTimeoutMs() {
|
|
188842
|
+
return parseInt(process.env.MCP_TIMEOUT || "", 10) || 30000;
|
|
188843
|
+
}
|
|
188844
|
+
function bindBodyReadDeadline(response) {
|
|
188845
|
+
const contentType = response.headers?.get("content-type") ?? "";
|
|
188846
|
+
if (contentType.includes("text/event-stream")) {
|
|
188847
|
+
return response;
|
|
188848
|
+
}
|
|
188849
|
+
const rawBody = response.body;
|
|
188850
|
+
if (!rawBody) {
|
|
188851
|
+
return response;
|
|
188852
|
+
}
|
|
188853
|
+
const reader = rawBody.getReader();
|
|
188854
|
+
let deadlineTimer;
|
|
188855
|
+
const deadline = new Promise((_, reject) => {
|
|
188856
|
+
deadlineTimer = setTimeout(() => reject(new DOMException("The operation timed out.", "TimeoutError")), getMcpConnectionTimeoutMs());
|
|
188857
|
+
deadlineTimer.unref?.();
|
|
188858
|
+
});
|
|
188859
|
+
deadline.catch(() => {});
|
|
188860
|
+
const clearDeadline = () => {
|
|
188861
|
+
if (deadlineTimer) {
|
|
188862
|
+
clearTimeout(deadlineTimer);
|
|
188863
|
+
}
|
|
188864
|
+
};
|
|
188865
|
+
const stream = new ReadableStream({
|
|
188866
|
+
async pull(ctrl) {
|
|
188867
|
+
try {
|
|
188868
|
+
const { done, value } = await Promise.race([reader.read(), deadline]);
|
|
188869
|
+
if (done) {
|
|
188870
|
+
clearDeadline();
|
|
188871
|
+
ctrl.close();
|
|
188872
|
+
} else {
|
|
188873
|
+
ctrl.enqueue(value);
|
|
188874
|
+
}
|
|
188875
|
+
} catch (error) {
|
|
188876
|
+
clearDeadline();
|
|
188877
|
+
reader.cancel().catch(() => {});
|
|
188878
|
+
ctrl.error(error);
|
|
188879
|
+
}
|
|
188880
|
+
},
|
|
188881
|
+
cancel(reason) {
|
|
188882
|
+
clearDeadline();
|
|
188883
|
+
return reader.cancel(reason);
|
|
188884
|
+
}
|
|
188885
|
+
});
|
|
188886
|
+
return new Response(stream, {
|
|
188887
|
+
status: response.status,
|
|
188888
|
+
statusText: response.statusText,
|
|
188889
|
+
headers: response.headers
|
|
188890
|
+
});
|
|
188891
|
+
}
|
|
188892
|
+
function wrapFetchWithTimeout(baseFetch) {
|
|
188893
|
+
return async (url, init) => {
|
|
188894
|
+
const method = (init?.method ?? "GET").toUpperCase();
|
|
188895
|
+
if (method === "GET") {
|
|
188896
|
+
return baseFetch(url, init);
|
|
188897
|
+
}
|
|
188898
|
+
const headers = new Headers(init?.headers);
|
|
188899
|
+
if (!headers.has("accept")) {
|
|
188900
|
+
headers.set("accept", MCP_STREAMABLE_HTTP_ACCEPT);
|
|
188901
|
+
}
|
|
188902
|
+
const controller = new AbortController;
|
|
188903
|
+
const timer = setTimeout((c) => c.abort(new DOMException("The operation timed out.", "TimeoutError")), MCP_REQUEST_TIMEOUT_MS, controller);
|
|
188904
|
+
timer.unref?.();
|
|
188905
|
+
const parentSignal = init?.signal;
|
|
188906
|
+
const abort = () => controller.abort(parentSignal?.reason);
|
|
188907
|
+
parentSignal?.addEventListener("abort", abort);
|
|
188908
|
+
if (parentSignal?.aborted) {
|
|
188909
|
+
controller.abort(parentSignal.reason);
|
|
188910
|
+
}
|
|
188911
|
+
const cleanup = () => {
|
|
188912
|
+
clearTimeout(timer);
|
|
188913
|
+
parentSignal?.removeEventListener("abort", abort);
|
|
188914
|
+
};
|
|
188915
|
+
try {
|
|
188916
|
+
const response = await baseFetch(url, {
|
|
188917
|
+
...init,
|
|
188918
|
+
headers,
|
|
188919
|
+
signal: controller.signal
|
|
188920
|
+
});
|
|
188921
|
+
cleanup();
|
|
188922
|
+
return bindBodyReadDeadline(response);
|
|
188923
|
+
} catch (error) {
|
|
188924
|
+
cleanup();
|
|
188925
|
+
throw error;
|
|
188926
|
+
}
|
|
188927
|
+
};
|
|
188928
|
+
}
|
|
188929
|
+
var MCP_REQUEST_TIMEOUT_MS = 60000, MCP_STREAMABLE_HTTP_ACCEPT = "application/json, text/event-stream";
|
|
188930
|
+
|
|
188185
188931
|
// node_modules/lodash-es/_baseZipObject.js
|
|
188186
188932
|
function baseZipObject(props, values, assignFunc) {
|
|
188187
188933
|
var index = -1, length = props.length, valsLength = values.length, result = {};
|
|
@@ -193521,6 +194267,73 @@ var init_browser = __esm(() => {
|
|
|
193521
194267
|
init_execFileNoThrow2();
|
|
193522
194268
|
});
|
|
193523
194269
|
|
|
194270
|
+
// src/tui/services/mcp/headersHelper.ts
|
|
194271
|
+
function isMcpServerFromProjectOrLocalSettings(config) {
|
|
194272
|
+
return config.scope === "project" || config.scope === "local";
|
|
194273
|
+
}
|
|
194274
|
+
async function getMcpHeadersFromHelper(serverName, config) {
|
|
194275
|
+
if (!config.headersHelper) {
|
|
194276
|
+
return null;
|
|
194277
|
+
}
|
|
194278
|
+
if ("scope" in config && isMcpServerFromProjectOrLocalSettings(config)) {
|
|
194279
|
+
const isNonInteractive = getIsNonInteractiveSession();
|
|
194280
|
+
const explicitTrust = process.env.ATLAS_TRUST_WORKSPACE === "true";
|
|
194281
|
+
const hasTrust = checkHasTrustDialogAccepted() || isNonInteractive && explicitTrust;
|
|
194282
|
+
if (!hasTrust) {
|
|
194283
|
+
const error = new Error(`Security: headersHelper for MCP server '${serverName}' executed before workspace trust is confirmed. If you see this message, post in ${MACRO.FEEDBACK_CHANNEL}.`);
|
|
194284
|
+
logAntError("MCP headersHelper invoked before trust check", error);
|
|
194285
|
+
return null;
|
|
194286
|
+
}
|
|
194287
|
+
}
|
|
194288
|
+
try {
|
|
194289
|
+
logMCPDebug(serverName, "Executing headersHelper to get dynamic headers");
|
|
194290
|
+
const execResult = await execFileNoThrowWithCwd2("sh", ["-c", config.headersHelper], {
|
|
194291
|
+
timeout: 1e4,
|
|
194292
|
+
env: {
|
|
194293
|
+
...process.env,
|
|
194294
|
+
ATLAS_MCP_SERVER_NAME: serverName,
|
|
194295
|
+
ATLAS_MCP_SERVER_URL: config.url
|
|
194296
|
+
}
|
|
194297
|
+
});
|
|
194298
|
+
if (execResult.code !== 0 || !execResult.stdout) {
|
|
194299
|
+
throw new Error(`headersHelper for MCP server '${serverName}' did not return a valid value`);
|
|
194300
|
+
}
|
|
194301
|
+
const result = execResult.stdout.trim();
|
|
194302
|
+
const headers = jsonParse2(result);
|
|
194303
|
+
if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
|
|
194304
|
+
throw new Error(`headersHelper for MCP server '${serverName}' must return a JSON object with string key-value pairs`);
|
|
194305
|
+
}
|
|
194306
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
194307
|
+
if (typeof value !== "string") {
|
|
194308
|
+
throw new Error(`headersHelper for MCP server '${serverName}' returned non-string value for key "${key}": ${typeof value}`);
|
|
194309
|
+
}
|
|
194310
|
+
}
|
|
194311
|
+
logMCPDebug(serverName, `Successfully retrieved ${Object.keys(headers).length} headers from headersHelper`);
|
|
194312
|
+
return headers;
|
|
194313
|
+
} catch (error) {
|
|
194314
|
+
logMCPError(serverName, `Error getting headers from headersHelper: ${errorMessage2(error)}`);
|
|
194315
|
+
logError3(new Error(`Error getting MCP headers from headersHelper for server '${serverName}': ${errorMessage2(error)}`));
|
|
194316
|
+
return null;
|
|
194317
|
+
}
|
|
194318
|
+
}
|
|
194319
|
+
async function getMcpServerHeaders(serverName, config) {
|
|
194320
|
+
const staticHeaders = config.headers || {};
|
|
194321
|
+
const dynamicHeaders = await getMcpHeadersFromHelper(serverName, config) || {};
|
|
194322
|
+
return {
|
|
194323
|
+
...staticHeaders,
|
|
194324
|
+
...dynamicHeaders
|
|
194325
|
+
};
|
|
194326
|
+
}
|
|
194327
|
+
var init_headersHelper = __esm(() => {
|
|
194328
|
+
init_bootstrap();
|
|
194329
|
+
init_config6();
|
|
194330
|
+
init_debug();
|
|
194331
|
+
init_errors4();
|
|
194332
|
+
init_execFileNoThrow2();
|
|
194333
|
+
init_log5();
|
|
194334
|
+
init_slowOperations();
|
|
194335
|
+
});
|
|
194336
|
+
|
|
193524
194337
|
// src/tui/services/mcp/oauthPort.ts
|
|
193525
194338
|
import { createServer } from "http";
|
|
193526
194339
|
function buildRedirectUri(port = REDIRECT_PORT_FALLBACK) {
|
|
@@ -194028,20 +194841,20 @@ async function writeMcpjsonFile2(config) {
|
|
|
194028
194841
|
throw e;
|
|
194029
194842
|
}
|
|
194030
194843
|
}
|
|
194031
|
-
function
|
|
194844
|
+
function getServerCommandArray2(config) {
|
|
194032
194845
|
if (config.type !== undefined && config.type !== "stdio") {
|
|
194033
194846
|
return null;
|
|
194034
194847
|
}
|
|
194035
194848
|
const stdioConfig = config;
|
|
194036
194849
|
return [stdioConfig.command, ...stdioConfig.args ?? []];
|
|
194037
194850
|
}
|
|
194038
|
-
function
|
|
194851
|
+
function commandArraysMatch2(a, b) {
|
|
194039
194852
|
if (a.length !== b.length) {
|
|
194040
194853
|
return false;
|
|
194041
194854
|
}
|
|
194042
194855
|
return a.every((val, idx) => val === b[idx]);
|
|
194043
194856
|
}
|
|
194044
|
-
function
|
|
194857
|
+
function getServerUrl2(config) {
|
|
194045
194858
|
return "url" in config ? config.url : null;
|
|
194046
194859
|
}
|
|
194047
194860
|
function unwrapCcrProxyUrl(url) {
|
|
@@ -194057,11 +194870,11 @@ function unwrapCcrProxyUrl(url) {
|
|
|
194057
194870
|
}
|
|
194058
194871
|
}
|
|
194059
194872
|
function getMcpServerSignature(config) {
|
|
194060
|
-
const cmd =
|
|
194873
|
+
const cmd = getServerCommandArray2(config);
|
|
194061
194874
|
if (cmd) {
|
|
194062
194875
|
return `stdio:${jsonStringify5(cmd)}`;
|
|
194063
194876
|
}
|
|
194064
|
-
const url =
|
|
194877
|
+
const url = getServerUrl2(config);
|
|
194065
194878
|
if (url) {
|
|
194066
194879
|
return `url:${unwrapCcrProxyUrl(url)}`;
|
|
194067
194880
|
}
|
|
@@ -194123,13 +194936,13 @@ function dedupManagedMcpServers(claudeAiServers, manualServers) {
|
|
|
194123
194936
|
}
|
|
194124
194937
|
return { servers, suppressed };
|
|
194125
194938
|
}
|
|
194126
|
-
function
|
|
194939
|
+
function urlPatternToRegex2(pattern) {
|
|
194127
194940
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
194128
194941
|
const regexStr = escaped.replace(/\*/g, ".*");
|
|
194129
194942
|
return new RegExp(`^${regexStr}$`);
|
|
194130
194943
|
}
|
|
194131
|
-
function
|
|
194132
|
-
const regex =
|
|
194944
|
+
function urlMatchesPattern2(url, pattern) {
|
|
194945
|
+
const regex = urlPatternToRegex2(pattern);
|
|
194133
194946
|
return regex.test(url);
|
|
194134
194947
|
}
|
|
194135
194948
|
function getMcpAllowlistSettings() {
|
|
@@ -194152,18 +194965,18 @@ function isMcpServerDenied(serverName, config) {
|
|
|
194152
194965
|
}
|
|
194153
194966
|
}
|
|
194154
194967
|
if (config) {
|
|
194155
|
-
const serverCommand =
|
|
194968
|
+
const serverCommand = getServerCommandArray2(config);
|
|
194156
194969
|
if (serverCommand) {
|
|
194157
194970
|
for (const entry of settings.deniedMcpServers) {
|
|
194158
|
-
if (isMcpServerCommandEntry(entry) &&
|
|
194971
|
+
if (isMcpServerCommandEntry(entry) && commandArraysMatch2(entry.serverCommand, serverCommand)) {
|
|
194159
194972
|
return true;
|
|
194160
194973
|
}
|
|
194161
194974
|
}
|
|
194162
194975
|
}
|
|
194163
|
-
const serverUrl =
|
|
194976
|
+
const serverUrl = getServerUrl2(config);
|
|
194164
194977
|
if (serverUrl) {
|
|
194165
194978
|
for (const entry of settings.deniedMcpServers) {
|
|
194166
|
-
if (isMcpServerUrlEntry(entry) &&
|
|
194979
|
+
if (isMcpServerUrlEntry(entry) && urlMatchesPattern2(serverUrl, entry.serverUrl)) {
|
|
194167
194980
|
return true;
|
|
194168
194981
|
}
|
|
194169
194982
|
}
|
|
@@ -194185,12 +194998,12 @@ function isMcpServerAllowedByPolicy(serverName, config) {
|
|
|
194185
194998
|
const hasCommandEntries = settings.allowedMcpServers.some(isMcpServerCommandEntry);
|
|
194186
194999
|
const hasUrlEntries = settings.allowedMcpServers.some(isMcpServerUrlEntry);
|
|
194187
195000
|
if (config) {
|
|
194188
|
-
const serverCommand =
|
|
194189
|
-
const serverUrl =
|
|
195001
|
+
const serverCommand = getServerCommandArray2(config);
|
|
195002
|
+
const serverUrl = getServerUrl2(config);
|
|
194190
195003
|
if (serverCommand) {
|
|
194191
195004
|
if (hasCommandEntries) {
|
|
194192
195005
|
for (const entry of settings.allowedMcpServers) {
|
|
194193
|
-
if (isMcpServerCommandEntry(entry) &&
|
|
195006
|
+
if (isMcpServerCommandEntry(entry) && commandArraysMatch2(entry.serverCommand, serverCommand)) {
|
|
194194
195007
|
return true;
|
|
194195
195008
|
}
|
|
194196
195009
|
}
|
|
@@ -194206,7 +195019,7 @@ function isMcpServerAllowedByPolicy(serverName, config) {
|
|
|
194206
195019
|
} else if (serverUrl) {
|
|
194207
195020
|
if (hasUrlEntries) {
|
|
194208
195021
|
for (const entry of settings.allowedMcpServers) {
|
|
194209
|
-
if (isMcpServerUrlEntry(entry) &&
|
|
195022
|
+
if (isMcpServerUrlEntry(entry) && urlMatchesPattern2(serverUrl, entry.serverUrl)) {
|
|
194210
195023
|
return true;
|
|
194211
195024
|
}
|
|
194212
195025
|
}
|
|
@@ -194235,7 +195048,7 @@ function isMcpServerAllowedByPolicy(serverName, config) {
|
|
|
194235
195048
|
}
|
|
194236
195049
|
return false;
|
|
194237
195050
|
}
|
|
194238
|
-
function
|
|
195051
|
+
function filterMcpServersByPolicy2(configs) {
|
|
194239
195052
|
const allowed = {};
|
|
194240
195053
|
const blocked = [];
|
|
194241
195054
|
for (const [name, config] of Object.entries(configs)) {
|
|
@@ -194687,7 +195500,7 @@ async function getAllMcpConfigs2() {
|
|
|
194687
195500
|
}
|
|
194688
195501
|
const claudeaiPromise = Promise.resolve({});
|
|
194689
195502
|
const { servers: claudeCodeServers, errors } = await getMcpConfigs({}, claudeaiPromise);
|
|
194690
|
-
const { allowed: claudeaiMcpServers } =
|
|
195503
|
+
const { allowed: claudeaiMcpServers } = filterMcpServersByPolicy2(await claudeaiPromise);
|
|
194691
195504
|
const { servers: dedupedClaudeAi } = dedupManagedMcpServers(claudeaiMcpServers, claudeCodeServers);
|
|
194692
195505
|
const servers = Object.assign({}, dedupedClaudeAi, claudeCodeServers);
|
|
194693
195506
|
return { servers, errors };
|
|
@@ -194792,6 +195605,9 @@ function parseMcpConfigFromFilePath(params) {
|
|
|
194792
195605
|
]
|
|
194793
195606
|
};
|
|
194794
195607
|
}
|
|
195608
|
+
if (!configContent.trim()) {
|
|
195609
|
+
return { config: { mcpServers: {} }, errors: [] };
|
|
195610
|
+
}
|
|
194795
195611
|
const parsedJson = safeParseJSON3(configContent);
|
|
194796
195612
|
if (!parsedJson) {
|
|
194797
195613
|
logForDebugging3(`MCP config is not valid JSON: ${filePath} (scope=${scope}, length=${configContent.length}, first100=${jsonStringify5(configContent.slice(0, 100))})`, { level: "error" });
|
|
@@ -194811,6 +195627,9 @@ function parseMcpConfigFromFilePath(params) {
|
|
|
194811
195627
|
]
|
|
194812
195628
|
};
|
|
194813
195629
|
}
|
|
195630
|
+
if (typeof parsedJson === "object" && parsedJson !== null && !Array.isArray(parsedJson) && Object.keys(parsedJson).length === 0) {
|
|
195631
|
+
return { config: { mcpServers: {} }, errors: [] };
|
|
195632
|
+
}
|
|
194814
195633
|
return parseMcpConfig({
|
|
194815
195634
|
configObject: parsedJson,
|
|
194816
195635
|
expandVars,
|
|
@@ -194941,11 +195760,11 @@ function hashMcpConfig(config) {
|
|
|
194941
195760
|
});
|
|
194942
195761
|
return createHash4("sha256").update(stable).digest("hex").slice(0, 16);
|
|
194943
195762
|
}
|
|
194944
|
-
function excludeStalePluginClients(mcp, configs) {
|
|
195763
|
+
function excludeStalePluginClients(mcp, configs, opts) {
|
|
194945
195764
|
const stale = mcp.clients.filter((c) => {
|
|
194946
195765
|
const fresh = configs[c.name];
|
|
194947
195766
|
if (!fresh)
|
|
194948
|
-
return c.config.scope === "dynamic";
|
|
195767
|
+
return opts?.absentIsStale || c.config.scope === "dynamic";
|
|
194949
195768
|
return hashMcpConfig(c.config) !== hashMcpConfig(fresh);
|
|
194950
195769
|
});
|
|
194951
195770
|
if (stale.length === 0) {
|
|
@@ -195741,6 +196560,53 @@ function createAuthFetch() {
|
|
|
195741
196560
|
}
|
|
195742
196561
|
};
|
|
195743
196562
|
}
|
|
196563
|
+
async function resolveConfiguredMcpHeaders(serverName, serverConfig) {
|
|
196564
|
+
try {
|
|
196565
|
+
return await getMcpServerHeaders(serverName, serverConfig);
|
|
196566
|
+
} catch (error) {
|
|
196567
|
+
logMCPDebug(serverName, `Failed to resolve configured headers for OAuth: ${errorMessage2(error)}`);
|
|
196568
|
+
return {};
|
|
196569
|
+
}
|
|
196570
|
+
}
|
|
196571
|
+
function headersToRecord2(headers) {
|
|
196572
|
+
const out = {};
|
|
196573
|
+
if (!headers)
|
|
196574
|
+
return out;
|
|
196575
|
+
if (headers instanceof Headers) {
|
|
196576
|
+
headers.forEach((v, k) => {
|
|
196577
|
+
out[k] = v;
|
|
196578
|
+
});
|
|
196579
|
+
return out;
|
|
196580
|
+
}
|
|
196581
|
+
if (Array.isArray(headers)) {
|
|
196582
|
+
for (const [k, v] of headers)
|
|
196583
|
+
out[k] = v;
|
|
196584
|
+
return out;
|
|
196585
|
+
}
|
|
196586
|
+
if (typeof headers === "object") {
|
|
196587
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
196588
|
+
out[k] = String(v);
|
|
196589
|
+
}
|
|
196590
|
+
}
|
|
196591
|
+
return out;
|
|
196592
|
+
}
|
|
196593
|
+
function mergeConfiguredHeaders(fetchLike, configured) {
|
|
196594
|
+
if (Object.keys(configured).length === 0)
|
|
196595
|
+
return fetchLike;
|
|
196596
|
+
return (url, init) => {
|
|
196597
|
+
const requestHeaders = headersToRecord2(init?.headers);
|
|
196598
|
+
const requestKeys = new Set(Object.keys(requestHeaders).map((k) => k.toLowerCase()));
|
|
196599
|
+
const configuredNotOverridden = Object.fromEntries(Object.entries(configured).filter(([k]) => !requestKeys.has(k.toLowerCase())));
|
|
196600
|
+
return fetchLike(url, {
|
|
196601
|
+
...init,
|
|
196602
|
+
headers: { ...configuredNotOverridden, ...requestHeaders }
|
|
196603
|
+
});
|
|
196604
|
+
};
|
|
196605
|
+
}
|
|
196606
|
+
async function withConfiguredHeaders(fetchLike, serverName, serverConfig) {
|
|
196607
|
+
const configured = await resolveConfiguredMcpHeaders(serverName, serverConfig);
|
|
196608
|
+
return mergeConfiguredHeaders(fetchLike, configured);
|
|
196609
|
+
}
|
|
195744
196610
|
async function fetchAuthServerMetadata(serverName, serverUrl, configuredMetadataUrl, fetchFn, resourceMetadataUrl) {
|
|
195745
196611
|
if (configuredMetadataUrl) {
|
|
195746
196612
|
if (!configuredMetadataUrl.startsWith("https://")) {
|
|
@@ -195799,12 +196665,14 @@ async function revokeToken({
|
|
|
195799
196665
|
clientId,
|
|
195800
196666
|
clientSecret,
|
|
195801
196667
|
accessToken,
|
|
195802
|
-
authMethod = "client_secret_basic"
|
|
196668
|
+
authMethod = "client_secret_basic",
|
|
196669
|
+
extraHeaders
|
|
195803
196670
|
}) {
|
|
195804
196671
|
const params = new URLSearchParams;
|
|
195805
196672
|
params.set("token", token);
|
|
195806
196673
|
params.set("token_type_hint", tokenTypeHint);
|
|
195807
196674
|
const headers = {
|
|
196675
|
+
...extraHeaders ?? {},
|
|
195808
196676
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
195809
196677
|
};
|
|
195810
196678
|
if (clientId && clientSecret) {
|
|
@@ -195847,7 +196715,8 @@ async function revokeServerTokens(serverName, serverConfig, { preserveStepUpStat
|
|
|
195847
196715
|
if (tokenData?.accessToken || tokenData?.refreshToken) {
|
|
195848
196716
|
try {
|
|
195849
196717
|
const asUrl = tokenData.discoveryState?.authorizationServerUrl ?? serverConfig.url;
|
|
195850
|
-
const
|
|
196718
|
+
const configuredHeaders = await resolveConfiguredMcpHeaders(serverName, serverConfig);
|
|
196719
|
+
const metadata = await fetchAuthServerMetadata(serverName, asUrl, serverConfig.oauth?.authServerMetadataUrl, mergeConfiguredHeaders(createAuthFetch(), configuredHeaders));
|
|
195851
196720
|
if (!metadata) {
|
|
195852
196721
|
logMCPDebug(serverName, "No OAuth metadata found");
|
|
195853
196722
|
} else {
|
|
@@ -195869,7 +196738,8 @@ async function revokeServerTokens(serverName, serverConfig, { preserveStepUpStat
|
|
|
195869
196738
|
clientId: tokenData.clientId,
|
|
195870
196739
|
clientSecret: tokenData.clientSecret,
|
|
195871
196740
|
accessToken: tokenData.accessToken,
|
|
195872
|
-
authMethod
|
|
196741
|
+
authMethod,
|
|
196742
|
+
extraHeaders: configuredHeaders
|
|
195873
196743
|
});
|
|
195874
196744
|
} catch (error) {
|
|
195875
196745
|
logMCPDebug(serverName, `Failed to revoke refresh token: ${errorMessage2(error)}`);
|
|
@@ -195885,7 +196755,8 @@ async function revokeServerTokens(serverName, serverConfig, { preserveStepUpStat
|
|
|
195885
196755
|
clientId: tokenData.clientId,
|
|
195886
196756
|
clientSecret: tokenData.clientSecret,
|
|
195887
196757
|
accessToken: tokenData.accessToken,
|
|
195888
|
-
authMethod
|
|
196758
|
+
authMethod,
|
|
196759
|
+
extraHeaders: configuredHeaders
|
|
195889
196760
|
});
|
|
195890
196761
|
} catch (error) {
|
|
195891
196762
|
logMCPDebug(serverName, `Failed to revoke access token: ${errorMessage2(error)}`);
|
|
@@ -196078,8 +196949,9 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
|
|
|
196078
196949
|
const redirectUri = buildRedirectUri(port);
|
|
196079
196950
|
logMCPDebug(serverName, `Using redirect port: ${port}${configuredCallbackPort ? " (from config)" : ""}`);
|
|
196080
196951
|
const provider = new ClaudeAuthProvider(serverName, serverConfig, redirectUri, true, onAuthorizationUrl, options?.skipBrowserOpen);
|
|
196952
|
+
const oauthFetch = await withConfiguredHeaders(createAuthFetch(), serverName, serverConfig);
|
|
196081
196953
|
try {
|
|
196082
|
-
const metadata = await fetchAuthServerMetadata(serverName, serverConfig.url, serverConfig.oauth?.authServerMetadataUrl,
|
|
196954
|
+
const metadata = await fetchAuthServerMetadata(serverName, serverConfig.url, serverConfig.oauth?.authServerMetadataUrl, oauthFetch, wwwAuthParams.resourceMetadataUrl);
|
|
196083
196955
|
if (metadata) {
|
|
196084
196956
|
provider.setMetadata(metadata);
|
|
196085
196957
|
logMCPDebug(serverName, `Fetched OAuth metadata with scope: ${getScopeFromMetadata(metadata) || "NONE"}`);
|
|
@@ -196215,7 +197087,8 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
|
|
|
196215
197087
|
const result = await auth(provider, {
|
|
196216
197088
|
serverUrl: serverConfig.url,
|
|
196217
197089
|
scope: wwwAuthParams.scope,
|
|
196218
|
-
resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl
|
|
197090
|
+
resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl,
|
|
197091
|
+
fetchFn: oauthFetch
|
|
196219
197092
|
});
|
|
196220
197093
|
logMCPDebug(serverName, `Initial auth result: ${result}`);
|
|
196221
197094
|
if (result !== "REDIRECT") {
|
|
@@ -196239,7 +197112,8 @@ async function performMCPOAuthFlow(serverName, serverConfig, onAuthorizationUrl,
|
|
|
196239
197112
|
const result = await auth(provider, {
|
|
196240
197113
|
serverUrl: serverConfig.url,
|
|
196241
197114
|
authorizationCode,
|
|
196242
|
-
resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl
|
|
197115
|
+
resourceMetadataUrl: wwwAuthParams.resourceMetadataUrl,
|
|
197116
|
+
fetchFn: oauthFetch
|
|
196243
197117
|
});
|
|
196244
197118
|
logMCPDebug(serverName, `Auth result: ${result}`);
|
|
196245
197119
|
if (result === "AUTHORIZED") {
|
|
@@ -196746,7 +197620,7 @@ class ClaudeAuthProvider {
|
|
|
196746
197620
|
if (metadataUrl) {
|
|
196747
197621
|
logMCPDebug(this.serverName, `Fetching metadata from configured URL: ${metadataUrl}`);
|
|
196748
197622
|
try {
|
|
196749
|
-
const metadata = await fetchAuthServerMetadata(this.serverName, this.serverConfig.url, metadataUrl);
|
|
197623
|
+
const metadata = await fetchAuthServerMetadata(this.serverName, this.serverConfig.url, metadataUrl, await withConfiguredHeaders(createAuthFetch(), this.serverName, this.serverConfig));
|
|
196750
197624
|
if (metadata) {
|
|
196751
197625
|
return {
|
|
196752
197626
|
authorizationServerUrl: metadata.issuer,
|
|
@@ -196831,7 +197705,7 @@ class ClaudeAuthProvider {
|
|
|
196831
197705
|
for (let attempt = 1;attempt <= MAX_ATTEMPTS; attempt++) {
|
|
196832
197706
|
try {
|
|
196833
197707
|
logMCPDebug(this.serverName, `Starting token refresh`);
|
|
196834
|
-
const authFetch = createAuthFetch();
|
|
197708
|
+
const authFetch = await withConfiguredHeaders(createAuthFetch(), this.serverName, this.serverConfig);
|
|
196835
197709
|
let metadata = this._metadata;
|
|
196836
197710
|
if (!metadata) {
|
|
196837
197711
|
const cached = await this.discoveryState();
|
|
@@ -197007,6 +197881,7 @@ var init_auth3 = __esm(() => {
|
|
|
197007
197881
|
init_secureStorage();
|
|
197008
197882
|
init_macOsKeychainHelpers();
|
|
197009
197883
|
init_slowOperations();
|
|
197884
|
+
init_headersHelper();
|
|
197010
197885
|
init_oauthPort();
|
|
197011
197886
|
init_utils6();
|
|
197012
197887
|
init_xaa();
|
|
@@ -201002,10 +201877,10 @@ function parseProxyResponse(socket) {
|
|
|
201002
201877
|
read();
|
|
201003
201878
|
});
|
|
201004
201879
|
}
|
|
201005
|
-
var
|
|
201880
|
+
var import_debug54, debug;
|
|
201006
201881
|
var init_parse_proxy_response = __esm(() => {
|
|
201007
|
-
|
|
201008
|
-
debug =
|
|
201882
|
+
import_debug54 = __toESM(require_src(), 1);
|
|
201883
|
+
debug = import_debug54.default("https-proxy-agent:parse-proxy-response");
|
|
201009
201884
|
});
|
|
201010
201885
|
|
|
201011
201886
|
// node_modules/proxy-agent-negotiate/dist/index.js
|
|
@@ -201058,7 +201933,7 @@ function omit2(obj, ...keys) {
|
|
|
201058
201933
|
}
|
|
201059
201934
|
return ret;
|
|
201060
201935
|
}
|
|
201061
|
-
var
|
|
201936
|
+
var import_debug55, debug2, setServernameFromNonIpHost = (options) => {
|
|
201062
201937
|
if (options.servername === undefined && options.host && !net2.isIP(options.host)) {
|
|
201063
201938
|
return {
|
|
201064
201939
|
...options,
|
|
@@ -201070,8 +201945,8 @@ var import_debug53, debug2, setServernameFromNonIpHost = (options) => {
|
|
|
201070
201945
|
var init_dist7 = __esm(() => {
|
|
201071
201946
|
init_dist6();
|
|
201072
201947
|
init_parse_proxy_response();
|
|
201073
|
-
|
|
201074
|
-
debug2 =
|
|
201948
|
+
import_debug55 = __toESM(require_src(), 1);
|
|
201949
|
+
debug2 = import_debug55.default("https-proxy-agent");
|
|
201075
201950
|
HttpsProxyAgent2 = class HttpsProxyAgent2 extends Agent2 {
|
|
201076
201951
|
constructor(proxy, opts) {
|
|
201077
201952
|
super(opts);
|
|
@@ -202132,73 +203007,6 @@ var init_classifyForCollapse = __esm(() => {
|
|
|
202132
203007
|
"resources_list"
|
|
202133
203008
|
]);
|
|
202134
203009
|
});
|
|
202135
|
-
|
|
202136
|
-
// src/tui/services/mcp/headersHelper.ts
|
|
202137
|
-
function isMcpServerFromProjectOrLocalSettings(config) {
|
|
202138
|
-
return config.scope === "project" || config.scope === "local";
|
|
202139
|
-
}
|
|
202140
|
-
async function getMcpHeadersFromHelper(serverName, config) {
|
|
202141
|
-
if (!config.headersHelper) {
|
|
202142
|
-
return null;
|
|
202143
|
-
}
|
|
202144
|
-
if ("scope" in config && isMcpServerFromProjectOrLocalSettings(config)) {
|
|
202145
|
-
const isNonInteractive = getIsNonInteractiveSession();
|
|
202146
|
-
const explicitTrust = process.env.ATLAS_TRUST_WORKSPACE === "true";
|
|
202147
|
-
const hasTrust = checkHasTrustDialogAccepted() || isNonInteractive && explicitTrust;
|
|
202148
|
-
if (!hasTrust) {
|
|
202149
|
-
const error = new Error(`Security: headersHelper for MCP server '${serverName}' executed before workspace trust is confirmed. If you see this message, post in ${MACRO.FEEDBACK_CHANNEL}.`);
|
|
202150
|
-
logAntError("MCP headersHelper invoked before trust check", error);
|
|
202151
|
-
return null;
|
|
202152
|
-
}
|
|
202153
|
-
}
|
|
202154
|
-
try {
|
|
202155
|
-
logMCPDebug(serverName, "Executing headersHelper to get dynamic headers");
|
|
202156
|
-
const execResult = await execFileNoThrowWithCwd2("sh", ["-c", config.headersHelper], {
|
|
202157
|
-
timeout: 1e4,
|
|
202158
|
-
env: {
|
|
202159
|
-
...process.env,
|
|
202160
|
-
ATLAS_MCP_SERVER_NAME: serverName,
|
|
202161
|
-
ATLAS_MCP_SERVER_URL: config.url
|
|
202162
|
-
}
|
|
202163
|
-
});
|
|
202164
|
-
if (execResult.code !== 0 || !execResult.stdout) {
|
|
202165
|
-
throw new Error(`headersHelper for MCP server '${serverName}' did not return a valid value`);
|
|
202166
|
-
}
|
|
202167
|
-
const result = execResult.stdout.trim();
|
|
202168
|
-
const headers = jsonParse2(result);
|
|
202169
|
-
if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
|
|
202170
|
-
throw new Error(`headersHelper for MCP server '${serverName}' must return a JSON object with string key-value pairs`);
|
|
202171
|
-
}
|
|
202172
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
202173
|
-
if (typeof value !== "string") {
|
|
202174
|
-
throw new Error(`headersHelper for MCP server '${serverName}' returned non-string value for key "${key}": ${typeof value}`);
|
|
202175
|
-
}
|
|
202176
|
-
}
|
|
202177
|
-
logMCPDebug(serverName, `Successfully retrieved ${Object.keys(headers).length} headers from headersHelper`);
|
|
202178
|
-
return headers;
|
|
202179
|
-
} catch (error) {
|
|
202180
|
-
logMCPError(serverName, `Error getting headers from headersHelper: ${errorMessage2(error)}`);
|
|
202181
|
-
logError3(new Error(`Error getting MCP headers from headersHelper for server '${serverName}': ${errorMessage2(error)}`));
|
|
202182
|
-
return null;
|
|
202183
|
-
}
|
|
202184
|
-
}
|
|
202185
|
-
async function getMcpServerHeaders(serverName, config) {
|
|
202186
|
-
const staticHeaders = config.headers || {};
|
|
202187
|
-
const dynamicHeaders = await getMcpHeadersFromHelper(serverName, config) || {};
|
|
202188
|
-
return {
|
|
202189
|
-
...staticHeaders,
|
|
202190
|
-
...dynamicHeaders
|
|
202191
|
-
};
|
|
202192
|
-
}
|
|
202193
|
-
var init_headersHelper = __esm(() => {
|
|
202194
|
-
init_bootstrap();
|
|
202195
|
-
init_config6();
|
|
202196
|
-
init_debug();
|
|
202197
|
-
init_errors4();
|
|
202198
|
-
init_execFileNoThrow2();
|
|
202199
|
-
init_log5();
|
|
202200
|
-
init_slowOperations();
|
|
202201
|
-
});
|
|
202202
203010
|
// src/tui/skills/mcpSkills.ts
|
|
202203
203011
|
var exports_mcpSkills = {};
|
|
202204
203012
|
__export(exports_mcpSkills, {
|
|
@@ -209645,6 +210453,11 @@ function isMcpSessionExpiredError(error) {
|
|
|
209645
210453
|
}
|
|
209646
210454
|
return error.message.includes('"code":-32001') || error.message.includes('"code": -32001');
|
|
209647
210455
|
}
|
|
210456
|
+
function truncateMcpDescriptionText(text) {
|
|
210457
|
+
if (!text)
|
|
210458
|
+
return text;
|
|
210459
|
+
return text.length > MAX_MCP_DESCRIPTION_LENGTH ? text.slice(0, MAX_MCP_DESCRIPTION_LENGTH) + "… [truncated]" : text;
|
|
210460
|
+
}
|
|
209648
210461
|
function getMcpToolTimeoutMs() {
|
|
209649
210462
|
return parseInt(process.env.MCP_TOOL_TIMEOUT || "", 10) || DEFAULT_MCP_TOOL_TIMEOUT_MS;
|
|
209650
210463
|
}
|
|
@@ -209693,46 +210506,6 @@ async function createNodeWsClient(url, options) {
|
|
|
209693
210506
|
const WS = wrapper_default;
|
|
209694
210507
|
return new WS(url, ["mcp"], options);
|
|
209695
210508
|
}
|
|
209696
|
-
function getConnectionTimeoutMs2() {
|
|
209697
|
-
return parseInt(process.env.MCP_TIMEOUT || "", 10) || 30000;
|
|
209698
|
-
}
|
|
209699
|
-
function wrapFetchWithTimeout(baseFetch) {
|
|
209700
|
-
return async (url, init) => {
|
|
209701
|
-
const method = (init?.method ?? "GET").toUpperCase();
|
|
209702
|
-
if (method === "GET") {
|
|
209703
|
-
return baseFetch(url, init);
|
|
209704
|
-
}
|
|
209705
|
-
const headers = new Headers(init?.headers);
|
|
209706
|
-
if (!headers.has("accept")) {
|
|
209707
|
-
headers.set("accept", MCP_STREAMABLE_HTTP_ACCEPT);
|
|
209708
|
-
}
|
|
209709
|
-
const controller = new AbortController;
|
|
209710
|
-
const timer = setTimeout((c) => c.abort(new DOMException("The operation timed out.", "TimeoutError")), MCP_REQUEST_TIMEOUT_MS, controller);
|
|
209711
|
-
timer.unref?.();
|
|
209712
|
-
const parentSignal = init?.signal;
|
|
209713
|
-
const abort = () => controller.abort(parentSignal?.reason);
|
|
209714
|
-
parentSignal?.addEventListener("abort", abort);
|
|
209715
|
-
if (parentSignal?.aborted) {
|
|
209716
|
-
controller.abort(parentSignal.reason);
|
|
209717
|
-
}
|
|
209718
|
-
const cleanup = () => {
|
|
209719
|
-
clearTimeout(timer);
|
|
209720
|
-
parentSignal?.removeEventListener("abort", abort);
|
|
209721
|
-
};
|
|
209722
|
-
try {
|
|
209723
|
-
const response = await baseFetch(url, {
|
|
209724
|
-
...init,
|
|
209725
|
-
headers,
|
|
209726
|
-
signal: controller.signal
|
|
209727
|
-
});
|
|
209728
|
-
cleanup();
|
|
209729
|
-
return response;
|
|
209730
|
-
} catch (error) {
|
|
209731
|
-
cleanup();
|
|
209732
|
-
throw error;
|
|
209733
|
-
}
|
|
209734
|
-
};
|
|
209735
|
-
}
|
|
209736
210509
|
function getMcpServerConnectionBatchSize() {
|
|
209737
210510
|
return parseInt(process.env.MCP_SERVER_CONNECTION_BATCH_SIZE || "", 10) || 3;
|
|
209738
210511
|
}
|
|
@@ -210382,7 +211155,7 @@ function extractToolUseId(message) {
|
|
|
210382
211155
|
}
|
|
210383
211156
|
return message.message.content[0].id;
|
|
210384
211157
|
}
|
|
210385
|
-
var fetchMcpSkillsForClient2, McpAuthError, McpSessionExpiredError, McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, DEFAULT_MCP_TOOL_TIMEOUT_MS = 1e8, MAX_MCP_DESCRIPTION_LENGTH =
|
|
211158
|
+
var fetchMcpSkillsForClient2, McpAuthError, McpSessionExpiredError, McpToolCallError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, DEFAULT_MCP_TOOL_TIMEOUT_MS = 1e8, MAX_MCP_DESCRIPTION_LENGTH = 4096, computerUseWrapper, isComputerUseMCPServer2, MCP_AUTH_CACHE_TTL_MS, authCachePromise = null, writeChain, IMAGE_MIME_TYPES, ALLOWED_IDE_TOOLS, connectToServer, MCP_FETCH_CACHE_SIZE2 = 20, fetchToolsForClient2, fetchResourcesForClient2, fetchCommandsForClient2;
|
|
210386
211159
|
var init_client4 = __esm(() => {
|
|
210387
211160
|
init_shared();
|
|
210388
211161
|
init_client3();
|
|
@@ -210674,7 +211447,7 @@ var init_client4 = __esm(() => {
|
|
|
210674
211447
|
]
|
|
210675
211448
|
};
|
|
210676
211449
|
});
|
|
210677
|
-
logMCPDebug(name, `Starting connection with timeout of ${
|
|
211450
|
+
logMCPDebug(name, `Starting connection with timeout of ${getMcpConnectionTimeoutMs()}ms`);
|
|
210678
211451
|
if (serverRef.type === "http") {
|
|
210679
211452
|
logMCPDebug(name, `Testing basic HTTP connectivity to ${serverRef.url}`);
|
|
210680
211453
|
try {
|
|
@@ -210691,13 +211464,13 @@ var init_client4 = __esm(() => {
|
|
|
210691
211464
|
const timeoutPromise = new Promise((_, reject) => {
|
|
210692
211465
|
const timeoutId = setTimeout(() => {
|
|
210693
211466
|
const elapsed = Date.now() - connectStartTime;
|
|
210694
|
-
logMCPDebug(name, `Connection timeout triggered after ${elapsed}ms (limit: ${
|
|
211467
|
+
logMCPDebug(name, `Connection timeout triggered after ${elapsed}ms (limit: ${getMcpConnectionTimeoutMs()}ms)`);
|
|
210695
211468
|
if (inProcessServer) {
|
|
210696
211469
|
inProcessServer.close().catch(() => {});
|
|
210697
211470
|
}
|
|
210698
211471
|
transport.close().catch(() => {});
|
|
210699
|
-
reject(new TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS(`MCP server "${name}" connection timed out after ${
|
|
210700
|
-
},
|
|
211472
|
+
reject(new TelemetrySafeError_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS(`MCP server "${name}" connection timed out after ${getMcpConnectionTimeoutMs()}ms`, "MCP connection timeout"));
|
|
211473
|
+
}, getMcpConnectionTimeoutMs());
|
|
210701
211474
|
connectPromise.then(() => {
|
|
210702
211475
|
clearTimeout(timeoutId);
|
|
210703
211476
|
}, (_error) => {
|
|
@@ -210745,9 +211518,8 @@ var init_client4 = __esm(() => {
|
|
|
210745
211518
|
const capabilities = client.getServerCapabilities();
|
|
210746
211519
|
const serverVersion = client.getServerVersion();
|
|
210747
211520
|
const rawInstructions = client.getInstructions();
|
|
210748
|
-
|
|
210749
|
-
if (
|
|
210750
|
-
instructions = rawInstructions.slice(0, MAX_MCP_DESCRIPTION_LENGTH) + "… [truncated]";
|
|
211521
|
+
const instructions = truncateMcpDescriptionText(rawInstructions);
|
|
211522
|
+
if (instructions !== rawInstructions) {
|
|
210751
211523
|
logMCPDebug(name, `Server instructions truncated from ${rawInstructions.length} to ${MAX_MCP_DESCRIPTION_LENGTH} chars`);
|
|
210752
211524
|
}
|
|
210753
211525
|
logMCPDebug(name, `Connection established with capabilities: ${jsonStringify5({
|
|
@@ -211035,8 +211807,7 @@ var init_client4 = __esm(() => {
|
|
|
211035
211807
|
return tool.description ?? "";
|
|
211036
211808
|
},
|
|
211037
211809
|
async prompt() {
|
|
211038
|
-
|
|
211039
|
-
return desc.length > MAX_MCP_DESCRIPTION_LENGTH ? desc.slice(0, MAX_MCP_DESCRIPTION_LENGTH) + "… [truncated]" : desc;
|
|
211810
|
+
return truncateMcpDescriptionText(tool.description ?? "");
|
|
211040
211811
|
},
|
|
211041
211812
|
isConcurrencySafe() {
|
|
211042
211813
|
return tool.annotations?.readOnlyHint ?? false;
|
|
@@ -262650,6 +263421,9 @@ function convertToHookMatchers(sessionMatchers) {
|
|
|
262650
263421
|
return sessionMatchers.map((sm) => ({
|
|
262651
263422
|
matcher: sm.matcher,
|
|
262652
263423
|
skillRoot: sm.skillRoot,
|
|
263424
|
+
pluginRoot: sm.pluginRoot,
|
|
263425
|
+
pluginName: sm.pluginName,
|
|
263426
|
+
pluginId: sm.pluginId,
|
|
262653
263427
|
hooks: sm.hooks.map((h) => h.hook).filter((h) => h.type !== "function")
|
|
262654
263428
|
}));
|
|
262655
263429
|
}
|
|
@@ -273429,6 +274203,19 @@ async function toArray2(generator) {
|
|
|
273429
274203
|
}
|
|
273430
274204
|
return result;
|
|
273431
274205
|
}
|
|
274206
|
+
async function runWithConcurrencyLimit(tasks, limit) {
|
|
274207
|
+
const results = new Array(tasks.length);
|
|
274208
|
+
let next = 0;
|
|
274209
|
+
const worker = async () => {
|
|
274210
|
+
while (next < tasks.length) {
|
|
274211
|
+
const index = next++;
|
|
274212
|
+
results[index] = await tasks[index]();
|
|
274213
|
+
}
|
|
274214
|
+
};
|
|
274215
|
+
const workers = Math.max(1, Math.min(limit, tasks.length));
|
|
274216
|
+
await Promise.all(Array.from({ length: workers }, worker));
|
|
274217
|
+
return results;
|
|
274218
|
+
}
|
|
273432
274219
|
var NO_VALUE;
|
|
273433
274220
|
var init_generators = __esm(() => {
|
|
273434
274221
|
NO_VALUE = Symbol("NO_VALUE");
|
|
@@ -276339,6 +277126,15 @@ function filterToolsForAgent2({
|
|
|
276339
277126
|
function computeChildSpawnDepth2(parentOptions) {
|
|
276340
277127
|
return (parentOptions?.spawnDepth ?? 0) + 1;
|
|
276341
277128
|
}
|
|
277129
|
+
function resolveMcpServerDomainTools2(toolName, available) {
|
|
277130
|
+
if (typeof toolName !== "string")
|
|
277131
|
+
return null;
|
|
277132
|
+
const info = mcpInfoFromString3(toolName);
|
|
277133
|
+
if (!info || info.toolName !== undefined)
|
|
277134
|
+
return null;
|
|
277135
|
+
const prefix = getMcpPrefix3(info.serverName);
|
|
277136
|
+
return available.filter((t) => t.name.startsWith(prefix));
|
|
277137
|
+
}
|
|
276342
277138
|
function resolveAgentTools2(agentDefinition, availableTools, isAsync = false, isMainThread = false, allowFanOut = false) {
|
|
276343
277139
|
const {
|
|
276344
277140
|
tools: agentTools,
|
|
@@ -276395,7 +277191,18 @@ function resolveAgentTools2(agentDefinition, availableTools, isAsync = false, is
|
|
|
276395
277191
|
resolvedToolsSet.add(tool);
|
|
276396
277192
|
}
|
|
276397
277193
|
} else {
|
|
276398
|
-
|
|
277194
|
+
const domain = resolveMcpServerDomainTools2(toolName, allowedAvailableTools);
|
|
277195
|
+
if (domain && domain.length > 0) {
|
|
277196
|
+
validTools.push(toolSpec);
|
|
277197
|
+
for (const t of domain) {
|
|
277198
|
+
if (!resolvedToolsSet.has(t)) {
|
|
277199
|
+
resolved.push(t);
|
|
277200
|
+
resolvedToolsSet.add(t);
|
|
277201
|
+
}
|
|
277202
|
+
}
|
|
277203
|
+
} else {
|
|
277204
|
+
invalidTools.push(toolSpec);
|
|
277205
|
+
}
|
|
276399
277206
|
}
|
|
276400
277207
|
}
|
|
276401
277208
|
return {
|
|
@@ -276652,6 +277459,7 @@ var init_agentToolUtils2 = __esm(() => {
|
|
|
276652
277459
|
init_errors4();
|
|
276653
277460
|
init_messages3();
|
|
276654
277461
|
init_permissionRuleParser2();
|
|
277462
|
+
init_mcpStringUtils();
|
|
276655
277463
|
init_yoloClassifier();
|
|
276656
277464
|
init_sdkProgress();
|
|
276657
277465
|
init_teammateContext3();
|
|
@@ -303311,11 +304119,11 @@ function byteAt2(L, charIdx) {
|
|
|
303311
304119
|
L.byteTable = t;
|
|
303312
304120
|
return t[charIdx];
|
|
303313
304121
|
}
|
|
303314
|
-
function
|
|
304122
|
+
function isWordChar4(c) {
|
|
303315
304123
|
return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_" || c === "/" || c === "." || c === "-" || c === "+" || c === ":" || c === "@" || c === "%" || c === "," || c === "~" || c === "^" || c === "?" || c === "*" || c === "!" || c === "=" || c === "[" || c === "]";
|
|
303316
304124
|
}
|
|
303317
304125
|
function isWordStart2(c) {
|
|
303318
|
-
return
|
|
304126
|
+
return isWordChar4(c) || c === "\\";
|
|
303319
304127
|
}
|
|
303320
304128
|
function isIdentStart2(c) {
|
|
303321
304129
|
return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_";
|
|
@@ -303625,7 +304433,7 @@ function nextToken2(L, ctx = "arg") {
|
|
|
303625
304433
|
advance2(L);
|
|
303626
304434
|
continue;
|
|
303627
304435
|
}
|
|
303628
|
-
if (!
|
|
304436
|
+
if (!isWordChar4(ch) && ch !== "{" && ch !== "}") {
|
|
303629
304437
|
break;
|
|
303630
304438
|
}
|
|
303631
304439
|
advance2(L);
|
|
@@ -321769,7 +322577,8 @@ function buildAgentLoopParams(m) {
|
|
|
321769
322577
|
}
|
|
321770
322578
|
},
|
|
321771
322579
|
systemPrompt: asSystemPrompt(appendSystemContext(m.systemPrompt, systemContext)),
|
|
321772
|
-
effortValue: m.effort !== undefined ? String(m.effort) : undefined
|
|
322580
|
+
effortValue: m.effort !== undefined ? String(m.effort) : undefined,
|
|
322581
|
+
refreshRoundContext: m.refreshRoundContext
|
|
321773
322582
|
};
|
|
321774
322583
|
const unboundedTurns = m.unboundedTurns ?? m.maxTurns === undefined;
|
|
321775
322584
|
const args = {
|
|
@@ -322338,7 +323147,8 @@ function getDefaultAppState() {
|
|
|
322338
323147
|
tools: [],
|
|
322339
323148
|
commands: [],
|
|
322340
323149
|
resources: {},
|
|
322341
|
-
pluginReconnectKey: 0
|
|
323150
|
+
pluginReconnectKey: 0,
|
|
323151
|
+
cwdReconnectKey: 0
|
|
322342
323152
|
},
|
|
322343
323153
|
plugins: {
|
|
322344
323154
|
enabled: [],
|
|
@@ -325228,7 +326038,7 @@ function parseSkillFrontmatterFields2(frontmatter, markdownContent, resolvedName
|
|
|
325228
326038
|
logForDebugging3(`Skill ${resolvedName} has invalid effort '${effortRaw}'. Valid options: ${EFFORT_LEVELS2.join(", ")} or an integer`);
|
|
325229
326039
|
}
|
|
325230
326040
|
return {
|
|
325231
|
-
displayName: frontmatter.name != null ? String(frontmatter.name) : undefined,
|
|
326041
|
+
displayName: sanitizeDisplayName2(frontmatter.name != null ? String(frontmatter.name) : undefined, resolvedName),
|
|
325232
326042
|
description,
|
|
325233
326043
|
hasUserSpecifiedDescription: validatedDescription !== null,
|
|
325234
326044
|
allowedTools: parseSlashCommandToolsFromFrontmatter2(frontmatter["allowed-tools"]),
|
|
@@ -325344,6 +326154,10 @@ async function loadSkillsFromSkillsDir2(basePath, source) {
|
|
|
325344
326154
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
325345
326155
|
return null;
|
|
325346
326156
|
}
|
|
326157
|
+
if (isUnsafeCommandName2(entry.name)) {
|
|
326158
|
+
logForDebugging3(`[skills] skipping unsafe skill directory name: ${JSON.stringify(entry.name)}`);
|
|
326159
|
+
return null;
|
|
326160
|
+
}
|
|
325347
326161
|
const skillDirPath = join108(basePath, entry.name);
|
|
325348
326162
|
const skillFilePath = join108(skillDirPath, "SKILL.md");
|
|
325349
326163
|
let content;
|
|
@@ -325454,6 +326268,10 @@ async function loadSkillsFromCommandsDir2(cwd) {
|
|
|
325454
326268
|
content,
|
|
325455
326269
|
source
|
|
325456
326270
|
});
|
|
326271
|
+
if (isUnsafeCommandName2(cmdName)) {
|
|
326272
|
+
logForDebugging3(`[skills] skipping unsafe command name: ${JSON.stringify(cmdName)}`);
|
|
326273
|
+
continue;
|
|
326274
|
+
}
|
|
325457
326275
|
const parsed = parseSkillFrontmatterFields2(frontmatter, content, cmdName, "Custom command");
|
|
325458
326276
|
skills.push({
|
|
325459
326277
|
skill: createSkillCommand2({
|
|
@@ -325592,6 +326410,7 @@ var init_loadSkillsDir2 = __esm(() => {
|
|
|
325592
326410
|
init_bootstrapState();
|
|
325593
326411
|
init_tokenEstimation();
|
|
325594
326412
|
init_argumentSubstitution2();
|
|
326413
|
+
init_commandNameSafety2();
|
|
325595
326414
|
init_debug();
|
|
325596
326415
|
init_effort2();
|
|
325597
326416
|
init_envUtils2();
|
|
@@ -330013,7 +330832,7 @@ function createPluginCommand(commandName, file, sourceName, pluginManifest, plug
|
|
|
330013
330832
|
const argumentNames = parseArgumentNames2(frontmatter.arguments);
|
|
330014
330833
|
const whenToUse = frontmatter["when-to-use"] ?? frontmatter.when_to_use;
|
|
330015
330834
|
const version = frontmatter.version;
|
|
330016
|
-
const displayName = frontmatter.name;
|
|
330835
|
+
const displayName = sanitizeDisplayName2(frontmatter.name, commandName);
|
|
330017
330836
|
const model = frontmatter.model === "inherit" ? undefined : frontmatter.model ? parseUserSpecifiedModel2(frontmatter.model) : undefined;
|
|
330018
330837
|
const effortRaw = frontmatter["effort"];
|
|
330019
330838
|
const effort = effortRaw !== undefined ? parseEffortValue2(effortRaw) : undefined;
|
|
@@ -330198,6 +331017,7 @@ var init_loadPluginCommands = __esm(() => {
|
|
|
330198
331017
|
init_bootstrapState();
|
|
330199
331018
|
init_argumentSubstitution2();
|
|
330200
331019
|
init_debug();
|
|
331020
|
+
init_commandNameSafety2();
|
|
330201
331021
|
init_effort2();
|
|
330202
331022
|
init_envUtils2();
|
|
330203
331023
|
init_errors4();
|
|
@@ -330349,8 +331169,15 @@ var init_loadPluginCommands = __esm(() => {
|
|
|
330349
331169
|
return pluginCommands;
|
|
330350
331170
|
}));
|
|
330351
331171
|
const allCommands = perPluginCommands.flat();
|
|
330352
|
-
|
|
330353
|
-
|
|
331172
|
+
const safeCommands = allCommands.filter((cmd) => {
|
|
331173
|
+
if (isUnsafeCommandName2(cmd.name)) {
|
|
331174
|
+
logForDebugging3(`Skipping unsafe plugin command name: ${JSON.stringify(cmd.name)}`);
|
|
331175
|
+
return false;
|
|
331176
|
+
}
|
|
331177
|
+
return true;
|
|
331178
|
+
});
|
|
331179
|
+
logForDebugging3(`Total plugin commands loaded: ${safeCommands.length}`);
|
|
331180
|
+
return safeCommands;
|
|
330354
331181
|
});
|
|
330355
331182
|
getPluginSkills = memoize_default(async () => {
|
|
330356
331183
|
if (isBareMode() && getInlinePlugins().length === 0) {
|
|
@@ -330395,8 +331222,15 @@ var init_loadPluginCommands = __esm(() => {
|
|
|
330395
331222
|
return pluginSkills;
|
|
330396
331223
|
}));
|
|
330397
331224
|
const allSkills = perPluginSkills.flat();
|
|
330398
|
-
|
|
330399
|
-
|
|
331225
|
+
const safeSkills = allSkills.filter((cmd) => {
|
|
331226
|
+
if (isUnsafeCommandName2(cmd.name)) {
|
|
331227
|
+
logForDebugging3(`Skipping unsafe plugin skill name: ${JSON.stringify(cmd.name)}`);
|
|
331228
|
+
return false;
|
|
331229
|
+
}
|
|
331230
|
+
return true;
|
|
331231
|
+
});
|
|
331232
|
+
logForDebugging3(`Total plugin skills loaded: ${safeSkills.length}`);
|
|
331233
|
+
return safeSkills;
|
|
330400
331234
|
});
|
|
330401
331235
|
});
|
|
330402
331236
|
|
|
@@ -335419,6 +336253,12 @@ function mergePluginSources(sources) {
|
|
|
335419
336253
|
const marketplacePlugins = sources.marketplace.filter((p) => {
|
|
335420
336254
|
if (sessionNames.has(p.name)) {
|
|
335421
336255
|
logForDebugging3(`Plugin "${p.name}" from --plugin-dir overrides installed version`);
|
|
336256
|
+
errors.push({
|
|
336257
|
+
type: "generic-error",
|
|
336258
|
+
source: p.source,
|
|
336259
|
+
plugin: p.name,
|
|
336260
|
+
error: `Installed plugin "${p.name}" excluded (hooks and commands inactive) — overridden by --plugin-dir copy of the same name`
|
|
336261
|
+
});
|
|
335422
336262
|
return false;
|
|
335423
336263
|
}
|
|
335424
336264
|
return true;
|
|
@@ -337425,8 +338265,9 @@ function handleMessageFromStream(message, onMessage, onUpdateLength, onSetStream
|
|
|
337425
338265
|
}
|
|
337426
338266
|
}
|
|
337427
338267
|
function wrapInSystemReminder(content) {
|
|
338268
|
+
const neutralized = content.replaceAll("</system-reminder>", "</system-reminder>").replaceAll("<system-reminder>", "<system-reminder>");
|
|
337428
338269
|
return `<system-reminder>
|
|
337429
|
-
${
|
|
338270
|
+
${neutralized}
|
|
337430
338271
|
</system-reminder>`;
|
|
337431
338272
|
}
|
|
337432
338273
|
function wrapMessagesInSystemReminder(messages) {
|
|
@@ -340035,6 +340876,16 @@ async function hasPermissionsToUseToolInner(tool, input, context) {
|
|
|
340035
340876
|
throw new AbortError2;
|
|
340036
340877
|
}
|
|
340037
340878
|
let appState = context.getAppState();
|
|
340879
|
+
if (appState.toolPermissionContext.mode === "plan" && !appState.toolPermissionContext.isBypassPermissionsModeAvailable && !PLAN_MODE_WRITE_EXEMPT_TOOL_NAMES2.has(tool.name) && !(tool.isReadOnly?.(input) ?? false)) {
|
|
340880
|
+
return {
|
|
340881
|
+
behavior: "deny",
|
|
340882
|
+
message: PLAN_MODE_WRITE_DENIED_MESSAGE2(tool.name),
|
|
340883
|
+
decisionReason: {
|
|
340884
|
+
type: "mode",
|
|
340885
|
+
mode: "plan"
|
|
340886
|
+
}
|
|
340887
|
+
};
|
|
340888
|
+
}
|
|
340038
340889
|
const denyRule = getDenyRuleForTool2(appState.toolPermissionContext, tool);
|
|
340039
340890
|
if (denyRule) {
|
|
340040
340891
|
return {
|
|
@@ -340215,7 +341066,7 @@ function syncPermissionRulesFromDisk2(toolPermissionContext, rules) {
|
|
|
340215
341066
|
function getUpdatedInputOrFallback2(permissionResult, fallback) {
|
|
340216
341067
|
return ("updatedInput" in permissionResult ? permissionResult.updatedInput : undefined) ?? fallback;
|
|
340217
341068
|
}
|
|
340218
|
-
var classifierDecisionModule, autoModeStateModule3, CLASSIFIER_FAIL_CLOSED_REFRESH_MS, PERMISSION_RULE_SOURCES2, hasPermissionsToUseTool2 = async (tool, input, context, assistantMessage, toolUseID) => {
|
|
341069
|
+
var PLAN_MODE_WRITE_EXEMPT_TOOL_NAMES2, PLAN_MODE_WRITE_DENIED_MESSAGE2 = (toolName) => `Plan mode is active — ${toolName} is not allowed to make changes. ` + `Present your plan and use ExitPlanMode, or ask the user to change the permission mode.`, classifierDecisionModule, autoModeStateModule3, CLASSIFIER_FAIL_CLOSED_REFRESH_MS, PERMISSION_RULE_SOURCES2, hasPermissionsToUseTool2 = async (tool, input, context, assistantMessage, toolUseID) => {
|
|
340219
341070
|
const result = await hasPermissionsToUseToolInner(tool, input, context);
|
|
340220
341071
|
if (result.behavior === "allow") {
|
|
340221
341072
|
const appState = context.getAppState();
|
|
@@ -340448,6 +341299,10 @@ var init_permissions6 = __esm(() => {
|
|
|
340448
341299
|
init_slowOperations();
|
|
340449
341300
|
init_denialTracking();
|
|
340450
341301
|
init_yoloClassifier();
|
|
341302
|
+
PLAN_MODE_WRITE_EXEMPT_TOOL_NAMES2 = new Set([
|
|
341303
|
+
"EnterPlanMode",
|
|
341304
|
+
"ExitPlanMode"
|
|
341305
|
+
]);
|
|
340451
341306
|
classifierDecisionModule = feature("TRANSCRIPT_CLASSIFIER") ? (init_classifierDecision(), __toCommonJS(exports_classifierDecision)) : null;
|
|
340452
341307
|
autoModeStateModule3 = feature("TRANSCRIPT_CLASSIFIER") ? __toCommonJS(exports_autoModeState) : null;
|
|
340453
341308
|
CLASSIFIER_FAIL_CLOSED_REFRESH_MS = 30 * 60 * 1000;
|
|
@@ -348079,7 +348934,8 @@ async function clearConversation({
|
|
|
348079
348934
|
tools: [],
|
|
348080
348935
|
commands: [],
|
|
348081
348936
|
resources: {},
|
|
348082
|
-
pluginReconnectKey: prev.mcp.pluginReconnectKey
|
|
348937
|
+
pluginReconnectKey: prev.mcp.pluginReconnectKey,
|
|
348938
|
+
cwdReconnectKey: prev.mcp.cwdReconnectKey
|
|
348083
348939
|
}
|
|
348084
348940
|
};
|
|
348085
348941
|
});
|
|
@@ -369468,7 +370324,9 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
|
|
|
369468
370324
|
const store = useAppStateStore();
|
|
369469
370325
|
const _authVersion = useAppState((s) => s.authVersion);
|
|
369470
370326
|
const _pluginReconnectKey = useAppState((s) => s.mcp.pluginReconnectKey);
|
|
370327
|
+
const _cwdReconnectKey = useAppState((s) => s.mcp.cwdReconnectKey);
|
|
369471
370328
|
const setAppState = useSetAppState();
|
|
370329
|
+
const lastCwdRef = import_react110.useRef(null);
|
|
369472
370330
|
const reconnectTimersRef = import_react110.useRef(new Map);
|
|
369473
370331
|
const mcpFailureTrackerRef = import_react110.useRef(null);
|
|
369474
370332
|
const notifyMcpConnectionFailure = import_react110.useCallback((serverName, error) => {
|
|
@@ -369695,9 +370553,12 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
|
|
|
369695
370553
|
async function initializeServersAsPending() {
|
|
369696
370554
|
const { servers: existingConfigs, errors: mcpErrors } = isStrictMcpConfig ? { servers: {}, errors: [] } : await getMcpConfigs(dynamicMcpConfig);
|
|
369697
370555
|
const configs = { ...existingConfigs, ...dynamicMcpConfig };
|
|
370556
|
+
const currentCwd = getCwd();
|
|
370557
|
+
const cwdChanged = lastCwdRef.current !== null && lastCwdRef.current !== currentCwd;
|
|
370558
|
+
lastCwdRef.current = currentCwd;
|
|
369698
370559
|
addErrorsToAppState(setAppState, mcpErrors);
|
|
369699
370560
|
setAppState((prevState) => {
|
|
369700
|
-
const { stale, ...mcpWithoutStale } = excludeStalePluginClients(prevState.mcp, configs);
|
|
370561
|
+
const { stale, ...mcpWithoutStale } = excludeStalePluginClients(prevState.mcp, configs, { absentIsStale: cwdChanged });
|
|
369701
370562
|
for (const s of stale) {
|
|
369702
370563
|
const timer = reconnectTimersRef.current.get(s.name);
|
|
369703
370564
|
if (timer) {
|
|
@@ -369736,7 +370597,8 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
|
|
|
369736
370597
|
dynamicMcpConfig,
|
|
369737
370598
|
setAppState,
|
|
369738
370599
|
sessionId,
|
|
369739
|
-
_pluginReconnectKey
|
|
370600
|
+
_pluginReconnectKey,
|
|
370601
|
+
_cwdReconnectKey
|
|
369740
370602
|
]);
|
|
369741
370603
|
import_react110.useEffect(() => {
|
|
369742
370604
|
let cancelled = false;
|
|
@@ -369753,7 +370615,7 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
|
|
|
369753
370615
|
});
|
|
369754
370616
|
let claudeaiConfigs = {};
|
|
369755
370617
|
if (!isStrictMcpConfig) {
|
|
369756
|
-
claudeaiConfigs =
|
|
370618
|
+
claudeaiConfigs = filterMcpServersByPolicy2(await claudeaiPromise).allowed;
|
|
369757
370619
|
if (cancelled)
|
|
369758
370620
|
return;
|
|
369759
370621
|
if (Object.keys(claudeaiConfigs).length > 0) {
|
|
@@ -369819,7 +370681,8 @@ function useManageMCPConnections(dynamicMcpConfig, isStrictMcpConfig = false) {
|
|
|
369819
370681
|
setAppState,
|
|
369820
370682
|
_authVersion,
|
|
369821
370683
|
sessionId,
|
|
369822
|
-
_pluginReconnectKey
|
|
370684
|
+
_pluginReconnectKey,
|
|
370685
|
+
_cwdReconnectKey
|
|
369823
370686
|
]);
|
|
369824
370687
|
import_react110.useEffect(() => {
|
|
369825
370688
|
const timers = reconnectTimersRef.current;
|
|
@@ -404670,6 +405533,8 @@ async function searchSkillsAcrossMarketplaces(query) {
|
|
|
404670
405533
|
for (const d of dirEntries) {
|
|
404671
405534
|
if (!d.isDirectory())
|
|
404672
405535
|
continue;
|
|
405536
|
+
if (isUnsafeCommandName2(d.name))
|
|
405537
|
+
continue;
|
|
404673
405538
|
const skillFile = join146(skillDir, d.name, "SKILL.md");
|
|
404674
405539
|
if (seen.has(skillFile))
|
|
404675
405540
|
continue;
|
|
@@ -404681,7 +405546,7 @@ async function searchSkillsAcrossMarketplaces(query) {
|
|
|
404681
405546
|
continue;
|
|
404682
405547
|
}
|
|
404683
405548
|
const { frontmatter, content: markdown } = parseFrontmatter2(content);
|
|
404684
|
-
const resolvedName =
|
|
405549
|
+
const resolvedName = d.name;
|
|
404685
405550
|
const parsed = parseSkillFrontmatterFields2(frontmatter, markdown, resolvedName);
|
|
404686
405551
|
const installId = createPluginId(entry.name, marketplaceName);
|
|
404687
405552
|
const skill = {
|
|
@@ -404706,6 +405571,7 @@ async function searchSkillsAcrossMarketplaces(query) {
|
|
|
404706
405571
|
return results;
|
|
404707
405572
|
}
|
|
404708
405573
|
var init_skillSearch = __esm(() => {
|
|
405574
|
+
init_commandNameSafety2();
|
|
404709
405575
|
init_frontmatterParser2();
|
|
404710
405576
|
init_loadSkillsDir2();
|
|
404711
405577
|
init_marketplaceHelpers();
|
|
@@ -412070,7 +412936,7 @@ var call45 = async (_args, _context) => {
|
|
|
412070
412936
|
reloadSkillsNow();
|
|
412071
412937
|
return {
|
|
412072
412938
|
type: "text",
|
|
412073
|
-
value: "Re-scanned skill directories; in-session skill registry refreshed (new/removed/changed skills now active without restart)."
|
|
412939
|
+
value: "Re-scanned skill directories; in-session skill registry refreshed (new/removed/changed skills now active without restart). The complete skill list will be re-sent to the model with your next message."
|
|
412074
412940
|
};
|
|
412075
412941
|
};
|
|
412076
412942
|
var init_reload_skills = __esm(() => {
|
|
@@ -412095,7 +412961,7 @@ var exports_cd = {};
|
|
|
412095
412961
|
__export(exports_cd, {
|
|
412096
412962
|
call: () => call46
|
|
412097
412963
|
});
|
|
412098
|
-
var USAGE2 = "Usage: /cd [path] (no argument = show current session cwd)", call46 = async (_args,
|
|
412964
|
+
var USAGE2 = "Usage: /cd [path] (no argument = show current session cwd)", call46 = async (_args, context) => {
|
|
412099
412965
|
const arg = (_args ?? "").trim();
|
|
412100
412966
|
if (arg === "") {
|
|
412101
412967
|
return {
|
|
@@ -412106,6 +412972,10 @@ ${USAGE2}`
|
|
|
412106
412972
|
}
|
|
412107
412973
|
try {
|
|
412108
412974
|
setCwd2(arg, pwd());
|
|
412975
|
+
context.setAppState((prev) => ({
|
|
412976
|
+
...prev,
|
|
412977
|
+
mcp: { ...prev.mcp, cwdReconnectKey: prev.mcp.cwdReconnectKey + 1 }
|
|
412978
|
+
}));
|
|
412109
412979
|
return {
|
|
412110
412980
|
type: "text",
|
|
412111
412981
|
value: `Session working directory changed to: ${pwd()}`
|
|
@@ -428100,9 +428970,144 @@ var init_combinedAbortSignal = __esm(() => {
|
|
|
428100
428970
|
init_abortController2();
|
|
428101
428971
|
});
|
|
428102
428972
|
|
|
428973
|
+
// src/tui/utils/hooks/hookInputDepth.ts
|
|
428974
|
+
function exceedsMaxNestingDepth(value) {
|
|
428975
|
+
const stack = [[value, 0]];
|
|
428976
|
+
const visited = new WeakSet;
|
|
428977
|
+
while (stack.length > 0) {
|
|
428978
|
+
const [v, depth] = stack.pop();
|
|
428979
|
+
if (depth > MAX_HOOK_INPUT_NESTING_DEPTH)
|
|
428980
|
+
return true;
|
|
428981
|
+
if (v === null || typeof v !== "object")
|
|
428982
|
+
continue;
|
|
428983
|
+
if (visited.has(v))
|
|
428984
|
+
continue;
|
|
428985
|
+
visited.add(v);
|
|
428986
|
+
let children;
|
|
428987
|
+
try {
|
|
428988
|
+
children = Array.isArray(v) ? v : Object.values(v);
|
|
428989
|
+
} catch {
|
|
428990
|
+
continue;
|
|
428991
|
+
}
|
|
428992
|
+
for (const child of children) {
|
|
428993
|
+
stack.push([child, depth + 1]);
|
|
428994
|
+
}
|
|
428995
|
+
}
|
|
428996
|
+
return false;
|
|
428997
|
+
}
|
|
428998
|
+
var MAX_HOOK_INPUT_NESTING_DEPTH = 28000;
|
|
428999
|
+
|
|
429000
|
+
// src/tui/utils/hooks/hookInputMaterialize.ts
|
|
429001
|
+
function materializeHookInput(value) {
|
|
429002
|
+
if (value === null || typeof value !== "object") {
|
|
429003
|
+
return { ok: true, value, droppedKeys: [] };
|
|
429004
|
+
}
|
|
429005
|
+
const droppedKeys = [];
|
|
429006
|
+
const visited = new WeakMap;
|
|
429007
|
+
const root = Array.isArray(value) ? [] : {};
|
|
429008
|
+
visited.set(value, root);
|
|
429009
|
+
const stack = [{ src: value, target: root }];
|
|
429010
|
+
while (stack.length > 0) {
|
|
429011
|
+
const { src, target } = stack.pop();
|
|
429012
|
+
let keys;
|
|
429013
|
+
try {
|
|
429014
|
+
keys = Object.keys(src);
|
|
429015
|
+
} catch {
|
|
429016
|
+
droppedKeys.push("<enumeration-failed>");
|
|
429017
|
+
continue;
|
|
429018
|
+
}
|
|
429019
|
+
for (const key of keys) {
|
|
429020
|
+
let child;
|
|
429021
|
+
try {
|
|
429022
|
+
child = src[key];
|
|
429023
|
+
} catch {
|
|
429024
|
+
droppedKeys.push(key);
|
|
429025
|
+
continue;
|
|
429026
|
+
}
|
|
429027
|
+
if (child === null || typeof child !== "object") {
|
|
429028
|
+
target[key] = child;
|
|
429029
|
+
} else if (visited.has(child)) {
|
|
429030
|
+
target[key] = "[Circular]";
|
|
429031
|
+
} else {
|
|
429032
|
+
const childClone = Array.isArray(child) ? [] : {};
|
|
429033
|
+
visited.set(child, childClone);
|
|
429034
|
+
target[key] = childClone;
|
|
429035
|
+
stack.push({ src: child, target: childClone });
|
|
429036
|
+
}
|
|
429037
|
+
}
|
|
429038
|
+
}
|
|
429039
|
+
return { ok: true, value: root, droppedKeys };
|
|
429040
|
+
}
|
|
429041
|
+
function stringifyWithGetterFallback(value) {
|
|
429042
|
+
try {
|
|
429043
|
+
return { ok: true, value: jsonStringify5(value), droppedKeys: [] };
|
|
429044
|
+
} catch (error) {
|
|
429045
|
+
const materialized = materializeHookInput(value);
|
|
429046
|
+
if (materialized.ok === false)
|
|
429047
|
+
return { ok: false, error };
|
|
429048
|
+
try {
|
|
429049
|
+
return {
|
|
429050
|
+
ok: true,
|
|
429051
|
+
value: jsonStringify5(materialized.value),
|
|
429052
|
+
droppedKeys: materialized.droppedKeys
|
|
429053
|
+
};
|
|
429054
|
+
} catch {
|
|
429055
|
+
return { ok: false, error };
|
|
429056
|
+
}
|
|
429057
|
+
}
|
|
429058
|
+
}
|
|
429059
|
+
var init_hookInputMaterialize = __esm(() => {
|
|
429060
|
+
init_slowOperations();
|
|
429061
|
+
});
|
|
429062
|
+
|
|
429063
|
+
// src/tui/utils/hooks/hookReentrancy.ts
|
|
429064
|
+
import { AsyncLocalStorage as AsyncLocalStorage8 } from "async_hooks";
|
|
429065
|
+
function getCallbackId(fn) {
|
|
429066
|
+
let id = callbackIds.get(fn);
|
|
429067
|
+
if (id === undefined) {
|
|
429068
|
+
id = nextCallbackId++;
|
|
429069
|
+
callbackIds.set(fn, id);
|
|
429070
|
+
}
|
|
429071
|
+
return id;
|
|
429072
|
+
}
|
|
429073
|
+
function getHookReentrancyKey(hookEvent, hook, pluginRoot) {
|
|
429074
|
+
const h = hook;
|
|
429075
|
+
const body = typeof h.callback === "function" ? `fn:${getCallbackId(h.callback)}` : String(h.command ?? h.prompt ?? h.url ?? "");
|
|
429076
|
+
return `${hookEvent}:${String(h.type)}:${pluginRoot ?? ""}:${body}`;
|
|
429077
|
+
}
|
|
429078
|
+
function isReentrantHook(hookEvent, hook, pluginRoot) {
|
|
429079
|
+
const inFlight = hookReentrancyStorage.getStore();
|
|
429080
|
+
if (inFlight === undefined)
|
|
429081
|
+
return false;
|
|
429082
|
+
return inFlight.has(getHookReentrancyKey(hookEvent, hook, pluginRoot));
|
|
429083
|
+
}
|
|
429084
|
+
function runWithHookReentrancy(hookEvent, hook, pluginRoot, fn) {
|
|
429085
|
+
const store = new Set(hookReentrancyStorage.getStore() ?? []);
|
|
429086
|
+
store.add(getHookReentrancyKey(hookEvent, hook, pluginRoot));
|
|
429087
|
+
return hookReentrancyStorage.run(store, fn);
|
|
429088
|
+
}
|
|
429089
|
+
var hookReentrancyStorage, callbackIds, nextCallbackId = 1;
|
|
429090
|
+
var init_hookReentrancy = __esm(() => {
|
|
429091
|
+
hookReentrancyStorage = new AsyncLocalStorage8;
|
|
429092
|
+
callbackIds = new WeakMap;
|
|
429093
|
+
});
|
|
429094
|
+
|
|
429095
|
+
// src/tui/utils/hooks/hookFailClosed.ts
|
|
429096
|
+
function shouldFailClosePluginHook(hookEvent, pluginRoot) {
|
|
429097
|
+
return pluginRoot !== undefined && GATE_EVENTS.has(hookEvent);
|
|
429098
|
+
}
|
|
429099
|
+
var GATE_EVENTS;
|
|
429100
|
+
var init_hookFailClosed = __esm(() => {
|
|
429101
|
+
GATE_EVENTS = new Set([
|
|
429102
|
+
"PreToolUse",
|
|
429103
|
+
"UserPromptSubmit",
|
|
429104
|
+
"PermissionRequest"
|
|
429105
|
+
]);
|
|
429106
|
+
});
|
|
429107
|
+
|
|
428103
429108
|
// src/tui/utils/hooks/hookHelpers.ts
|
|
428104
429109
|
function addArgumentsToPrompt(prompt, jsonInput) {
|
|
428105
|
-
return
|
|
429110
|
+
return substituteArgumentsStrict2(prompt, jsonInput);
|
|
428106
429111
|
}
|
|
428107
429112
|
function createStructuredOutputTool() {
|
|
428108
429113
|
return {
|
|
@@ -428150,6 +429155,19 @@ async function execPromptHook(hook, hookName, hookEvent, jsonInput, signal, tool
|
|
|
428150
429155
|
const effectiveToolUseID = toolUseID || `hook-${randomUUID41()}`;
|
|
428151
429156
|
try {
|
|
428152
429157
|
const processedPrompt = addArgumentsToPrompt(hook.prompt, jsonInput);
|
|
429158
|
+
if (processedPrompt === null) {
|
|
429159
|
+
logForDebugging3(`Hooks: hook input exceeds substitution budget — blocking (fail-closed)`);
|
|
429160
|
+
return {
|
|
429161
|
+
hook,
|
|
429162
|
+
outcome: "blocking",
|
|
429163
|
+
blockingError: {
|
|
429164
|
+
blockingError: "Hook input exceeds substitution budget; refusing to evaluate on truncated input",
|
|
429165
|
+
command: hook.prompt
|
|
429166
|
+
},
|
|
429167
|
+
preventContinuation: true,
|
|
429168
|
+
stopReason: "hook input exceeds substitution budget"
|
|
429169
|
+
};
|
|
429170
|
+
}
|
|
428153
429171
|
logForDebugging3(`Hooks: Processing prompt hook with prompt: ${processedPrompt}`);
|
|
428154
429172
|
const userMessage = createUserMessage2({ content: processedPrompt });
|
|
428155
429173
|
const messagesToQuery = messages && messages.length > 0 ? [...messages, userMessage] : [userMessage];
|
|
@@ -428296,6 +429314,17 @@ async function execAgentHook(hook, hookName, hookEvent, jsonInput, signal, toolU
|
|
|
428296
429314
|
const hookStartTime = Date.now();
|
|
428297
429315
|
try {
|
|
428298
429316
|
const processedPrompt = addArgumentsToPrompt(hook.prompt, jsonInput);
|
|
429317
|
+
if (processedPrompt === null) {
|
|
429318
|
+
logForDebugging3(`Hooks: hook input exceeds substitution budget — blocking (fail-closed)`);
|
|
429319
|
+
return {
|
|
429320
|
+
hook,
|
|
429321
|
+
outcome: "blocking",
|
|
429322
|
+
blockingError: {
|
|
429323
|
+
blockingError: "Hook input exceeds substitution budget; refusing to verify on truncated input",
|
|
429324
|
+
command: hook.prompt
|
|
429325
|
+
}
|
|
429326
|
+
};
|
|
429327
|
+
}
|
|
428299
429328
|
logForDebugging3(`Hooks: Processing agent hook with prompt: ${processedPrompt}`);
|
|
428300
429329
|
const userMessage = createUserMessage2({ content: processedPrompt });
|
|
428301
429330
|
const agentMessages = [userMessage];
|
|
@@ -428686,7 +429715,7 @@ function getHttpHookPolicy() {
|
|
|
428686
429715
|
allowedEnvVars: settings.httpHookAllowedEnvVars
|
|
428687
429716
|
};
|
|
428688
429717
|
}
|
|
428689
|
-
function
|
|
429718
|
+
function urlMatchesPattern3(url, pattern) {
|
|
428690
429719
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
428691
429720
|
const regexStr = escaped.replace(/\*/g, ".*");
|
|
428692
429721
|
return new RegExp(`^${regexStr}$`).test(url);
|
|
@@ -428708,7 +429737,7 @@ function interpolateEnvVars(value, allowedEnvVars) {
|
|
|
428708
429737
|
async function execHttpHook(hook, _hookEvent, jsonInput, signal) {
|
|
428709
429738
|
const policy = getHttpHookPolicy();
|
|
428710
429739
|
if (policy.allowedUrls !== undefined) {
|
|
428711
|
-
const matched = policy.allowedUrls.some((p) =>
|
|
429740
|
+
const matched = policy.allowedUrls.some((p) => urlMatchesPattern3(hook.url, p));
|
|
428712
429741
|
if (!matched) {
|
|
428713
429742
|
const msg = `HTTP hook blocked: ${hook.url} does not match any pattern in allowedHttpHookUrls`;
|
|
428714
429743
|
logForDebugging3(msg, { level: "warn" });
|
|
@@ -428784,7 +429813,7 @@ import { randomUUID as randomUUID43 } from "crypto";
|
|
|
428784
429813
|
function getSessionEndHookTimeoutMs() {
|
|
428785
429814
|
const raw = process.env.ATLAS_SESSIONEND_HOOKS_TIMEOUT_MS;
|
|
428786
429815
|
const parsed = raw ? parseInt(raw, 10) : NaN;
|
|
428787
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed :
|
|
429816
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : SESSION_END_HOOK_TIMEOUT_MS_DEFAULT2;
|
|
428788
429817
|
}
|
|
428789
429818
|
function executeInBackground({
|
|
428790
429819
|
processId,
|
|
@@ -429820,18 +430849,33 @@ async function* executeHooks({
|
|
|
429820
430849
|
if (jsonInputResult !== undefined) {
|
|
429821
430850
|
return jsonInputResult;
|
|
429822
430851
|
}
|
|
429823
|
-
|
|
429824
|
-
|
|
429825
|
-
} catch (error) {
|
|
430852
|
+
if (exceedsMaxNestingDepth(hookInput)) {
|
|
430853
|
+
const error = Error(`Hook input nesting depth exceeds MAX_HOOK_INPUT_NESTING_DEPTH (${MAX_HOOK_INPUT_NESTING_DEPTH}); refusing to serialize`);
|
|
429826
430854
|
logError3(Error(`Failed to stringify hook ${hookName} input`, { cause: error }));
|
|
429827
430855
|
return jsonInputResult = { ok: false, error };
|
|
429828
430856
|
}
|
|
430857
|
+
const serialized = stringifyWithGetterFallback(hookInput);
|
|
430858
|
+
if (serialized.ok !== true) {
|
|
430859
|
+
logError3(Error(`Failed to stringify hook ${hookName} input`, {
|
|
430860
|
+
cause: serialized.error
|
|
430861
|
+
}));
|
|
430862
|
+
return jsonInputResult = { ok: false, error: serialized.error };
|
|
430863
|
+
}
|
|
430864
|
+
if (serialized.droppedKeys.length > 0) {
|
|
430865
|
+
logForDebugging3(`Hooks: ${hookName}: dropped input keys with throwing getters: ${serialized.droppedKeys.join(", ")}`);
|
|
430866
|
+
}
|
|
430867
|
+
return jsonInputResult = { ok: true, value: serialized.value };
|
|
429829
430868
|
}
|
|
429830
430869
|
const hookPromises = matchingHooks.map(async function* ({ hook, pluginRoot, pluginId, skillRoot }, hookIndex) {
|
|
430870
|
+
if (isReentrantHook(hookEvent, hook, pluginRoot)) {
|
|
430871
|
+
logForDebugging3(`Hooks: ${hookName}: skipping re-entrant execution of ${getHookDisplayText(hook)}`);
|
|
430872
|
+
yield { outcome: "success", hook };
|
|
430873
|
+
return;
|
|
430874
|
+
}
|
|
429831
430875
|
if (hook.type === "callback") {
|
|
429832
430876
|
const callbackTimeoutMs = hook.timeout ? hook.timeout * 1000 : timeoutMs;
|
|
429833
430877
|
const { signal: abortSignal, cleanup } = createCombinedAbortSignal(signal, { timeoutMs: callbackTimeoutMs });
|
|
429834
|
-
yield executeHookCallback({
|
|
430878
|
+
yield runWithHookReentrancy(hookEvent, hook, pluginRoot, () => executeHookCallback({
|
|
429835
430879
|
toolUseID,
|
|
429836
430880
|
hook,
|
|
429837
430881
|
hookEvent,
|
|
@@ -429839,7 +430883,23 @@ async function* executeHooks({
|
|
|
429839
430883
|
signal: abortSignal,
|
|
429840
430884
|
hookIndex,
|
|
429841
430885
|
toolUseContext
|
|
429842
|
-
}).
|
|
430886
|
+
}).catch((error) => {
|
|
430887
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
430888
|
+
return {
|
|
430889
|
+
message: createAttachmentMessage({
|
|
430890
|
+
type: "hook_non_blocking_error",
|
|
430891
|
+
hookName: `${hookEvent}:Callback`,
|
|
430892
|
+
toolUseID,
|
|
430893
|
+
hookEvent,
|
|
430894
|
+
stderr: `Failed to run: ${errorMessage}`,
|
|
430895
|
+
stdout: "",
|
|
430896
|
+
exitCode: 1,
|
|
430897
|
+
command: "callback"
|
|
430898
|
+
}),
|
|
430899
|
+
outcome: "non_blocking_error",
|
|
430900
|
+
hook
|
|
430901
|
+
};
|
|
430902
|
+
}).finally(cleanup));
|
|
429843
430903
|
return;
|
|
429844
430904
|
}
|
|
429845
430905
|
if (hook.type === "function") {
|
|
@@ -429857,7 +430917,7 @@ async function* executeHooks({
|
|
|
429857
430917
|
};
|
|
429858
430918
|
return;
|
|
429859
430919
|
}
|
|
429860
|
-
yield executeFunctionHook({
|
|
430920
|
+
yield runWithHookReentrancy(hookEvent, hook, pluginRoot, () => executeFunctionHook({
|
|
429861
430921
|
hook,
|
|
429862
430922
|
messages,
|
|
429863
430923
|
hookName,
|
|
@@ -429865,7 +430925,7 @@ async function* executeHooks({
|
|
|
429865
430925
|
hookEvent,
|
|
429866
430926
|
timeoutMs,
|
|
429867
430927
|
signal
|
|
429868
|
-
});
|
|
430928
|
+
}));
|
|
429869
430929
|
return;
|
|
429870
430930
|
}
|
|
429871
430931
|
const commandTimeoutMs = hook.timeout ? hook.timeout * 1000 : timeoutMs;
|
|
@@ -429878,6 +430938,7 @@ async function* executeHooks({
|
|
|
429878
430938
|
try {
|
|
429879
430939
|
const jsonInputRes = getJsonInput();
|
|
429880
430940
|
if (!jsonInputRes.ok) {
|
|
430941
|
+
const failClosed = shouldFailClosePluginHook(hookEvent, pluginRoot);
|
|
429881
430942
|
yield {
|
|
429882
430943
|
message: createAttachmentMessage({
|
|
429883
430944
|
type: "hook_error_during_execution",
|
|
@@ -429888,7 +430949,13 @@ async function* executeHooks({
|
|
|
429888
430949
|
command: hookCommand,
|
|
429889
430950
|
durationMs: Date.now() - hookStartMs
|
|
429890
430951
|
}),
|
|
429891
|
-
|
|
430952
|
+
...failClosed ? {
|
|
430953
|
+
blockingError: {
|
|
430954
|
+
blockingError: `Plugin hook input could not be prepared; blocking gated action (fail-closed): ${errorMessage2(jsonInputRes.error)}`,
|
|
430955
|
+
command: hookCommand
|
|
430956
|
+
}
|
|
430957
|
+
} : {},
|
|
430958
|
+
outcome: failClosed ? "blocking" : "non_blocking_error",
|
|
429892
430959
|
hook
|
|
429893
430960
|
};
|
|
429894
430961
|
cleanup();
|
|
@@ -429899,7 +430966,7 @@ async function* executeHooks({
|
|
|
429899
430966
|
if (!toolUseContext) {
|
|
429900
430967
|
throw new Error("ToolUseContext is required for prompt hooks. This is a bug.");
|
|
429901
430968
|
}
|
|
429902
|
-
const promptResult = await execPromptHook(hook, hookName, hookEvent, jsonInput, abortSignal, toolUseContext, messages, toolUseID);
|
|
430969
|
+
const promptResult = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => execPromptHook(hook, hookName, hookEvent, jsonInput, abortSignal, toolUseContext, messages, toolUseID));
|
|
429903
430970
|
if (promptResult.message?.type === "attachment") {
|
|
429904
430971
|
const att = promptResult.message.attachment;
|
|
429905
430972
|
if (att.type === "hook_success" || att.type === "hook_non_blocking_error") {
|
|
@@ -429918,7 +430985,7 @@ async function* executeHooks({
|
|
|
429918
430985
|
if (!messages) {
|
|
429919
430986
|
throw new Error("Messages are required for agent hooks. This is a bug.");
|
|
429920
430987
|
}
|
|
429921
|
-
const agentResult = await execAgentHook(hook, hookName, hookEvent, jsonInput, abortSignal, toolUseContext, toolUseID, messages, "agent_type" in hookInput ? hookInput.agent_type : undefined);
|
|
430988
|
+
const agentResult = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => execAgentHook(hook, hookName, hookEvent, jsonInput, abortSignal, toolUseContext, toolUseID, messages, "agent_type" in hookInput ? hookInput.agent_type : undefined));
|
|
429922
430989
|
if (agentResult.message?.type === "attachment") {
|
|
429923
430990
|
const att = agentResult.message.attachment;
|
|
429924
430991
|
if (att.type === "hook_success" || att.type === "hook_non_blocking_error") {
|
|
@@ -429932,7 +430999,7 @@ async function* executeHooks({
|
|
|
429932
430999
|
}
|
|
429933
431000
|
if (hook.type === "http") {
|
|
429934
431001
|
emitHookStarted(hookId, hookName, hookEvent);
|
|
429935
|
-
const httpResult = await execHttpHook(hook, hookEvent, jsonInput, signal);
|
|
431002
|
+
const httpResult = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => execHttpHook(hook, hookEvent, jsonInput, signal));
|
|
429936
431003
|
cleanup?.();
|
|
429937
431004
|
if (httpResult.aborted) {
|
|
429938
431005
|
emitHookResponse({
|
|
@@ -430060,7 +431127,7 @@ async function* executeHooks({
|
|
|
430060
431127
|
return;
|
|
430061
431128
|
}
|
|
430062
431129
|
emitHookStarted(hookId, hookName, hookEvent);
|
|
430063
|
-
const result = await execCommandHook(hook, hookEvent, hookName, jsonInput, abortSignal, hookId, hookIndex, pluginRoot, pluginId, skillRoot, forceSyncExecution, boundRequestPrompt);
|
|
431130
|
+
const result = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => execCommandHook(hook, hookEvent, hookName, jsonInput, abortSignal, hookId, hookIndex, pluginRoot, pluginId, skillRoot, forceSyncExecution, boundRequestPrompt));
|
|
430064
431131
|
cleanup?.();
|
|
430065
431132
|
const durationMs = Date.now() - hookStartMs;
|
|
430066
431133
|
if (result.backgrounded) {
|
|
@@ -430306,7 +431373,7 @@ async function* executeHooks({
|
|
|
430306
431373
|
cancelled: 0
|
|
430307
431374
|
};
|
|
430308
431375
|
let permissionBehavior;
|
|
430309
|
-
for await (const result of all2(hookPromises)) {
|
|
431376
|
+
for await (const result of all2(hookPromises, MAX_HOOK_CONCURRENCY)) {
|
|
430310
431377
|
outcomes[result.outcome]++;
|
|
430311
431378
|
if (result.terminalSequence) {
|
|
430312
431379
|
try {
|
|
@@ -430496,20 +431563,46 @@ async function executeHooksOutsideREPL({
|
|
|
430496
431563
|
const pluginHookCounts = getPluginHookCounts(userHooks);
|
|
430497
431564
|
const hookTypeCounts = getHookTypeCounts(userHooks);
|
|
430498
431565
|
}
|
|
430499
|
-
|
|
430500
|
-
|
|
430501
|
-
|
|
430502
|
-
|
|
430503
|
-
|
|
430504
|
-
|
|
431566
|
+
if (exceedsMaxNestingDepth(hookInput)) {
|
|
431567
|
+
const message = `Hook input nesting depth exceeds MAX_HOOK_INPUT_NESTING_DEPTH (${MAX_HOOK_INPUT_NESTING_DEPTH}); refusing to run hooks`;
|
|
431568
|
+
logForDebugging3(`Hooks: ${hookName}: ${message}`);
|
|
431569
|
+
return matchingHooks.map(({ hook, pluginRoot }) => ({
|
|
431570
|
+
command: getHookDisplayText(hook),
|
|
431571
|
+
succeeded: false,
|
|
431572
|
+
output: message,
|
|
431573
|
+
blocked: shouldFailClosePluginHook(hookEvent, pluginRoot)
|
|
431574
|
+
}));
|
|
431575
|
+
}
|
|
431576
|
+
const serialized = stringifyWithGetterFallback(hookInput);
|
|
431577
|
+
if (serialized.ok !== true) {
|
|
431578
|
+
logError3(serialized.error);
|
|
431579
|
+
return matchingHooks.map(({ hook, pluginRoot }) => ({
|
|
431580
|
+
command: getHookDisplayText(hook),
|
|
431581
|
+
succeeded: false,
|
|
431582
|
+
output: `Failed to serialize hook input: ${errorMessage2(serialized.error)}`,
|
|
431583
|
+
blocked: shouldFailClosePluginHook(hookEvent, pluginRoot)
|
|
431584
|
+
}));
|
|
431585
|
+
}
|
|
431586
|
+
if (serialized.droppedKeys.length > 0) {
|
|
431587
|
+
logForDebugging3(`Hooks: ${hookName}: dropped input keys with throwing getters: ${serialized.droppedKeys.join(", ")}`);
|
|
430505
431588
|
}
|
|
430506
|
-
const
|
|
431589
|
+
const jsonInput = serialized.value;
|
|
431590
|
+
const hookTasks = matchingHooks.map(({ hook, pluginRoot, pluginId }, hookIndex) => async () => {
|
|
431591
|
+
if (isReentrantHook(hookEvent, hook, pluginRoot)) {
|
|
431592
|
+
logForDebugging3(`Hooks: ${hookName}: skipping re-entrant execution of ${getHookDisplayText(hook)}`);
|
|
431593
|
+
return {
|
|
431594
|
+
command: getHookDisplayText(hook),
|
|
431595
|
+
succeeded: true,
|
|
431596
|
+
output: "Skipped: re-entrant hook execution (already running in this call chain)",
|
|
431597
|
+
blocked: false
|
|
431598
|
+
};
|
|
431599
|
+
}
|
|
430507
431600
|
if (hook.type === "callback") {
|
|
430508
431601
|
const callbackTimeoutMs = hook.timeout ? hook.timeout * 1000 : timeoutMs;
|
|
430509
431602
|
const { signal: abortSignal, cleanup } = createCombinedAbortSignal(signal, { timeoutMs: callbackTimeoutMs });
|
|
430510
431603
|
try {
|
|
430511
431604
|
const toolUseID = randomUUID43();
|
|
430512
|
-
const json = await hook.callback(hookInput, toolUseID, abortSignal, hookIndex);
|
|
431605
|
+
const json = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => hook.callback(hookInput, toolUseID, abortSignal, hookIndex));
|
|
430513
431606
|
cleanup?.();
|
|
430514
431607
|
if (isAsyncHookJSONOutput(json)) {
|
|
430515
431608
|
logForDebugging3(`${hookName} [callback] returned async response, returning empty output`);
|
|
@@ -430568,7 +431661,7 @@ async function executeHooksOutsideREPL({
|
|
|
430568
431661
|
}
|
|
430569
431662
|
if (hook.type === "http") {
|
|
430570
431663
|
try {
|
|
430571
|
-
const httpResult = await execHttpHook(hook, hookEvent, jsonInput, signal);
|
|
431664
|
+
const httpResult = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => execHttpHook(hook, hookEvent, jsonInput, signal));
|
|
430572
431665
|
if (httpResult.aborted) {
|
|
430573
431666
|
logForDebugging3(`${hookName} [${hook.url}] cancelled`);
|
|
430574
431667
|
return {
|
|
@@ -430619,7 +431712,7 @@ async function executeHooksOutsideREPL({
|
|
|
430619
431712
|
const commandTimeoutMs = hook.timeout ? hook.timeout * 1000 : timeoutMs;
|
|
430620
431713
|
const { signal: abortSignal, cleanup } = createCombinedAbortSignal(signal, { timeoutMs: commandTimeoutMs });
|
|
430621
431714
|
try {
|
|
430622
|
-
const result = await execCommandHook(hook, hookEvent, hookName, jsonInput, abortSignal, randomUUID43(), hookIndex, pluginRoot, pluginId);
|
|
431715
|
+
const result = await runWithHookReentrancy(hookEvent, hook, pluginRoot, () => execCommandHook(hook, hookEvent, hookName, jsonInput, abortSignal, randomUUID43(), hookIndex, pluginRoot, pluginId));
|
|
430623
431716
|
cleanup?.();
|
|
430624
431717
|
if (result.aborted) {
|
|
430625
431718
|
logForDebugging3(`${hookName} [${hook.command}] cancelled`);
|
|
@@ -430663,7 +431756,7 @@ async function executeHooksOutsideREPL({
|
|
|
430663
431756
|
};
|
|
430664
431757
|
}
|
|
430665
431758
|
});
|
|
430666
|
-
return await
|
|
431759
|
+
return await runWithConcurrencyLimit(hookTasks, MAX_HOOK_CONCURRENCY);
|
|
430667
431760
|
}
|
|
430668
431761
|
async function* executePermissionDeniedHooks(toolName, toolUseID, toolInput, reason, toolUseContext, permissionMode, signal, timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS) {
|
|
430669
431762
|
const appState = toolUseContext.getAppState();
|
|
@@ -431463,7 +432556,7 @@ function getHookDefinitionsForTelemetry(matchedHooks) {
|
|
|
431463
432556
|
return { type: "unknown" };
|
|
431464
432557
|
});
|
|
431465
432558
|
}
|
|
431466
|
-
var TOOL_HOOK_EXECUTION_TIMEOUT_MS,
|
|
432559
|
+
var TOOL_HOOK_EXECUTION_TIMEOUT_MS, SESSION_END_HOOK_TIMEOUT_MS_DEFAULT2 = 1500;
|
|
431467
432560
|
var init_hooks7 = __esm(() => {
|
|
431468
432561
|
init_file();
|
|
431469
432562
|
init_ShellCommand2();
|
|
@@ -431496,12 +432589,16 @@ var init_hooks7 = __esm(() => {
|
|
|
431496
432589
|
init_permissionRuleParser2();
|
|
431497
432590
|
init_log5();
|
|
431498
432591
|
init_combinedAbortSignal();
|
|
432592
|
+
init_hookInputMaterialize();
|
|
432593
|
+
init_hookReentrancy();
|
|
432594
|
+
init_hookFailClosed();
|
|
431499
432595
|
init_AsyncHookRegistry();
|
|
431500
432596
|
init_messageQueueManager();
|
|
431501
432597
|
init_messages3();
|
|
431502
432598
|
init_hookEvents2();
|
|
431503
432599
|
init_attachments();
|
|
431504
432600
|
init_generators();
|
|
432601
|
+
init_engine();
|
|
431505
432602
|
init_Tool();
|
|
431506
432603
|
init_execPromptHook();
|
|
431507
432604
|
init_execAgentHook();
|
|
@@ -440582,7 +441679,21 @@ function getDestructiveCommandWarning2(command) {
|
|
|
440582
441679
|
}
|
|
440583
441680
|
return null;
|
|
440584
441681
|
}
|
|
440585
|
-
|
|
441682
|
+
function isStrictInlineShellRmPromptEnabled(env = process.env) {
|
|
441683
|
+
return env[STRICT_INLINE_SHELL_RM_PROMPT_ENV] === "1";
|
|
441684
|
+
}
|
|
441685
|
+
function getStrictInlineShellRmWarning(command) {
|
|
441686
|
+
if (STRICT_CONCAT_RM_RE.test(command)) {
|
|
441687
|
+
return STRICT_CONCAT_RM_WARNING;
|
|
441688
|
+
}
|
|
441689
|
+
for (const re of STRICT_SCRIPT_INVOCATION_RES) {
|
|
441690
|
+
if (re.test(command)) {
|
|
441691
|
+
return STRICT_SCRIPT_WARNING;
|
|
441692
|
+
}
|
|
441693
|
+
}
|
|
441694
|
+
return null;
|
|
441695
|
+
}
|
|
441696
|
+
var DESTRUCTIVE_PATTERNS, STRICT_INLINE_SHELL_RM_PROMPT_ENV = "ATLAS_STRICT_INLINE_SHELL_RM_PROMPT", STRICT_CONCAT_RM_RE, STRICT_SCRIPT_INVOCATION_RES, STRICT_CONCAT_RM_WARNING = "Note: may remove files via a non-literal rm invocation", STRICT_SCRIPT_WARNING = "Note: runs a shell script whose contents are not inspected";
|
|
440586
441697
|
var init_destructiveCommandWarning2 = __esm(() => {
|
|
440587
441698
|
DESTRUCTIVE_PATTERNS = [
|
|
440588
441699
|
{
|
|
@@ -440650,6 +441761,13 @@ var init_destructiveCommandWarning2 = __esm(() => {
|
|
|
440650
441761
|
warning: "Note: may destroy Terraform infrastructure"
|
|
440651
441762
|
}
|
|
440652
441763
|
];
|
|
441764
|
+
STRICT_CONCAT_RM_RE = /\brm\s+(?:-[a-zA-Z]*[rRf]|--recursive|--force)/;
|
|
441765
|
+
STRICT_SCRIPT_INVOCATION_RES = [
|
|
441766
|
+
/(^|[;&|\n]\s*)(?:[^\s;&|]*\/)?(?:bash|zsh|dash|ksh|sh)\b/,
|
|
441767
|
+
/(^|[;&|\n]\s*)source\s+\S/,
|
|
441768
|
+
/(^|[;&|\n]\s*)\.[ \t]*\S/,
|
|
441769
|
+
/(^|[;&|\n]\s*)\S+\.sh\b/
|
|
441770
|
+
];
|
|
440653
441771
|
});
|
|
440654
441772
|
|
|
440655
441773
|
// src/tui/utils/shell/specPrefix.ts
|
|
@@ -444710,7 +445828,7 @@ function BashPermissionRequestInner({
|
|
|
444710
445828
|
sandboxingEnabled: sandboxingEnabled_0,
|
|
444711
445829
|
isSandboxed: isSandboxed_0
|
|
444712
445830
|
} = import_react193.useMemo(() => {
|
|
444713
|
-
const destructiveWarning = getFeatureValue_CACHED_MAY_BE_STALE("atlas_destructive_command_warning", false) ? getDestructiveCommandWarning2(command) : null;
|
|
445831
|
+
const destructiveWarning = getFeatureValue_CACHED_MAY_BE_STALE("atlas_destructive_command_warning", false) ? getDestructiveCommandWarning2(command) ?? (isStrictInlineShellRmPromptEnabled() ? getStrictInlineShellRmWarning(command) : null) : null;
|
|
444714
445832
|
const sandboxingEnabled = getSandboxManager().isSandboxingEnabled();
|
|
444715
445833
|
const isSandboxed = sandboxingEnabled && shouldUseSandbox2(toolUseConfirm.input);
|
|
444716
445834
|
return {
|
|
@@ -469670,41 +470788,28 @@ async function processUserInput({
|
|
|
469670
470788
|
if (mode === "prompt" && inputString !== null && !isMeta) {
|
|
469671
470789
|
setUserInputOnProcessing?.(inputString);
|
|
469672
470790
|
}
|
|
469673
|
-
queryCheckpoint("query_process_user_input_base_start");
|
|
469674
470791
|
const appState = context.getAppState();
|
|
469675
|
-
const result = await processUserInputBase(input, mode, setToolJSX, context, pastedContents, ideSelection, messages, uuid, isAlreadyProcessing, querySource, canUseTool, appState.toolPermissionContext.mode, skipSlashCommands, bridgeOrigin, isMeta, skipAttachments, preExpansionInput);
|
|
469676
|
-
queryCheckpoint("query_process_user_input_base_end");
|
|
469677
|
-
if (!result.shouldQuery) {
|
|
469678
|
-
return result;
|
|
469679
|
-
}
|
|
469680
470792
|
queryCheckpoint("query_hooks_start");
|
|
469681
470793
|
const inputMessage = getContentText(input) || "";
|
|
470794
|
+
let hookBlockingMessage = null;
|
|
470795
|
+
let hookStopped = false;
|
|
470796
|
+
let hookStopReason;
|
|
470797
|
+
const hookMessages = [];
|
|
469682
470798
|
for await (const hookResult of executeUserPromptSubmitHooks(inputMessage, appState.toolPermissionContext.mode, context, context.requestPrompt)) {
|
|
469683
470799
|
if (hookResult.message?.type === "progress") {
|
|
469684
470800
|
continue;
|
|
469685
470801
|
}
|
|
469686
470802
|
if (hookResult.blockingError) {
|
|
469687
|
-
|
|
469688
|
-
|
|
469689
|
-
messages: [
|
|
469690
|
-
createSystemMessage(`${blockingMessage}
|
|
469691
|
-
|
|
469692
|
-
Original prompt: ${input}`, "warning")
|
|
469693
|
-
],
|
|
469694
|
-
shouldQuery: false,
|
|
469695
|
-
allowedTools: result.allowedTools
|
|
469696
|
-
};
|
|
470803
|
+
hookBlockingMessage = getUserPromptSubmitHookBlockingMessage(hookResult.blockingError);
|
|
470804
|
+
break;
|
|
469697
470805
|
}
|
|
469698
470806
|
if (hookResult.preventContinuation) {
|
|
469699
|
-
|
|
469700
|
-
|
|
469701
|
-
|
|
469702
|
-
}));
|
|
469703
|
-
result.shouldQuery = false;
|
|
469704
|
-
return result;
|
|
470807
|
+
hookStopped = true;
|
|
470808
|
+
hookStopReason = hookResult.stopReason;
|
|
470809
|
+
continue;
|
|
469705
470810
|
}
|
|
469706
470811
|
if (hookResult.additionalContexts && hookResult.additionalContexts.length > 0) {
|
|
469707
|
-
|
|
470812
|
+
hookMessages.push(createAttachmentMessage({
|
|
469708
470813
|
type: "hook_additional_context",
|
|
469709
470814
|
content: hookResult.additionalContexts.map(applyTruncation),
|
|
469710
470815
|
hookName: "UserPromptSubmit",
|
|
@@ -469718,7 +470823,7 @@ Original prompt: ${input}`, "warning")
|
|
|
469718
470823
|
if (!hookResult.message.attachment.content) {
|
|
469719
470824
|
break;
|
|
469720
470825
|
}
|
|
469721
|
-
|
|
470826
|
+
hookMessages.push({
|
|
469722
470827
|
...hookResult.message,
|
|
469723
470828
|
attachment: {
|
|
469724
470829
|
...hookResult.message.attachment,
|
|
@@ -469727,12 +470832,38 @@ Original prompt: ${input}`, "warning")
|
|
|
469727
470832
|
});
|
|
469728
470833
|
break;
|
|
469729
470834
|
default:
|
|
469730
|
-
|
|
470835
|
+
hookMessages.push(hookResult.message);
|
|
469731
470836
|
break;
|
|
469732
470837
|
}
|
|
469733
470838
|
}
|
|
469734
470839
|
}
|
|
469735
470840
|
queryCheckpoint("query_hooks_end");
|
|
470841
|
+
if (hookBlockingMessage !== null) {
|
|
470842
|
+
return {
|
|
470843
|
+
messages: [
|
|
470844
|
+
createSystemMessage(`${hookBlockingMessage}
|
|
470845
|
+
|
|
470846
|
+
Original prompt: ${input}`, "warning")
|
|
470847
|
+
],
|
|
470848
|
+
shouldQuery: false,
|
|
470849
|
+
allowedTools: []
|
|
470850
|
+
};
|
|
470851
|
+
}
|
|
470852
|
+
queryCheckpoint("query_process_user_input_base_start");
|
|
470853
|
+
const result = await processUserInputBase(input, mode, setToolJSX, context, pastedContents, ideSelection, messages, uuid, isAlreadyProcessing, querySource, canUseTool, appState.toolPermissionContext.mode, skipSlashCommands, bridgeOrigin, isMeta, skipAttachments, preExpansionInput);
|
|
470854
|
+
queryCheckpoint("query_process_user_input_base_end");
|
|
470855
|
+
if (hookStopped && result.shouldQuery) {
|
|
470856
|
+
const message = hookStopReason ? `Operation stopped by hook: ${hookStopReason}` : "Operation stopped by hook";
|
|
470857
|
+
result.messages.push(createUserMessage2({
|
|
470858
|
+
content: message
|
|
470859
|
+
}));
|
|
470860
|
+
result.shouldQuery = false;
|
|
470861
|
+
return result;
|
|
470862
|
+
}
|
|
470863
|
+
if (!result.shouldQuery) {
|
|
470864
|
+
return result;
|
|
470865
|
+
}
|
|
470866
|
+
result.messages.push(...hookMessages);
|
|
469736
470867
|
return result;
|
|
469737
470868
|
}
|
|
469738
470869
|
function applyTruncation(content) {
|
|
@@ -470300,6 +471431,32 @@ var init_useMailboxBridge = __esm(() => {
|
|
|
470300
471431
|
|
|
470301
471432
|
// src/tui/replLoopDeps.ts
|
|
470302
471433
|
function buildReplLoopParams(m) {
|
|
471434
|
+
let prevPoolSig = (m.tools ?? []).map((t) => t.name).join(",");
|
|
471435
|
+
const refreshRoundContext = (messages) => {
|
|
471436
|
+
const appState = m.toolUseContext?.getAppState?.();
|
|
471437
|
+
const liveMcpTools = appState?.mcp?.tools;
|
|
471438
|
+
if (!liveMcpTools)
|
|
471439
|
+
return null;
|
|
471440
|
+
const pool = assembleToolPool(m.toolPermissionContext, liveMcpTools);
|
|
471441
|
+
const sig = pool.map((t) => t.name).join(",");
|
|
471442
|
+
const tuiMessages = messages;
|
|
471443
|
+
const atts = [
|
|
471444
|
+
...getDeferredToolsDeltaAttachment(pool, m.mainLoopModel, tuiMessages, {
|
|
471445
|
+
callSite: "round_refresh",
|
|
471446
|
+
querySource: m.querySource
|
|
471447
|
+
}),
|
|
471448
|
+
...getMcpInstructionsDeltaAttachment(m.toolUseContext.options.mcpClients ?? [], pool, m.mainLoopModel, tuiMessages)
|
|
471449
|
+
];
|
|
471450
|
+
if (sig === prevPoolSig && atts.length === 0)
|
|
471451
|
+
return null;
|
|
471452
|
+
prevPoolSig = sig;
|
|
471453
|
+
return {
|
|
471454
|
+
tools: pool,
|
|
471455
|
+
...atts.length > 0 ? {
|
|
471456
|
+
appendedMessages: atts.map((att) => createAttachmentMessage(att))
|
|
471457
|
+
} : {}
|
|
471458
|
+
};
|
|
471459
|
+
};
|
|
470303
471460
|
return buildAgentLoopParams({
|
|
470304
471461
|
messages: m.messages,
|
|
470305
471462
|
userContext: m.userContext,
|
|
@@ -470315,11 +471472,14 @@ function buildReplLoopParams(m) {
|
|
|
470315
471472
|
toolUseContext: m.toolUseContext,
|
|
470316
471473
|
querySource: m.querySource,
|
|
470317
471474
|
unboundedTurns: true,
|
|
470318
|
-
maxGoalTurns: m.maxGoalTurns
|
|
471475
|
+
maxGoalTurns: m.maxGoalTurns,
|
|
471476
|
+
refreshRoundContext
|
|
470319
471477
|
});
|
|
470320
471478
|
}
|
|
470321
471479
|
var init_replLoopDeps = __esm(() => {
|
|
470322
471480
|
init_agentLoopDeps();
|
|
471481
|
+
init_tools3();
|
|
471482
|
+
init_attachments();
|
|
470323
471483
|
});
|
|
470324
471484
|
|
|
470325
471485
|
// src/tui/hooks/useMergedClients.ts
|
|
@@ -493526,7 +494686,7 @@ ${formattedErrors}
|
|
|
493526
494686
|
const {
|
|
493527
494687
|
allowed,
|
|
493528
494688
|
blocked
|
|
493529
|
-
} =
|
|
494689
|
+
} = filterMcpServersByPolicy2(scopedConfigs);
|
|
493530
494690
|
if (blocked.length > 0) {
|
|
493531
494691
|
process.stderr.write(`Warning: MCP ${plural2(blocked.length, "server")} blocked by enterprise policy: ${blocked.join(", ")}
|
|
493532
494692
|
`);
|
|
@@ -494207,7 +495367,8 @@ ${inputPrompt}` : mergePrompt;
|
|
|
494207
495367
|
tools: [],
|
|
494208
495368
|
commands: [],
|
|
494209
495369
|
resources: {},
|
|
494210
|
-
pluginReconnectKey: 0
|
|
495370
|
+
pluginReconnectKey: 0,
|
|
495371
|
+
cwdReconnectKey: 0
|
|
494211
495372
|
},
|
|
494212
495373
|
plugins: {
|
|
494213
495374
|
enabled: [],
|