fix(agents): hide grep glob for frontier agents
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -8,6 +8,7 @@ import { applyEnvironmentContext } from "./environment-context"
|
|||||||
import { applyCategoryOverride, mergeAgentConfig } from "./agent-overrides"
|
import { applyCategoryOverride, mergeAgentConfig } from "./agent-overrides"
|
||||||
import { applyModelResolution, getFirstFallbackModel } from "./model-resolution"
|
import { applyModelResolution, getFirstFallbackModel } from "./model-resolution"
|
||||||
import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard"
|
import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard"
|
||||||
|
import { getFrontierToolSchemaPermission } from "../frontier-tool-schema-guard"
|
||||||
|
|
||||||
export function maybeCreateHephaestusConfig(input: {
|
export function maybeCreateHephaestusConfig(input: {
|
||||||
disabledAgents: string[]
|
disabledAgents: string[]
|
||||||
@@ -89,6 +90,11 @@ export function maybeCreateHephaestusConfig(input: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resolvedModel = hephaestusConfig.model ?? ""
|
const resolvedModel = hephaestusConfig.model ?? ""
|
||||||
|
const frontierDeny = getFrontierToolSchemaPermission(resolvedModel)
|
||||||
|
if (Object.keys(frontierDeny).length > 0 && hephaestusConfig.permission) {
|
||||||
|
Object.assign(hephaestusConfig.permission, frontierDeny)
|
||||||
|
}
|
||||||
|
|
||||||
const gptDeny = getGptApplyPatchPermission(resolvedModel)
|
const gptDeny = getGptApplyPatchPermission(resolvedModel)
|
||||||
if (Object.keys(gptDeny).length > 0 && hephaestusConfig.permission) {
|
if (Object.keys(gptDeny).length > 0 && hephaestusConfig.permission) {
|
||||||
Object.assign(hephaestusConfig.permission, gptDeny)
|
Object.assign(hephaestusConfig.permission, gptDeny)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import { maybeCreateSisyphusConfig } from "./sisyphus-agent";
|
import { maybeCreateSisyphusConfig } from "./sisyphus-agent";
|
||||||
import type { AgentOverrides } from "../types";
|
import type { AgentOverrides } from "../types";
|
||||||
@@ -12,7 +14,7 @@ describe("maybeCreateSisyphusConfig", () => {
|
|||||||
model: "openai/gpt-5.4",
|
model: "openai/gpt-5.4",
|
||||||
permission: {
|
permission: {
|
||||||
apply_patch: "allow",
|
apply_patch: "allow",
|
||||||
},
|
} as Record<string, "allow">,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const mergedCategories: Record<string, CategoryConfig> = {};
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
@@ -46,7 +48,7 @@ describe("maybeCreateSisyphusConfig", () => {
|
|||||||
model: "anthropic/claude-opus-4-7",
|
model: "anthropic/claude-opus-4-7",
|
||||||
permission: {
|
permission: {
|
||||||
apply_patch: "allow",
|
apply_patch: "allow",
|
||||||
},
|
} as Record<string, "allow">,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const mergedCategories: Record<string, CategoryConfig> = {};
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
@@ -73,6 +75,74 @@ describe("maybeCreateSisyphusConfig", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#given Opus 4.7 model with user override allowing grep and glob", () => {
|
||||||
|
test("#when config is created #then grep and glob are still denied", () => {
|
||||||
|
// given
|
||||||
|
const agentOverrides: AgentOverrides = {
|
||||||
|
sisyphus: {
|
||||||
|
model: "anthropic/claude-opus-4-7",
|
||||||
|
permission: {
|
||||||
|
grep: "allow",
|
||||||
|
glob: "allow",
|
||||||
|
} as Record<string, "allow">,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
|
|
||||||
|
// when
|
||||||
|
const config = maybeCreateSisyphusConfig({
|
||||||
|
disabledAgents: [],
|
||||||
|
agentOverrides,
|
||||||
|
availableModels: new Set(["anthropic/claude-opus-4-7"]),
|
||||||
|
systemDefaultModel: "anthropic/claude-opus-4-7",
|
||||||
|
isFirstRunNoCache: false,
|
||||||
|
availableAgents: [],
|
||||||
|
availableSkills: [],
|
||||||
|
availableCategories: [],
|
||||||
|
mergedCategories,
|
||||||
|
useTaskSystem: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(config?.permission).toHaveProperty("grep", "deny");
|
||||||
|
expect(config?.permission).toHaveProperty("glob", "deny");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#given GPT 5.5 model with user override allowing grep and glob", () => {
|
||||||
|
test("#when config is created #then grep and glob are still denied", () => {
|
||||||
|
// given
|
||||||
|
const agentOverrides: AgentOverrides = {
|
||||||
|
sisyphus: {
|
||||||
|
model: "openai/gpt-5.5",
|
||||||
|
permission: {
|
||||||
|
grep: "allow",
|
||||||
|
glob: "allow",
|
||||||
|
} as Record<string, "allow">,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
|
|
||||||
|
// when
|
||||||
|
const config = maybeCreateSisyphusConfig({
|
||||||
|
disabledAgents: [],
|
||||||
|
agentOverrides,
|
||||||
|
availableModels: new Set(["openai/gpt-5.5"]),
|
||||||
|
systemDefaultModel: "openai/gpt-5.5",
|
||||||
|
isFirstRunNoCache: false,
|
||||||
|
availableAgents: [],
|
||||||
|
availableSkills: [],
|
||||||
|
availableCategories: [],
|
||||||
|
mergedCategories,
|
||||||
|
useTaskSystem: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(config?.permission).toHaveProperty("grep", "deny");
|
||||||
|
expect(config?.permission).toHaveProperty("glob", "deny");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#given generic GPT model with user override allowing apply_patch", () => {
|
describe("#given generic GPT model with user override allowing apply_patch", () => {
|
||||||
test("#when config is created #then apply_patch is still denied", () => {
|
test("#when config is created #then apply_patch is still denied", () => {
|
||||||
// given
|
// given
|
||||||
@@ -81,7 +151,7 @@ describe("maybeCreateSisyphusConfig", () => {
|
|||||||
model: "openai/gpt-4o",
|
model: "openai/gpt-4o",
|
||||||
permission: {
|
permission: {
|
||||||
apply_patch: "allow",
|
apply_patch: "allow",
|
||||||
},
|
} as Record<string, "allow">,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const mergedCategories: Record<string, CategoryConfig> = {};
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { applyOverrides } from "./agent-overrides"
|
|||||||
import { applyModelResolution, getFirstFallbackModel } from "./model-resolution"
|
import { applyModelResolution, getFirstFallbackModel } from "./model-resolution"
|
||||||
import { createSisyphusAgent } from "../sisyphus"
|
import { createSisyphusAgent } from "../sisyphus"
|
||||||
import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard"
|
import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard"
|
||||||
|
import { getFrontierToolSchemaPermission } from "../frontier-tool-schema-guard"
|
||||||
|
|
||||||
export function maybeCreateSisyphusConfig(input: {
|
export function maybeCreateSisyphusConfig(input: {
|
||||||
disabledAgents: string[]
|
disabledAgents: string[]
|
||||||
@@ -83,6 +84,11 @@ export function maybeCreateSisyphusConfig(input: {
|
|||||||
sisyphusConfig = applyOverrides(sisyphusConfig, sisyphusOverride, mergedCategories, directory)
|
sisyphusConfig = applyOverrides(sisyphusConfig, sisyphusOverride, mergedCategories, directory)
|
||||||
|
|
||||||
const resolvedModel = sisyphusConfig.model ?? ""
|
const resolvedModel = sisyphusConfig.model ?? ""
|
||||||
|
const frontierDeny = getFrontierToolSchemaPermission(resolvedModel)
|
||||||
|
if (Object.keys(frontierDeny).length > 0 && sisyphusConfig.permission) {
|
||||||
|
Object.assign(sisyphusConfig.permission, frontierDeny)
|
||||||
|
}
|
||||||
|
|
||||||
const gptDeny = getGptApplyPatchPermission(resolvedModel)
|
const gptDeny = getGptApplyPatchPermission(resolvedModel)
|
||||||
if (Object.keys(gptDeny).length > 0 && sisyphusConfig.permission) {
|
if (Object.keys(gptDeny).length > 0 && sisyphusConfig.permission) {
|
||||||
Object.assign(sisyphusConfig.permission, gptDeny)
|
Object.assign(sisyphusConfig.permission, gptDeny)
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { isGpt5_5Model } from "./types"
|
||||||
|
|
||||||
|
function isOpus47Model(model: string): boolean {
|
||||||
|
const modelName = model.includes("/") ? (model.split("/").pop() ?? model) : model
|
||||||
|
return modelName.toLowerCase().includes("claude-opus-4-7")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFrontierToolSchemaPermission(model: string): Record<string, "deny"> {
|
||||||
|
return isOpus47Model(model) || isGpt5_5Model(model)
|
||||||
|
? { grep: "deny" as const, glob: "deny" as const }
|
||||||
|
: {}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { describe, expect, test } from "bun:test";
|
import { describe, expect, test } from "bun:test";
|
||||||
import {
|
import {
|
||||||
getHephaestusPromptSource,
|
getHephaestusPromptSource,
|
||||||
@@ -321,7 +323,7 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => {
|
|||||||
model: "openai/gpt-5.4",
|
model: "openai/gpt-5.4",
|
||||||
permission: {
|
permission: {
|
||||||
apply_patch: "allow",
|
apply_patch: "allow",
|
||||||
},
|
} as Record<string, "allow">,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const mergedCategories: Record<string, CategoryConfig> = {};
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
@@ -355,7 +357,7 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => {
|
|||||||
model: "anthropic/claude-opus-4-7",
|
model: "anthropic/claude-opus-4-7",
|
||||||
permission: {
|
permission: {
|
||||||
apply_patch: "allow",
|
apply_patch: "allow",
|
||||||
},
|
} as Record<string, "allow">,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const mergedCategories: Record<string, CategoryConfig> = {};
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
@@ -389,7 +391,7 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => {
|
|||||||
model: "openai/gpt-4o",
|
model: "openai/gpt-4o",
|
||||||
permission: {
|
permission: {
|
||||||
apply_patch: "allow",
|
apply_patch: "allow",
|
||||||
},
|
} as Record<string, "allow">,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const mergedCategories: Record<string, CategoryConfig> = {};
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
@@ -414,4 +416,72 @@ describe("maybeCreateHephaestusConfig GPT apply_patch guard", () => {
|
|||||||
expect(config?.permission).toHaveProperty("apply_patch", "deny");
|
expect(config?.permission).toHaveProperty("apply_patch", "deny");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#given Opus 4.7 model with user override allowing grep and glob", () => {
|
||||||
|
test("#when config is created #then grep and glob are still denied", () => {
|
||||||
|
// given
|
||||||
|
const agentOverrides: AgentOverrides = {
|
||||||
|
hephaestus: {
|
||||||
|
model: "anthropic/claude-opus-4-7",
|
||||||
|
permission: {
|
||||||
|
grep: "allow",
|
||||||
|
glob: "allow",
|
||||||
|
} as Record<string, "allow">,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
|
|
||||||
|
// when
|
||||||
|
const config = maybeCreateHephaestusConfig({
|
||||||
|
disabledAgents: [],
|
||||||
|
agentOverrides,
|
||||||
|
availableModels: new Set(["anthropic/claude-opus-4-7"]),
|
||||||
|
systemDefaultModel: "anthropic/claude-opus-4-7",
|
||||||
|
isFirstRunNoCache: false,
|
||||||
|
availableAgents: [],
|
||||||
|
availableSkills: [],
|
||||||
|
availableCategories: [],
|
||||||
|
mergedCategories,
|
||||||
|
useTaskSystem: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(config?.permission).toHaveProperty("grep", "deny");
|
||||||
|
expect(config?.permission).toHaveProperty("glob", "deny");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#given GPT 5.5 model with user override allowing grep and glob", () => {
|
||||||
|
test("#when config is created #then grep and glob are still denied", () => {
|
||||||
|
// given
|
||||||
|
const agentOverrides: AgentOverrides = {
|
||||||
|
hephaestus: {
|
||||||
|
model: "openai/gpt-5.5",
|
||||||
|
permission: {
|
||||||
|
grep: "allow",
|
||||||
|
glob: "allow",
|
||||||
|
} as Record<string, "allow">,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mergedCategories: Record<string, CategoryConfig> = {};
|
||||||
|
|
||||||
|
// when
|
||||||
|
const config = maybeCreateHephaestusConfig({
|
||||||
|
disabledAgents: [],
|
||||||
|
agentOverrides,
|
||||||
|
availableModels: new Set(["openai/gpt-5.5"]),
|
||||||
|
systemDefaultModel: "openai/gpt-5.5",
|
||||||
|
isFirstRunNoCache: false,
|
||||||
|
availableAgents: [],
|
||||||
|
availableSkills: [],
|
||||||
|
availableCategories: [],
|
||||||
|
mergedCategories,
|
||||||
|
useTaskSystem: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(config?.permission).toHaveProperty("grep", "deny");
|
||||||
|
expect(config?.permission).toHaveProperty("glob", "deny");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type {
|
|||||||
} from "../dynamic-agent-prompt-builder";
|
} from "../dynamic-agent-prompt-builder";
|
||||||
import { categorizeTools, buildAgentIdentitySection } from "../dynamic-agent-prompt-builder";
|
import { categorizeTools, buildAgentIdentitySection } from "../dynamic-agent-prompt-builder";
|
||||||
import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard";
|
import { getGptApplyPatchPermission } from "../gpt-apply-patch-guard";
|
||||||
|
import { getFrontierToolSchemaPermission } from "../frontier-tool-schema-guard";
|
||||||
|
|
||||||
import { buildHephaestusPrompt as buildGptPrompt } from "./gpt";
|
import { buildHephaestusPrompt as buildGptPrompt } from "./gpt";
|
||||||
import { buildHephaestusPrompt as buildGpt53CodexPrompt } from "./gpt-5-3-codex";
|
import { buildHephaestusPrompt as buildGpt53CodexPrompt } from "./gpt-5-3-codex";
|
||||||
@@ -139,6 +140,7 @@ export function createHephaestusAgent(
|
|||||||
permission: {
|
permission: {
|
||||||
question: "allow",
|
question: "allow",
|
||||||
call_omo_agent: "deny",
|
call_omo_agent: "deny",
|
||||||
|
...getFrontierToolSchemaPermission(model),
|
||||||
...getGptApplyPatchPermission(model),
|
...getGptApplyPatchPermission(model),
|
||||||
} as AgentConfig["permission"],
|
} as AgentConfig["permission"],
|
||||||
reasoningEffort: "medium",
|
reasoningEffort: "medium",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { buildGpt54SisyphusPrompt } from "./sisyphus/gpt-5-4";
|
|||||||
import { buildGpt55SisyphusPrompt } from "./sisyphus/gpt-5-5";
|
import { buildGpt55SisyphusPrompt } from "./sisyphus/gpt-5-5";
|
||||||
import { buildTaskManagementSection } from "./sisyphus/default";
|
import { buildTaskManagementSection } from "./sisyphus/default";
|
||||||
import { getGptApplyPatchPermission } from "./gpt-apply-patch-guard";
|
import { getGptApplyPatchPermission } from "./gpt-apply-patch-guard";
|
||||||
|
import { getFrontierToolSchemaPermission } from "./frontier-tool-schema-guard";
|
||||||
|
|
||||||
const MODE: AgentMode = "primary";
|
const MODE: AgentMode = "primary";
|
||||||
export const SISYPHUS_PROMPT_METADATA: AgentPromptMetadata = {
|
export const SISYPHUS_PROMPT_METADATA: AgentPromptMetadata = {
|
||||||
@@ -501,6 +502,7 @@ export function createSisyphusAgent(
|
|||||||
permission: {
|
permission: {
|
||||||
question: "allow",
|
question: "allow",
|
||||||
call_omo_agent: "deny",
|
call_omo_agent: "deny",
|
||||||
|
...getFrontierToolSchemaPermission(model),
|
||||||
...getGptApplyPatchPermission(model),
|
...getGptApplyPatchPermission(model),
|
||||||
} as AgentConfig["permission"],
|
} as AgentConfig["permission"],
|
||||||
reasoningEffort: "medium",
|
reasoningEffort: "medium",
|
||||||
@@ -527,6 +529,7 @@ export function createSisyphusAgent(
|
|||||||
permission: {
|
permission: {
|
||||||
question: "allow",
|
question: "allow",
|
||||||
call_omo_agent: "deny",
|
call_omo_agent: "deny",
|
||||||
|
...getFrontierToolSchemaPermission(model),
|
||||||
...getGptApplyPatchPermission(model),
|
...getGptApplyPatchPermission(model),
|
||||||
} as AgentConfig["permission"],
|
} as AgentConfig["permission"],
|
||||||
reasoningEffort: "medium",
|
reasoningEffort: "medium",
|
||||||
@@ -567,6 +570,7 @@ export function createSisyphusAgent(
|
|||||||
const permission = {
|
const permission = {
|
||||||
question: "allow",
|
question: "allow",
|
||||||
call_omo_agent: "deny",
|
call_omo_agent: "deny",
|
||||||
|
...getFrontierToolSchemaPermission(model),
|
||||||
...getGptApplyPatchPermission(model),
|
...getGptApplyPatchPermission(model),
|
||||||
} as AgentConfig["permission"];
|
} as AgentConfig["permission"];
|
||||||
const base = {
|
const base = {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/// <reference types="bun-types" />
|
||||||
|
|
||||||
import { describe, test, expect } from "bun:test"
|
import { describe, test, expect } from "bun:test"
|
||||||
import { createOracleAgent } from "./oracle"
|
import { createOracleAgent } from "./oracle"
|
||||||
import { createLibrarianAgent } from "./librarian"
|
import { createLibrarianAgent } from "./librarian"
|
||||||
@@ -6,6 +8,7 @@ import { createMomusAgent } from "./momus"
|
|||||||
import { createMetisAgent } from "./metis"
|
import { createMetisAgent } from "./metis"
|
||||||
import { createAtlasAgent } from "./atlas"
|
import { createAtlasAgent } from "./atlas"
|
||||||
import { createSisyphusAgent } from "./sisyphus"
|
import { createSisyphusAgent } from "./sisyphus"
|
||||||
|
import { createHephaestusAgent } from "./hephaestus"
|
||||||
|
|
||||||
const TEST_MODEL = "anthropic/claude-sonnet-4-5"
|
const TEST_MODEL = "anthropic/claude-sonnet-4-5"
|
||||||
|
|
||||||
@@ -131,4 +134,47 @@ describe("read-only agent tool restrictions", () => {
|
|||||||
expect(claudePermission["apply_patch"]).toBeUndefined()
|
expect(claudePermission["apply_patch"]).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("Sisyphus and Hephaestus frontier tool schema restrictions", () => {
|
||||||
|
test("deny grep and glob for Opus 4.7 and GPT 5.5 models", () => {
|
||||||
|
// given
|
||||||
|
const frontierAgents = [
|
||||||
|
createSisyphusAgent("anthropic/claude-opus-4-7"),
|
||||||
|
createSisyphusAgent("openai/gpt-5.5"),
|
||||||
|
createHephaestusAgent("anthropic/claude-opus-4-7"),
|
||||||
|
createHephaestusAgent("openai/gpt-5.5"),
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const permissions = frontierAgents.map(
|
||||||
|
(agent) => (agent.permission ?? {}) as Record<string, string>,
|
||||||
|
)
|
||||||
|
|
||||||
|
// then
|
||||||
|
for (const permission of permissions) {
|
||||||
|
expect(permission.grep).toBe("deny")
|
||||||
|
expect(permission.glob).toBe("deny")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test("keeps grep and glob available for other models", () => {
|
||||||
|
// given
|
||||||
|
const otherAgents = [
|
||||||
|
createSisyphusAgent("anthropic/claude-sonnet-4-5"),
|
||||||
|
createSisyphusAgent("openai/gpt-5.4"),
|
||||||
|
createHephaestusAgent("openai/gpt-5.4"),
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const permissions = otherAgents.map(
|
||||||
|
(agent) => (agent.permission ?? {}) as Record<string, string>,
|
||||||
|
)
|
||||||
|
|
||||||
|
// then
|
||||||
|
for (const permission of permissions) {
|
||||||
|
expect(permission.grep).toBeUndefined()
|
||||||
|
expect(permission.glob).toBeUndefined()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user